Files
n8n-openai-adapter/lib/record.ex
T
2012-01-02 11:08:25 +01:00

141 lines
3.9 KiB
Elixir

defmodule Record do
# Main entry point for records definition.
def defrecord(name, values, opts) do
as = Orddict.fetch(opts, :as, name)
quote do
defmodule __MODULE__ :: unquote(name) do
require ::Record
Record.getters_and_setters(unquote(values), 1, [])
Record.initializers(unquote(values))
end
require __MODULE__ :: unquote(name), as: unquote(as)
end
end
# Define initializers methods. For a declaration like:
#
# defrecord FileInfo, atime: nil, mtime: nil
#
# It will define three methods:
#
# def new() do
# new([])
# end
#
# def new([]) do
# { FileInfo, nil, nil }
# end
#
# def new(opts) do
# { FileInfo, Orddict.fetch(opts, :atime, nil), Orddict.fetch(opts, :mtime, nil) }
# end
#
defmacro initializers(values) do
# Get default values from the dictionary.
defaults = Orddict.values(values)
# For each value, define a piece of code that will receive
# an ordered dict of options (opts) and it will try to fetch
# the given key from the ordered dict, falling back to the
# default value if one does not exist.
selective = List.map values, fn({k,v}) {
quote { ::Orddict.fetch(opts, unquote(k), unquote(v)) }
}
quote do
def new(), do: new([])
def new([]), do: { __MODULE__, unquote_splice(defaults) }
def new(opts), do: { __MODULE__, unquote_splice(selective) }
end
end
# Implement getters and setters for each attribute.
# For a declaration like:
#
# defrecord FileInfo, atime: nil, mtime: nil
#
# It will define four methods:
#
# def :atime.(record) do
# element(2, record)
# end
#
# def :atime.(record, value) do
# setelement(2, value, record)
# end
#
# def :mtime.(record) do
# element(3, record)
# end
#
# def :mtime.(record, value) do
# setelement(3, value, record)
# end
#
# `element` and `setelement` will simply get and set values
# from the record tuple. Notice that `:atime.(record)` is just
# a dynamic way to say `atime(record)`. We need to use this
# syntax as `unquote(key)(record)` wouldn't be valid (as Elixir
# allows you to parenthesis just on specific cases as `foo()`
# and `foo.bar()`)
defmacro getters_and_setters([{ key, default }|t], i, acc) do
i = i + 1
contents = quote do
def unquote(key).(record) do
element(unquote(i), record)
end
def unquote(key).(value, record) do
setelement(unquote(i), record, value)
end
end
typed = typed_functions(key, default, i)
getters_and_setters(t, i, [contents, typed | acc])
end
defmacro getters_and_setters([], _i, acc), do: acc
## Private
defp typed_functions(key, default, i) when is_list(default) do
bin_key = atom_to_binary(key, :utf8)
append = :"append_#{bin_key}"
prepend = :"prepend_#{bin_key}"
merge = :"merge_#{bin_key}"
quote do
def unquote(append).(value, record) do
current = element(unquote(i), record)
setelement(unquote(i), record, List.append(current, value))
end
def unquote(prepend).(value, record) do
current = element(unquote(i), record)
setelement(unquote(i), record, List.append(value, current))
end
def unquote(merge).(value, record) do
current = element(unquote(i), record)
setelement(unquote(i), record, Orddict.merge(current, value))
end
end
end
defp typed_functions(key, default, i) when is_number(default) do
bin_key = atom_to_binary(key, :utf8)
increment = :"increment_#{bin_key}"
quote do
def unquote(increment).(value // 1, record) do
current = element(unquote(i), record)
setelement(unquote(i), record, current + value)
end
end
end
defp typed_functions(_, _, _), do: nil
end