Skip to content

Commit

Permalink
Merge pull request #12 from njausteve/add-date-style-content-field
Browse files Browse the repository at this point in the history
add date style content field
  • Loading branch information
njausteve authored Sep 21, 2024
2 parents 1e49b75 + a6124cc commit 487f1b9
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 82 deletions.
102 changes: 65 additions & 37 deletions lib/structs/field_content.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,21 @@ defmodule ExPass.Structs.FieldContent do
- `data_detector_types`: A list of data detectors to apply to the field's value.
These detectors can automatically convert certain types of data into tappable links.
By default, all data detectors are applied. To use no data detectors, specify an empty list.
Supported values are:
* "PKDataDetectorTypePhoneNumber" - Detects phone numbers
* "PKDataDetectorTypeLink" - Detects URLs and web links
* "PKDataDetectorTypeAddress" - Detects physical addresses
* "PKDataDetectorTypeCalendarEvent" - Detects calendar events
- `date_style`: The style of the date to display in the field.
Supported values are:
* "PKDateStyleNone"
* "PKDateStyleShort"
* "PKDateStyleMedium"
* "PKDateStyleLong"
* "PKDateStyleFull"
"""

use TypedStruct
Expand Down Expand Up @@ -70,21 +79,35 @@ defmodule ExPass.Structs.FieldContent do
- "PKDataDetectorTypeCalendarEvent"
These detectors can automatically convert certain types of data into tappable links.
By default, all data detectors are applied. To use no data detectors, specify an empty list.
"""
@type data_detector_types() :: list(String.t()) | []

@typedoc """
The style of the date to display in the field.
Optional. Valid values are:
- "PKDateStyleNone"
- "PKDateStyleShort"
- "PKDateStyleMedium"
- "PKDateStyleLong"
- "PKDateStyleFull"
"""
@type data_detector_types() :: list(String.t())
@type date_style() :: String.t()

typedstruct do
field :attributed_value, attributed_value(), default: nil
field :change_message, String.t(), default: nil
field :currency_code, String.t(), default: nil
field :data_detector_types, data_detector_types(), default: nil
field :date_style, date_style(), default: nil
end

@doc """
Creates a new FieldContent struct.
This function initializes a new FieldContent struct with the given attributes.
It validates the `attributed_value`, `change_message`, `currency_code`, and `data_detector_types`.
It validates the `attributed_value`, `change_message`, `currency_code`, `data_detector_types`, and `date_style`.
## Parameters
Expand All @@ -101,19 +124,22 @@ defmodule ExPass.Structs.FieldContent do
## Examples
iex> FieldContent.new(%{attributed_value: "Hello, World!"})
%FieldContent{attributed_value: "Hello, World!", change_message: nil, currency_code: nil, data_detector_types: nil}
%FieldContent{attributed_value: "Hello, World!", change_message: nil, currency_code: nil, data_detector_types: nil, date_style: nil}
iex> FieldContent.new(%{attributed_value: 42, data_detector_types: ["PKDataDetectorTypePhoneNumber"]})
%FieldContent{attributed_value: 42, change_message: nil, currency_code: nil, data_detector_types: ["PKDataDetectorTypePhoneNumber"]}
iex> FieldContent.new(%{attributed_value: 42, data_detector_types: ["PKDataDetectorTypePhoneNumber"], date_style: "PKDateStyleShort"})
%FieldContent{attributed_value: 42, change_message: nil, currency_code: nil, data_detector_types: ["PKDataDetectorTypePhoneNumber"], date_style: "PKDateStyleShort"}
iex> datetime = DateTime.utc_now()
iex> field_content = FieldContent.new(%{attributed_value: datetime, currency_code: "USD"})
iex> %FieldContent{attributed_value: ^datetime, currency_code: "USD"} = field_content
iex> field_content = FieldContent.new(%{attributed_value: datetime, currency_code: "USD", date_style: "PKDateStyleLong"})
iex> %FieldContent{attributed_value: ^datetime, currency_code: "USD", date_style: "PKDateStyleLong"} = field_content
iex> field_content.change_message
nil
iex> FieldContent.new(%{attributed_value: "<a href='http://example.com'>Click here</a>", data_detector_types: ["PKDataDetectorTypeLink"]})
%FieldContent{attributed_value: "<a href='http://example.com'>Click here</a>", change_message: nil, currency_code: nil, data_detector_types: ["PKDataDetectorTypeLink"]}
iex> FieldContent.new(%{attributed_value: "<a href='http://example.com'>Click here</a>", data_detector_types: ["PKDataDetectorTypeLink"], date_style: "PKDateStyleFull"})
%FieldContent{attributed_value: "<a href='http://example.com'>Click here</a>", change_message: nil, currency_code: nil, data_detector_types: ["PKDataDetectorTypeLink"], date_style: "PKDateStyleFull"}
iex> FieldContent.new(%{attributed_value: "No detectors", data_detector_types: []})
%FieldContent{attributed_value: "No detectors", change_message: nil, currency_code: nil, data_detector_types: [], date_style: nil}
"""
@spec new(map()) :: %__MODULE__{}
def new(attrs \\ %{}) do
Expand All @@ -124,6 +150,7 @@ defmodule ExPass.Structs.FieldContent do
|> validate(:change_message, &Validators.validate_change_message/1)
|> validate(:currency_code, &Validators.validate_currency_code/1)
|> validate(:data_detector_types, &Validators.validate_data_detector_types/1)
|> validate(:date_style, &Validators.validate_date_style/1)

