Packages
Terminal emulation and driver infrastructure for Raxol. ANSI parsing, screen buffers, command processing, cursor management, input handling, session management, and termbox2 NIF integration.
Current section
Files
Jump to
Current section
Files
lib/raxol/terminal/sync/protocol.ex
defmodule Raxol.Terminal.Sync.Protocol do
@moduledoc """
Defines the synchronization protocol for terminal components.
Handles message formats, versioning, and conflict resolution strategies.
"""
# Types
@type sync_message :: %{
type: :sync | :ack | :conflict | :resolve,
component_id: String.t(),
component_type: :split | :window | :tab,
state: term(),
metadata: %{
version: non_neg_integer(),
timestamp: non_neg_integer(),
source: String.t(),
consistency: :strong | :eventual | :causal
}
}
@type sync_result ::
:ok | {:error, :conflict | :version_mismatch | :invalid_state}
# Message Types
@sync_type :sync
@ack_type :ack
@conflict_type :conflict
@resolve_type :resolve
# Protocol Functions
def create_sync_message(component_id, component_type, state, opts \\ []) do
# Convert keyword list to map if needed
opts_map = convert_opts_to_map(Keyword.keyword?(opts), opts)
%{
type: @sync_type,
component_id: component_id,
component_type: component_type,
state: state,
metadata: %{
version: Map.get(opts_map, :version, System.monotonic_time()),
timestamp: System.system_time(),
source: Map.get(opts_map, :source, "unknown"),
consistency:
Map.get(
opts_map,
:consistency,
get_default_consistency(component_type)
)
}
}
end
def create_ack_message(component_id, component_type, version) do
%{
type: @ack_type,
component_id: component_id,
component_type: component_type,
metadata: %{
version: version,
timestamp: System.system_time()
}
}
end
def create_conflict_message(
component_id,
component_type,
current_state,
incoming_state
) do
%{
type: @conflict_type,
component_id: component_id,
component_type: component_type,
states: %{
current: current_state,
incoming: incoming_state
},
metadata: %{
timestamp: System.system_time()
}
}
end
def create_resolve_message(
component_id,
component_type,
resolved_state,
version
) do
%{
type: @resolve_type,
component_id: component_id,
component_type: component_type,
state: resolved_state,
metadata: %{
version: version,
timestamp: System.system_time()
}
}
end
# Protocol Handlers
def handle_sync_message(message, current_state) do
case validate_message(message) do
:ok ->
handle_valid_sync(message, current_state)
{:error, reason} ->
{:error, reason}
end
end
def handle_ack_message(message, current_state) do
case validate_message(message) do
:ok ->
handle_valid_ack(message, current_state)
{:error, reason} ->
{:error, reason}
end
end
def handle_conflict_message(message, current_state) do
case validate_message(message) do
:ok ->
handle_valid_conflict(message, current_state)
{:error, reason} ->
{:error, reason}
end
end
def handle_resolve_message(message, current_state) do
case validate_message(message) do
:ok ->
handle_valid_resolve(message, current_state)
{:error, reason} ->
{:error, reason}
end
end
# Private Functions
defp validate_message(message) when not is_map(message) do
{:error, :invalid_message}
end
defp validate_message(message) do
with true <- Map.has_key?(message, :type) || {:error, :missing_type},
true <-
Map.has_key?(message, :component_id) ||
{:error, :missing_component_id},
true <-
Map.has_key?(message, :component_type) ||
{:error, :missing_component_type},
true <- Map.has_key?(message, :metadata) || {:error, :missing_metadata} do
:ok
else
{:error, reason} -> {:error, reason}
false -> {:error, :unknown_validation_error}
end
end
defp handle_valid_sync(message, current_state) do
case resolve_conflict(message, current_state) do
:accept ->
{:ok, message.state, message.metadata.version}
:reject ->
{:error, :version_mismatch}
:conflict ->
{:error, :conflict}
end
end
defp handle_valid_ack(message, current_state) do
handle_ack_version_check(message.metadata.version == current_state.metadata.version)
end
defp handle_valid_conflict(message, current_state) do
# Compare incoming state with current state
case resolve_conflict(message.states.incoming, current_state) do
:accept ->
{:ok, message.states.incoming}
:reject ->
{:ok, current_state}
:conflict ->
{:error, :unresolved_conflict}
end
end
defp handle_valid_resolve(message, current_state) do
handle_resolve_version_check(
message.metadata.version > current_state.metadata.version,
message.state
)
end
defp resolve_conflict(message, current_state) do
# Extract metadata from both states, handling different formats
message_metadata = extract_metadata(message)
current_metadata = extract_metadata(current_state)
case {message_metadata.consistency, current_metadata.consistency} do
{:strong, :strong} ->
resolve_strong_consistency(message_metadata.version > current_metadata.version)
{:strong, _} ->
:accept
{_, :strong} ->
:reject
_ ->
# Both eventual consistency
resolve_eventual_consistency(
message_metadata.version,
current_metadata.version
)
end
end
defp extract_metadata(%{metadata: meta}) do
%{
version: Map.get(meta, :version, 0),
consistency: Map.get(meta, :consistency, :eventual)
}
end
defp extract_metadata(%{version: version} = state) do
%{
version: version,
consistency: Map.get(state, :consistency, :eventual)
}
end
defp extract_metadata(_state) do
%{
version: 0,
consistency: :eventual
}
end
defp get_default_consistency(:split), do: :strong
defp get_default_consistency(:window), do: :strong
defp get_default_consistency(:tab), do: :eventual
defp get_default_consistency(_), do: :eventual
# Helper functions for pattern matching refactoring
defp convert_opts_to_map(true, opts), do: Map.new(opts)
defp convert_opts_to_map(false, opts), do: opts
defp handle_ack_version_check(true), do: :ok
defp handle_ack_version_check(false), do: {:error, :version_mismatch}
defp handle_resolve_version_check(true, state), do: {:ok, state}
defp handle_resolve_version_check(false, _state),
do: {:error, :version_mismatch}
defp resolve_strong_consistency(true), do: :accept
defp resolve_strong_consistency(false), do: :reject
defp resolve_eventual_consistency(message_version, current_version)
when message_version > current_version do
:accept
end
defp resolve_eventual_consistency(message_version, current_version)
when message_version == current_version do
:conflict
end
defp resolve_eventual_consistency(_message_version, _current_version) do
:reject
end
end