diff --git a/lib/harald/data_type.ex b/lib/harald/data_type.ex new file mode 100644 index 0000000..38bdb8c --- /dev/null +++ b/lib/harald/data_type.ex @@ -0,0 +1,17 @@ +defmodule Harald.DataType do + @moduledoc """ + Reference: Core Specification Supplement, Part A + """ + + alias Harald.DataType.{ManufacturerData, ServiceData} + + @doc """ + Returns a list of data type implementation modules. + """ + def modules do + [ + ManufacturerData, + ServiceData + ] + end +end diff --git a/lib/harald/data_type/manufacturer_data.ex b/lib/harald/data_type/manufacturer_data.ex index 06d4c77..24d2959 100644 --- a/lib/harald/data_type/manufacturer_data.ex +++ b/lib/harald/data_type/manufacturer_data.ex @@ -1,4 +1,4 @@ -defmodule Harald.ManufacturerData do +defmodule Harald.DataType.ManufacturerData do @moduledoc """ > The Manufacturer Specific data type is used for manufacturer specific data. @@ -8,7 +8,7 @@ defmodule Harald.ManufacturerData do `Harald.ManufacturerDataBehaviour` and `Harald.Serializable` behaviours. """ - alias Harald.ManufacturerData.Apple + alias Harald.DataType.ManufacturerData.Apple require Harald.AssignedNumbers.CompanyIdentifiers, as: CompanyIdentifiers @modules [Apple] @@ -59,12 +59,10 @@ defmodule Harald.ManufacturerData do ) do case unquote(module).deserialize(sub_bin) do {:ok, data} -> {:ok, {unquote(module).company, data}} - :error -> {:error, bin} + {:error, _} -> {:error, bin} end end end) - def deserialize(_) do - {:error, :unhandled_company_id} - end + def deserialize(bin), do: {:error, bin} end diff --git a/lib/harald/data_type/manufacturer_data/apple.ex b/lib/harald/data_type/manufacturer_data/apple.ex index 06ca54a..7c08abc 100644 --- a/lib/harald/data_type/manufacturer_data/apple.ex +++ b/lib/harald/data_type/manufacturer_data/apple.ex @@ -1,4 +1,4 @@ -defmodule Harald.ManufacturerData.Apple do +defmodule Harald.DataType.ManufacturerData.Apple do @moduledoc """ Serialization module for Apple. @@ -83,10 +83,7 @@ defmodule Harald.ManufacturerData.Apple do {:ok, {"iBeacon", %{major: 1, minor: 2, tx_power: 3, uuid: 4}}} iex> deserialize(<<2, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 1, 0, 2>>) - :error - - iex> deserialize(false) - :error + {:error, <<2, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 1, 0, 2>>} """ @impl Serializable def deserialize(<<@ibeacon_identifier, @ibeacon_length, binary::binary-size(21)>>) do @@ -107,5 +104,5 @@ defmodule Harald.ManufacturerData.Apple do {:ok, {"iBeacon", data}} end - def deserialize(_), do: :error + def deserialize(bin) when is_binary(bin), do: {:error, bin} end diff --git a/lib/harald/data_type/service_data.ex b/lib/harald/data_type/service_data.ex new file mode 100644 index 0000000..c78e206 --- /dev/null +++ b/lib/harald/data_type/service_data.ex @@ -0,0 +1,64 @@ +defmodule Harald.DataType.ServiceData do + @moduledoc """ + > The Service Data data type consists of a service UUID with the data associated with that + > service. + + Reference: Core Specification Supplement, Part A, section 1.11.1 + """ + + alias Harald.Serializable + require Harald.AssignedNumbers.GenericAccessProfile, as: GenericAccessProfile + + @behaviour Serializable + + @description_32 "Service Data - 32-bit UUID" + + @doc """ + Returns the three GAP descriptions encompassed by service data. + """ + def gap_descriptions, do: [@description_32] + + @doc """ + iex> serialize({"Service Data - 32-bit UUID", %{data: <<5, 6>>, uuid: 67305985}}) + {:ok, <<32, 1, 2, 3, 4, 5, 6>>} + """ + @impl Serializable + def serialize({@description_32, %{data: data, uuid: uuid}}) do + binary = << + GenericAccessProfile.id(unquote(@description_32)), + uuid::little-size(32), + data::binary + >> + + {:ok, binary} + end + + def serialize(_), do: :error + + @doc """ + Deserializes a service data binary. + + iex> deserialize(<<32, 1, 2, 3, 4, 5, 6>>) + {:ok, {"Service Data - 32-bit UUID", %{data: <<5, 6>>, uuid: 67305985}}} + """ + @impl Serializable + def deserialize(<>) do + {status, data} = + case bin do + <> -> + service_data_32 = %{ + uuid: uuid, + data: data + } + + {:ok, service_data_32} + + _ -> + {:error, bin} + end + + {status, {@description_32, data}} + end + + def deserialize(bin), do: {:error, bin} +end diff --git a/lib/harald/hci/event/le_meta/advertising_report/device.ex b/lib/harald/hci/event/le_meta/advertising_report/device.ex index 9e939e4..eaeb279 100644 --- a/lib/harald/hci/event/le_meta/advertising_report/device.ex +++ b/lib/harald/hci/event/le_meta/advertising_report/device.ex @@ -22,7 +22,7 @@ defmodule Harald.HCI.Event.LEMeta.AdvertisingReport.Device do """ alias Harald.HCI.{ArrayedData, Event.LEMeta.AdvertisingReport.Device} - alias Harald.{ManufacturerData, Serializable, Transport.UART.Framing} + alias Harald.{DataType.ManufacturerData, Serializable, Transport.UART.Framing} require Harald.AssignedNumbers.GenericAccessProfile, as: GenericAccessProfile @enforce_keys [:event_type, :address_type, :address, :data, :rss] diff --git a/test/harald/data_type/manufacturer_data/apple_test.exs b/test/harald/data_type/manufacturer_data/apple_test.exs new file mode 100644 index 0000000..88ce49b --- /dev/null +++ b/test/harald/data_type/manufacturer_data/apple_test.exs @@ -0,0 +1,24 @@ +defmodule Harald.DataType.ManufacturerData.AppleTest do + use ExUnit.Case, async: true + use ExUnitProperties + alias Harald.Generators.DataType.ManufacturerData.Apple, as: AppleGen + alias Harald.DataType.ManufacturerData.Apple + + doctest Apple, import: true + + property "symmetric (de)serialization" do + check all bin <- AppleGen.binary() do + case Apple.deserialize(bin) do + {:ok, data} -> assert {:ok, bin} == Apple.serialize(data) + {:error, _} -> :ok + end + end + + check all bin <- StreamData.binary() do + case Apple.deserialize(bin) do + {:ok, data} -> assert {:ok, bin} == Apple.serialize(data) + {:error, _} -> :ok + end + end + end +end diff --git a/test/harald/manufacturer_data_test.exs b/test/harald/data_type/manufacturer_data_test.exs similarity index 77% rename from test/harald/manufacturer_data_test.exs rename to test/harald/data_type/manufacturer_data_test.exs index 390509e..13995c4 100644 --- a/test/harald/manufacturer_data_test.exs +++ b/test/harald/data_type/manufacturer_data_test.exs @@ -1,8 +1,8 @@ -defmodule Harald.ManufacturerDataTest do +defmodule Harald.DataType.ManufacturerDataTest do use ExUnit.Case, async: true use ExUnitProperties - alias Harald.Generators.ManufacturerData, as: ManufacturerDataGen - alias Harald.ManufacturerData + alias Harald.Generators.DataType.ManufacturerData, as: ManufacturerDataGen + alias Harald.DataType.ManufacturerData doctest ManufacturerData, import: true diff --git a/test/harald/data_type/service_data_test.exs b/test/harald/data_type/service_data_test.exs new file mode 100644 index 0000000..1f48a54 --- /dev/null +++ b/test/harald/data_type/service_data_test.exs @@ -0,0 +1,24 @@ +defmodule Harald.DataType.ServiceDataTest do + use ExUnit.Case, async: true + use ExUnitProperties + alias Harald.Generators.DataType.ServiceData, as: ServiceDataGen + alias Harald.DataType.ServiceData + + doctest ServiceData, import: true + + property "symmetric (de)serialization" do + check all bin <- ServiceDataGen.binary() do + case ServiceData.deserialize(bin) do + {:ok, data} -> assert {:ok, bin} == ServiceData.serialize(data) + {:error, _} -> :ok + end + end + + check all bin <- StreamData.binary() do + case ServiceData.deserialize(bin) do + {:ok, data} -> assert {:ok, bin} == ServiceData.serialize(data) + {:error, _} -> :ok + end + end + end +end diff --git a/test/harald/manufacturer_data/apple_test.exs b/test/harald/manufacturer_data/apple_test.exs deleted file mode 100644 index 544593d..0000000 --- a/test/harald/manufacturer_data/apple_test.exs +++ /dev/null @@ -1,18 +0,0 @@ -defmodule Harald.ManufacturerData.AppleTest do - use ExUnit.Case, async: true - use ExUnitProperties - alias Harald.Generators.ManufacturerData.Apple, as: AppleGen - alias Harald.ManufacturerData.Apple - - doctest Apple, import: true - - property "symmetric (de)serialization" do - check all binary <- AppleGen.binary() do - assert {:ok, binary} == - binary - |> Apple.deserialize() - |> elem(1) - |> Apple.serialize() - end - end -end diff --git a/test/support/generators/manufacturer_data.ex b/test/support/generators/data_type/manufacturer_data.ex similarity index 81% rename from test/support/generators/manufacturer_data.ex rename to test/support/generators/data_type/manufacturer_data.ex index bcc788a..6bb92c6 100644 --- a/test/support/generators/manufacturer_data.ex +++ b/test/support/generators/data_type/manufacturer_data.ex @@ -1,10 +1,10 @@ -defmodule Harald.Generators.ManufacturerData do +defmodule Harald.Generators.DataType.ManufacturerData do @moduledoc """ StreamData generators for manufacturer data. """ use ExUnitProperties - alias Harald.{Generators, ManufacturerData} + alias Harald.{DataType.ManufacturerData, Generators} require Harald.AssignedNumbers.GenericAccessProfile, as: GenericAccessProfile @spec binary :: no_return() diff --git a/test/support/generators/manufacturer_data/apple.ex b/test/support/generators/data_type/manufacturer_data/apple.ex similarity index 79% rename from test/support/generators/manufacturer_data/apple.ex rename to test/support/generators/data_type/manufacturer_data/apple.ex index ca2b76d..439104f 100644 --- a/test/support/generators/manufacturer_data/apple.ex +++ b/test/support/generators/data_type/manufacturer_data/apple.ex @@ -1,4 +1,4 @@ -defmodule Harald.Generators.ManufacturerData.Apple do +defmodule Harald.Generators.DataType.ManufacturerData.Apple do @moduledoc """ StreamData generators for Apple manufacturer data. @@ -8,7 +8,7 @@ defmodule Harald.Generators.ManufacturerData.Apple do """ use ExUnitProperties - alias Harald.ManufacturerData.Apple + alias Harald.DataType.ManufacturerData.Apple @spec binary :: no_return() def binary do diff --git a/test/support/generators/data_type/service_data.ex b/test/support/generators/data_type/service_data.ex new file mode 100644 index 0000000..49b9d13 --- /dev/null +++ b/test/support/generators/data_type/service_data.ex @@ -0,0 +1,25 @@ +defmodule Harald.Generators.DataType.ServiceData do + @moduledoc """ + StreamData generators for service data. + """ + + import ExUnitProperties, only: :macros + alias Harald.DataType.ServiceData + require Harald.AssignedNumbers.GenericAccessProfile, as: GenericAccessProfile + + @description_32 "Service Data - 32-bit UUID" + + def binary do + gen all gap_description <- StreamData.member_of(ServiceData.gap_descriptions()), + bin <- binary(gap_description) do + bin + end + end + + def binary(@description_32) do + gen all uuid <- StreamData.binary(length: 4), + data <- StreamData.binary(length: 1) do + <> + end + end +end