defmodule Alaja.Structures.MessageInfo do
@moduledoc """
Contains all information related to message printing.
## Fields
- `chunks`: List of ChunkText to print
- `align`: Alignment (:left, :center, :right, :justified)
- `padding`: Padding (integer or tuple {top, right, bottom, left})
- `add_line`: Additional lines (:before, :after, :both, :none)
- `raw_coords`: Coordinates for raw mode {x, y}
## Examples
iex> MessageInfo.new([ChunkText.new("Hello")])
%MessageInfo{chunks: [...], align: :left, padding: 0, ...}
iex> MessageInfo.new([ChunkText.new("Hello")], align: :center, padding: 2)
%MessageInfo{chunks: [...], align: :center, padding: 2, ...}
"""
alias Alaja.Structures.ChunkText
@type align :: :left | :center | :right | :justified
@type add_line :: :before | :after | :both | :none
@type padding :: integer() | {integer(), integer(), integer(), integer()}
@type t :: %__MODULE__{
chunks: list(ChunkText.t()),
align: align(),
padding: padding(),
add_line: add_line(),
raw_coords: {integer(), integer()} | nil
}
defstruct chunks: [],
align: :left,
padding: 0,
add_line: :none,
raw_coords: nil
@valid_aligns [:left, :center, :right, :justified]
@valid_add_lines [:before, :after, :both, :none]
@doc """
Creates a new MessageInfo structure.
## Parameters
- `chunks` - List of ChunkText or strings (automatically converted)
- `opts` - Options:
- `:align` - Alignment (default: :left)
- `:padding` - Padding (default: 0)
- `:add_line` - Additional lines (default: :none)
- `:raw_coords` - Coordinates for raw mode (default: nil)
## Examples
iex> MessageInfo.new(["Hello", " world"])
%MessageInfo{chunks: [...], align: :left}
iex> MessageInfo.new([ChunkText.new("Hello")], align: :center, padding: 2)
%MessageInfo{chunks: [...], align: :center, padding: 2}
"""
@spec new(list(String.t() | ChunkText.t()), keyword()) :: t()
def new(chunks, opts \\ []) do
chunks =
Enum.map(chunks, fn
%ChunkText{} = chunk -> chunk
text when is_binary(text) -> ChunkText.new(text)
end)
align = Keyword.get(opts, :align, :left)
align = if align in @valid_aligns, do: align, else: :left
padding = Keyword.get(opts, :padding, 0)
add_line = Keyword.get(opts, :add_line, :none)
add_line = if add_line in @valid_add_lines, do: add_line, else: :none
raw_coords = Keyword.get(opts, :raw_coords)
%__MODULE__{
chunks: chunks,
align: align,
padding: padding,
add_line: add_line,
raw_coords: raw_coords
}
end
@doc """
Gets the complete message text (without formatting).
## Examples
iex> msg = MessageInfo.new(["Hello", " world"])
iex> MessageInfo.get_text(msg)
"Hello world"
"""
@spec get_text(t()) :: String.t()
def get_text(%__MODULE__{chunks: chunks}) do
Enum.map_join(chunks, "", fn chunk -> chunk.text end)
end
@doc """
Gets the message width (text length without formatting).
## Examples
iex> msg = MessageInfo.new(["Hello world"])
iex> MessageInfo.get_width(msg)
11
"""
@spec get_width(t()) :: integer()
def get_width(%__MODULE__{} = message_info) do
message_info |> get_text() |> String.length()
end
@doc """
Normalizes padding to tuple {top, right, bottom, left}.
## Examples
iex> MessageInfo.normalize_padding(2)
{2, 2, 2, 2}
iex> MessageInfo.normalize_padding({1, 2, 3, 4})
{1, 2, 3, 4}
"""
@spec normalize_padding(padding()) :: {integer(), integer(), integer(), integer()}
def normalize_padding(padding) when is_integer(padding) do
{padding, padding, padding, padding}
end
def normalize_padding({top, right, bottom, left}) do
{top, right, bottom, left}
end
end