Skip to content

Commit

Permalink
Keyword.ex: merge/2 take/2 pop/pop! and helper fns
Browse files Browse the repository at this point in the history
Keyword.pop is needed for Elixir.GenServer.

rest of the functions is often used for config/starting genservers in elixir land - and minimum needed for running eg https://github.com/elixir-circuits on atomvm.

Signed-off-by: Peter M <[email protected]>
  • Loading branch information
petermm committed Jun 8, 2024
1 parent 2278a7f commit 48a2781
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Simple http client, that can be used for different use case such as downloading OTA updates
- Elixir support for `Keyword.merge` `Keyword.take` `Keyword.pop(!)` `Keyword.keyword?` `Keyword.has_key?` functions.

### Changed

Expand Down
52 changes: 51 additions & 1 deletion libs/exavmlib/lib/Keyword.ex
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
#
# This file is part of elixir-lang.
#
# Copyright 2012-2019 Elixir Contributors
# Copyright 2012-2024 Elixir Contributors
# https://github.com/elixir-lang/elixir/commits/v1.10.1/lib/elixir/lib/keyword.ex
#
# merge/2 take/2 pop/2/3 pop!/2 keyword?/1 has_key?/2 from:
# https://github.com/elixir-lang/elixir/blob/v1.16/lib/elixir/lib/keyword.ex
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
Expand Down Expand Up @@ -52,6 +55,41 @@ defmodule Keyword do
end
end

def merge(keywords1, []) when is_list(keywords1), do: keywords1
def merge([], keywords2) when is_list(keywords2), do: keywords2

def merge(keywords1, keywords2) when is_list(keywords1) and is_list(keywords2) do
if keyword?(keywords2) do
fun = fn
{key, _value} when is_atom(key) ->
not has_key?(keywords2, key)

_ ->
raise ArgumentError,
"expected a keyword list as the first argument, got: #{inspect(keywords1)}"
end

:lists.filter(fun, keywords1) ++ keywords2
else
raise ArgumentError,
"expected a keyword list as the second argument, got: #{inspect(keywords2)}"
end
end

def pop(keywords, key, default \\ nil) when is_list(keywords) and is_atom(key) do
case fetch(keywords, key) do
{:ok, value} -> {value, delete(keywords, key)}
:error -> {default, keywords}
end
end

def pop!(keywords, key) when is_list(keywords) and is_atom(key) do
case fetch(keywords, key) do
{:ok, value} -> {value, delete(keywords, key)}
:error -> raise KeyError, key: key, term: keywords
end
end

def put(keywords, key, value) when is_list(keywords) and is_atom(key) do
[{key, value} | delete(keywords, key)]
end
Expand All @@ -63,6 +101,18 @@ defmodule Keyword do
end
end

def take(keywords, keys) when is_list(keywords) and is_list(keys) do
:lists.filter(fn {k, _} -> :lists.member(k, keys) end, keywords)
end

def keyword?([{key, _value} | rest]) when is_atom(key), do: keyword?(rest)
def keyword?([]), do: true
def keyword?(_other), do: false

def has_key?(keywords, key) when is_list(keywords) and is_atom(key) do
:lists.keymember(key, 1, keywords)
end

defp delete_key([{key, _} | tail], key), do: delete_key(tail, key)
defp delete_key([{_, _} = pair | tail], key), do: [pair | delete_key(tail, key)]
defp delete_key([], _key), do: []
Expand Down

0 comments on commit 48a2781

Please sign in to comment.