struct!(__MODULE__, attrs)
end
Expand All @@ -134,37 +161,38 @@ defmodule ExPass.Structs.FieldContent do
attrs

{:error, reason} ->
cond do
key == :attributed_value ->
raise ArgumentError, """
Invalid attributed_value: #{inspect(attrs[key])}
Reason: #{reason}
Supported types are: String (including <a></a> tag), number, DateTime and Date
"""

key == :change_message ->
raise ArgumentError, """
Invalid change_message: #{inspect(attrs[key])}
Reason: #{reason}
The change_message must be a string containing the '%@' placeholder for the new value.
"""

key == :data_detector_types ->
raise ArgumentError, """
Invalid data_detector_types: #{inspect(attrs[key])}
Reason: #{reason}
data_detector_types must be a list of valid detector type strings.
"""

true ->
raise ArgumentError, """
Invalid value for #{key}: #{inspect(attrs[key])}
Reason: #{reason}
"""
end
error_message = get_error_message(key, attrs[key], reason)
raise ArgumentError, error_message
end
end

defp get_error_message(key, value, reason) do
base_message = """
Invalid #{key}: #{inspect(value)}
Reason: #{reason}
"""

additional_info =
case key do
:attributed_value ->
"Supported types are: String (including <a></a> tag), number, DateTime and Date"

:change_message ->
"The change_message must be a string containing the '%@' placeholder for the new value."

:data_detector_types ->
"data_detector_types must be a list of valid detector type strings. Use an empty list to disable all detectors."

:date_style ->
"Supported values are: PKDateStyleNone, PKDateStyleShort, PKDateStyleMedium, PKDateStyleLong, PKDateStyleFull"

_ ->
""
end

base_message <> additional_info
end

defimpl Jason.Encoder do
def encode(field_content, opts) do
field_content
Expand Down
47 changes: 47 additions & 0 deletions lib/utils/validators.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ defmodule ExPass.Utils.Validators do
"PKDataDetectorTypeCalendarEvent"
]

@valid_date_styles [
"PKDateStyleNone",
"PKDateStyleShort",
"PKDateStyleMedium",
"PKDateStyleLong",
"PKDateStyleFull"
]

@doc """
Validates the type of the attributed value.
Expand Down Expand Up @@ -203,6 +211,45 @@ defmodule ExPass.Utils.Validators do

def validate_data_detector_types(_), do: {:error, "data_detector_types must be a list"}

@doc """
Validates the date_style field.
The date_style must be a valid date style string.
## Returns
* `:ok` if the value is a valid date style string or nil.
* `{:error, reason}` if the value is not valid, where reason is a string explaining the error.
## Examples
iex> validate_date_style("PKDateStyleShort")
:ok
iex> validate_date_style(nil)
:ok
iex> validate_date_style("InvalidStyle")
{:error, "Invalid date_style: InvalidStyle. Supported values are: PKDateStyleNone, PKDateStyleShort, PKDateStyleMedium, PKDateStyleLong, PKDateStyleFull"}
iex> validate_date_style(42)
{:error, "date_style must be a string"}
"""
@spec validate_date_style(String.t() | nil) :: :ok | {:error, String.t()}
def validate_date_style(nil), do: :ok

def validate_date_style(style) when is_binary(style) do
if style in @valid_date_styles do
:ok
else
{:error,
"Invalid date_style: #{style}. Supported values are: #{Enum.join(@valid_date_styles, ", ")}"}
end
end

def validate_date_style(_), do: {:error, "date_style must be a string"}

defp contains_unsupported_html_tags?(string) do
# Remove all valid anchor tags
string_without_anchors = String.replace(string, ~r{<a\s[^>]*>.*?</a>|<a\s[^>]*/>}, "")
Expand Down
Loading

0 comments on commit 487f1b9

Please sign in to comment.