Packages

Fork of tmbb/dantzig tweaked for Advent of Code 2025

Current section

Files

Jump to
dantzig_adventofcode2025 lib dantzig constraint_metadata.ex
Raw

lib/dantzig/constraint_metadata.ex

defmodule Dantzig.ConstraintMetadata do
@moduledoc """
Constraint metadata for debugging purposes.
"""
@type t :: %__MODULE__{}
defstruct app: nil,
module: nil,
file: nil,
line: nil,
tags: [],
attrs: %{}
@doc """
Create a metadata struct from an environment (e.g. `__ENV__`, `__CALLER__`),
and some extra attributes.
Supports the following extra attributes:
- `:tags` (*optional*, default: [])
- `:attrs` (*optional*, default: [])
"""
def from_env(env, extra \\ []) do
app = Application.get_application(env.module)
%__MODULE__{
app: app,
module: env.module,
file: env.file,
line: env.line,
tags: Keyword.get(extra, :tags, []),
attrs: Keyword.get(extra, :attrs, %{})
}
end
@doc """
Updates a metadata struct withn extra attributes.
Supports the following extra attributes:
- `:tags` (*optional*, default: [])
- `:attrs` (*optional*, default: [])
If the `:app` field isn't present, the function will attempt
to get the `:app` value from the given `:module`.
If the metadata was created from an environment,
sometimes the application name can't be deduced at
compile time, and if this function runs at runtime,
it will pretty much always be able to get the application
from the module name.
"""
def update(metadata, extra) do
# Create an actual metadata struct if it doesn't exist
metadata = metadata || %__MODULE__{}
# Try to find the app at runtime if it couldn't be found
# at compile-time
app =
case {metadata.app, metadata.module} do
{nil, nil} ->
nil
{nil, module} ->
Application.get_application(module)
{app, _module} ->
app
end
new_tags = Keyword.get(extra, :tags, [])
new_attrs = Keyword.get(extra, :attrs, %{})
all_tags = metadata.tags ++ new_tags
all_attrs = Map.merge(metadata.attrs, new_attrs)
%{metadata | app: app, tags: all_tags, attrs: all_attrs}
end
@doc """
Converts constraint metadata into a comment in the `.lp` file format.
Supports `nil` as an argument, so it can be used for constraints without metadata.
"""
def to_lp_comment(nil), do: ""
def to_lp_comment(metadata) do
location_line =
case metadata.file do
nil ->
""
file ->
relative_path = Path.relative_to(file, File.cwd!())
" \\\\ location: #{relative_path}:#{metadata.line}\n"
end
tags = inspect(metadata.tags)
[
" \\\\ app: #{inspect(metadata.app)} | module: #{inspect(metadata.module)}\n",
location_line,
" \\\\ tags: #{tags}\n"
]
end
end