Skip to content

Commit

Permalink
Improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
vlemann committed Feb 28, 2025
1 parent 03d76ce commit 72248a8
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 53 deletions.
29 changes: 17 additions & 12 deletions lib/estructura/flattenable.ex
Original file line number Diff line number Diff line change
Expand Up @@ -89,18 +89,23 @@ defimpl Estructura.Flattenable, for: Map do
map
|> Enum.reduce(outer_acc, fn {k, v}, %{key: key, acc: acc} ->
acc =
if not is_nil(Estructura.Flattenable.impl_for(v)) and v != %{} and v != [] do
Estructura.Flattenable.flatten(
v,
Keyword.put(options, :__acc__, %{key: [k | key], acc: acc})
)
else
value =
options
|> Keyword.get(:jsonify, false)
|> handle_jsonify(v)

Map.put(acc, [k | key] |> Enum.reverse() |> Enum.join(coupler), value)
cond do
v == %{} or v == [] ->
acc

not is_nil(Estructura.Flattenable.impl_for(v)) ->
Estructura.Flattenable.flatten(
v,
Keyword.put(options, :__acc__, %{key: [k | key], acc: acc})
)

true ->
value =
options
|> Keyword.get(:jsonify, false)
|> handle_jsonify(v)

Map.put(acc, [k | key] |> Enum.reverse() |> Enum.join(coupler), value)
end

%{key: key, acc: acc}
Expand Down
24 changes: 14 additions & 10 deletions lib/estructura/nested.ex
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ defmodule Estructura.Nested do
{:%{}, [], [{:__struct__, module} | content]}
end

@spec generator_ast(shape()) :: [{atom(), mfargs()}]
@spec generator_ast(shape() | [{:unknown, simple_type()}]) :: [{atom(), mfargs()}]
defp generator_ast(fields) do
Enum.map(fields, fn
{name, {:list, type}} ->
Expand Down Expand Up @@ -482,20 +482,24 @@ defmodule Estructura.Nested do
defp stream_data_type_for({:datetime, opts}),
do: {Estructura.StreamData, :datetime, [opts]}

defp stream_data_type_for(:datetime),
do: stream_data_type_for({:datetime, []})

defp stream_data_type_for({:date, opts}),
do: {Estructura.StreamData, :date, [opts]}

defp stream_data_type_for(:date),
do: stream_data_type_for({:date, []})
defp stream_data_type_for({stream_data_gen, opts}) when is_atom(stream_data_gen) do
params =
case generator_ast([{:unknown, type(opts)}]) do
[{_, {StreamData, gen, []}}] -> gen
[{_, params}] -> params
end

defp stream_data_type_for({:constant, const}),
do: {StreamData, :constant, [const]}
{StreamData, stream_data_gen, [params]}
end

defp stream_data_type_for(:datetime),
do: stream_data_type_for({:datetime, []})

defp stream_data_type_for({:string, kind}),
do: {StreamData, :string, [kind]}
defp stream_data_type_for(:date),
do: stream_data_type_for({:date, []})

defp stream_data_type_for(:string),
do: stream_data_type_for({:string, :alphanumeric})
Expand Down
10 changes: 6 additions & 4 deletions lib/estructura/nested/type/ip.ex
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,12 @@ defmodule Estructura.Nested.Type.IP do
end
end

defimpl Jason.Encoder do
@moduledoc false
def encode(%Estructura.Nested.Type.IP{} = ip, _opts) do
[?", to_string(ip), ?"]
if Code.ensure_loaded?(Jason.Encoder) do
defimpl Jason.Encoder do
@moduledoc false
def encode(%Estructura.Nested.Type.IP{} = ip, _opts) do
[?", to_string(ip), ?"]
end
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/estructura/nested/type/scaffold/enum.ex
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ defmodule Estructura.Nested.Type.Enum do
def validate(other),
do: {:error, "Expected #{inspect(other)} to be one of: " <> inspect(@elements)}

if is_function(unquote(encoder), 2) do
if Code.ensure_loaded?(Jason.Encoder) and is_function(unquote(encoder), 2) do
defimpl Jason.Encoder do
@moduledoc false
def encode(term, opts), do: unquote(encoder).(term, opts)
Expand Down
2 changes: 1 addition & 1 deletion lib/estructura/nested/type/scaffold/tags.ex
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ defmodule Estructura.Nested.Type.Tags do
def validate(other),
do: {:error, "Expected #{inspect(other)} to be list of: " <> inspect(@elements)}

if is_function(unquote(encoder), 2) do
if Code.ensure_loaded?(Jason.Encoder) and is_function(unquote(encoder), 2) do
defimpl Jason.Encoder do
@moduledoc false
def encode(term, opts), do: unquote(encoder).(term, opts)
Expand Down
8 changes: 5 additions & 3 deletions lib/estructura/nested/type/uri.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ defmodule Estructura.Nested.Type.URI do
def validate(other), do: {:error, "Expected URI, got: " <> inspect(other)}
end

defimpl Jason.Encoder, for: URI do
@moduledoc false
def encode(%URI{} = uri, _opts), do: [?", URI.to_string(uri), ?"]
if Code.ensure_loaded?(Jason.Encoder) do
defimpl Jason.Encoder, for: URI do
@moduledoc false
def encode(%URI{} = uri, _opts), do: [?", URI.to_string(uri), ?"]
end
end
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ defmodule Estructura.MixProject do
use Mix.Project

@app :estructura
@version "1.7.2"
@version "1.8.0"

def project do
[
Expand Down Expand Up @@ -115,6 +115,7 @@ defmodule Estructura.MixProject do
Estructura.Nested.Type.Date,
Estructura.Nested.Type.DateTime,
Estructura.Nested.Type.IP,
Estructura.Nested.Type.String,
Estructura.Nested.Type.Time,
Estructura.Nested.Type.URI
],
Expand Down
35 changes: 17 additions & 18 deletions test/estructura/nested_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -209,26 +209,25 @@ defmodule Estructura.Nested.Test do
}

raw_user_flatten =
if user.address.street.name == [] do
raw_user_flatten
else
user.address.street.name
|> Enum.with_index()
|> Enum.reduce(Map.delete(raw_user_flatten, :address_street_name), fn {v, idx}, acc ->
Map.put(acc, "address_street_name_#{idx}", v)
end)
end
user.homepage
|> Enum.with_index()
|> Enum.reduce(Map.delete(raw_user_flatten, :homepage), fn {hp, idx}, acc ->
Map.put(acc, :"homepage_#{idx}", hp)
end)

raw_user_flatten =
if user.tags == [] do
raw_user_flatten
else
user.tags
|> Enum.with_index()
|> Enum.reduce(Map.delete(raw_user_flatten, :tags), fn {v, idx}, acc ->
Map.put(acc, "tags_#{idx}", v)
end)
end
user.address.street.name
|> Enum.with_index()
|> Enum.reduce(Map.delete(raw_user_flatten, :address_street_name), fn {v, idx}, acc ->
Map.put(acc, "address_street_name_#{idx}", v)
end)

raw_user_flatten =
user.tags
|> Enum.with_index()
|> Enum.reduce(Map.delete(raw_user_flatten, :tags), fn {v, idx}, acc ->
Map.put(acc, "tags_#{idx}", v)
end)

raw_user_flatten =
raw_user_flatten
Expand Down
24 changes: 21 additions & 3 deletions test/support/structs.ex
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ defmodule Estructura.User do
Nested example. The source code of the file follows.
```elixir
defmodule Calculated do
@moduledoc false
def person(this) do
Expand All @@ -107,7 +107,7 @@ defmodule Estructura.User do
name: {:string, kind_of_codepoints: Enum.concat([?a..?c, ?l..?o])},
address: %{city: :string, street: %{name: [:string], house: :string}},
person: :string,
homepage: Estructura.Nested.Type.URI,
homepage: {:list_of, Estructura.Nested.Type.URI},
ip: Estructura.Nested.Type.IP,
data: %{age: :float},
birthday: Estructura.Nested.Type.Date,
Expand Down Expand Up @@ -185,7 +185,7 @@ defmodule Estructura.User do
name: {:string, kind_of_codepoints: Enum.concat([?a..?c, ?l..?o])},
address: %{city: :string, street: %{name: [:string], house: :positive_integer}},
person: :string,
homepage: Estructura.Nested.Type.URI,
homepage: {:list_of, Estructura.Nested.Type.URI},
ip: Estructura.Nested.Type.IP,
data: %{age: :float},
birthday: Estructura.Nested.Type.Date,
Expand Down Expand Up @@ -215,6 +215,24 @@ defmodule Estructura.User do

coerce do
defdelegate created_at(value), to: :datetime

def homepage(value) when is_list(value) do
value
|> Enum.reduce({:ok, []}, fn
value, {:error, errors} ->
case Estructura.Nested.Type.URI.coerce(value) do
{:ok, _} -> {:error, errors}
{:error, error} -> {:error, [error | errors]}
end

value, {:ok, result} ->
case Estructura.Nested.Type.URI.coerce(value) do
{:ok, coerced} -> {:ok, [coerced | result]}
{:error, error} -> {:error, [error]}
end
end)
|> then(fn {kind, list} -> {kind, Enum.reverse(list)} end)
end
end

validate do
Expand Down

0 comments on commit 72248a8

Please sign in to comment.