Packages

Reference Elixir validator for WPL (Wellness Plan Language) — JSON Schema + semantic invariants

Current section

Files

Jump to
wpl_validator lib wpl validator rules unresolved_ref.ex
Raw

lib/wpl/validator/rules/unresolved_ref.ex

defmodule WPL.Validator.Rules.UnresolvedRef do
@moduledoc false
use WPL.Validator.Rule
alias WPL.Validator.{Error, WalkContext}
# {json_field, ref_kind_string, catalog_atom_key}
@ref_kinds [
{"exercise_ref", "exercise", :exercises},
{"meal_ref", "meal", :meals},
{"meditation_ref", "meditation", :meditations}
]
@impl true
def enter_activity(ctx, activity, path) do
catalog = Keyword.get(ctx.opts, :catalog)
require_catalog = Keyword.get(ctx.opts, :require_catalog, false)
Enum.reduce(@ref_kinds, ctx, fn {field, kind, catalog_key}, acc ->
check_ref(acc, activity, path, field, kind, catalog_key, catalog, require_catalog)
end)
end
defp check_ref(ctx, activity, path, field, kind, catalog_key, catalog, require_catalog) do
ref_value = Map.get(activity, field)
if is_binary(ref_value) do
if catalog == nil do
if require_catalog do
WalkContext.emit(ctx, %Error{
path: "#{path}/#{field}",
code: :catalog_required,
message:
"catalog is required in strict mode but was not provided; " <>
"cannot resolve #{kind} '#{ref_value}'",
severity: :error,
meta: %{ref_kind: kind, ref_value: ref_value}
})
else
ctx
end
else
catalog_set = Map.get(catalog, catalog_key)
resolved = has_ref?(catalog_set, ref_value)
if resolved do
ctx
else
WalkContext.emit(ctx, %Error{
path: "#{path}/#{field}",
code: :unresolved_ref,
message: "#{kind} '#{ref_value}' not found in catalog",
severity: :error,
meta: %{ref_kind: kind, ref_value: ref_value}
})
end
end
else
ctx
end
end
# Case-insensitive membership: try exact first, then lowercase-fold both sides.
defp has_ref?(nil, _ref), do: false
defp has_ref?(set, ref) when is_struct(set, MapSet) do
if MapSet.member?(set, ref) do
true
else
ref_lower = String.downcase(ref)
Enum.any?(set, fn entry -> String.downcase(entry) == ref_lower end)
end
end
defp has_ref?(_set, _ref), do: false
end