defprotocol DataLength do
@moduledoc """
DataLength protocol defines a function to retrieve the length of data for various data types.
Implemenations of this protocol need to implement `len(data)` function.
## Default Implementations
The protocol provides default implementations for the following data types:
- `String`: Returns the number of graphemes in the string.
- `List`: Returns the number of elements in the list.
- `Map`: Returns the number of key-value pairs at the first level of the map.
- `Tuple`: Returns the number of elements in the tuple.
- `Integer`: Returns the number of digits in the integer plus 1 for a minus sign if the number is negative.
- `Float`: Returns the number of digits in the float plus 1 for a minus sign if the number is negative and 1 for a ".".
### Default implementations examples:
`BitString` implementation returns the number of graphemes:
iex> DataLength.len("Hello! Dzień dobry!")
19
`List` implementation returns the number of elements in the list.
iex> DataLength.len(["a", :b, ["c"], %{d: "d"}])
4
`Map` implementation returns the number of key-value pairs at the first level of the map.:
iex> DataLength.len(%{a: 1, b: [2], c: "hello"})
3
`Tuple` implementation returns the number of elements in the tuple:
iex> DataLength.len({"a", :b, ["c", "d"], {"e"}, "f"})
5
`Integer` implementation returns the number of digits in the integer plus 1 for a minus sign if the number is negative:
iex> DataLength.len(1234567890)
10
iex> DataLength.len(-1234567890)
11
`Float` implemantation returns the number of digits in the float plus 1 for a minus sign if the number is negative and 1 for a ".". Terminal zeros in fractional part are ignored:
iex> DataLength.len(12345.6789)
10
iex> DataLength.len(12345.67890)
10
iex> DataLength.len(12345.67899)
11
iex> DataLength.len(-12345.67890)
11
iex> DataLength.len(-12345.67899)
12
## Custom implementation:
You can implement custom length calculations for other data types by providing specific protocol implementations.
### Example
defmodule MyStruct do
defstruct data: [1, 2, 3]
end
defimpl Length, for: MyStruct do
def len(%MyStruct{data: data}), do: length(data)
end
"""
@doc """
Retrieves the length of the input data.
## Parameters
* `data` - The data for which to calculate the length.
## Returns
An integer representing the length of the data.
"""
@spec len(any) :: non_neg_integer
def len(data)
end
defimpl DataLength, for: BitString do
@spec len(binary) :: non_neg_integer
def len(data), do: String.length(data)
end
defimpl DataLength, for: List do
@spec len(list) :: non_neg_integer
def len(data), do: length(data)
end
defimpl DataLength, for: Map do
@spec len(map) :: non_neg_integer
def len(data), do: map_size(data)
end
defimpl DataLength, for: Tuple do
@spec len(tuple) :: non_neg_integer
def len(data), do: tuple_size(data)
end
defimpl DataLength, for: Integer do
@spec len(integer) :: non_neg_integer
def len(data), do: data |> Integer.to_string() |> DataLength.len()
end
defimpl DataLength, for: Float do
@spec len(float) :: non_neg_integer
def len(data), do: data |> Float.to_string() |> DataLength.len()
end