From 0938752c0051837d5bbf9287395029ca40d180b9 Mon Sep 17 00:00:00 2001 From: Luca Corti Date: Tue, 24 Sep 2024 16:58:40 +0200 Subject: [PATCH] WIP --- lib/sassone/builder.ex | 29 +++++++++++++++++++++++------ lib/sassone/builder/field.ex | 12 ++++-------- lib/sassone/xml.ex | 4 ++++ test/sassone/builder_test.exs | 20 ++++++++++++++++++++ test/support/test_schemas.ex | 14 ++++++++++++++ 5 files changed, 65 insertions(+), 14 deletions(-) create mode 100644 test/support/test_schemas.ex diff --git a/lib/sassone/builder.ex b/lib/sassone/builder.ex index 3151721..7eed6a7 100644 --- a/lib/sassone/builder.ex +++ b/lib/sassone/builder.ex @@ -83,14 +83,14 @@ defimpl Sassone.Builder, for: Any do %Field{struct(Field, field_options) | xml_name: xml_name, name: name} end) - {elements, attributes} = + {attributes, elements} = Enum.split_with(fields, fn %Field{} = field -> - field.type == :element + field.type == :attribute end) start_document = generate_start_document(module) end_document = generate_end_document() - start_element = generate_start_element(elements) + start_element = generate_start_element(elements, attributes) characters = generate_characters(elements) end_element = generate_end_element(elements) @@ -163,13 +163,30 @@ defimpl Sassone.Builder, for: Any do end end - defp generate_start_element(elements) do - Enum.filter(elements, fn %Field{} = field -> field.parse end) + defp generate_start_element(elements, attributes) do + Enum.filter(elements, fn %Field{} = field -> field.parse and field.type != :content end) |> Enum.reduce( [ quote do @impl Sassone.Handler - def handle_event(:start_element, _data, state), do: {:ok, state} + def handle_event(:start_element, {ns, element, attributes}, %Parser{} = parser) do + state = + unquote( + Enum.map(attributes, fn %Field{} = field -> {field.name, field.xml_name} end) + ) + |> Enum.reduce(parser.state, fn {key, xml_name}, state -> + value = + Enum.find_value(attributes, fn {_ns, name, value} -> + if name == xml_name, do: value + end) + + unless is_nil(value) do + put_in(state, Enum.reverse([key | parser.keys]), value) + end + end) + + {:ok, %Parser{parser | state: state}} + end end ], fn diff --git a/lib/sassone/builder/field.ex b/lib/sassone/builder/field.ex index d22626f..13b4de8 100644 --- a/lib/sassone/builder/field.ex +++ b/lib/sassone/builder/field.ex @@ -3,21 +3,17 @@ defmodule Sassone.Builder.Field do A struct representing the builder options for a struct field. """ - @type type :: :element | :attribute - - @type name :: atom() - @type t :: %__MODULE__{ - name: name(), + name: atom(), parse: boolean(), many: boolean(), struct: module(), build: boolean(), - type: type(), + type: :attribute | :content | :element, xml_name: String.t() } - @enforce_keys [:name, :parse, :build, :type, :xml_name] + @enforce_keys [:name, :type, :xml_name] defstruct build: true, name: nil, many: false, @@ -66,7 +62,7 @@ defmodule Sassone.Builder.Field do ], type: [ doc: "How the field is represented in XML: `:element`, `:attribute`, `:content`.", - type: {:in, [:element, :attribute]}, + type: {:in, [:attribute, :content, :element]}, default: :element ] ] diff --git a/lib/sassone/xml.ex b/lib/sassone/xml.ex index 8cbbcd5..64014a0 100644 --- a/lib/sassone/xml.ex +++ b/lib/sassone/xml.ex @@ -132,6 +132,10 @@ defmodule Sassone.XML do Enum.reduce(values, elements, &build_element(field, &1, &2)) end + defp build_element(%Field{type: :content}, value, elements) do + [characters(value) | elements] + end + defp build_element(%Field{} = field, value, elements) do if Builder.impl_for(value) do [build(value, field.xml_name) | elements] diff --git a/test/sassone/builder_test.exs b/test/sassone/builder_test.exs index 8c7a1a6..e0123bc 100644 --- a/test/sassone/builder_test.exs +++ b/test/sassone/builder_test.exs @@ -1,3 +1,23 @@ defmodule Sassone.BuilderTest do use ExUnit.Case, async: true + + alias Sassone.Builder + alias Sassone.TestSchemas.Person + + describe "building" do + test "encode simple schema" do + assert ~s|Alice| = + Builder.build(%Person{gender: "female", name: "Alice"}) + |> Sassone.encode!() + end + + test "decode simple schema" do + assert {:ok, {Person, %{gender: "female", name: "Alice"}}} = + Sassone.parse_string( + ~s|Alice|, + Builder.handler(%Person{}), + nil + ) + end + end end diff --git a/test/support/test_schemas.ex b/test/support/test_schemas.ex new file mode 100644 index 0000000..02dc049 --- /dev/null +++ b/test/support/test_schemas.ex @@ -0,0 +1,14 @@ +defmodule Sassone.TestSchemas do + @moduledoc false + + defmodule Person do + @derive { + Sassone.Builder, + case: :snake, + debug: true, + root_element: "person", + fields: [gender: [type: :attribute], name: [type: :content]] + } + defstruct [:gender, :name] + end +end