Current section
Files
Jump to
Current section
Files
src/glixir.gleam
//// glixir - Seamless OTP interop between Gleam and Elixir/Erlang
////
//// This module provides a unified API for working with OTP processes from Gleam.
//// Import this module to get access to Supervisors, GenServers, and Agents.
import gleam/dynamic.{type Dynamic}
import gleam/dynamic/decode.{type Decoder}
import gleam/erlang/atom
import gleam/erlang/process.{type Pid}
import gleam/int
import gleam/io
import gleam/list
import gleam/string
import glixir/agent
import glixir/genserver
import glixir/supervisor
// Re-export main types
pub type GenServer =
genserver.GenServer
pub type Agent =
agent.Agent
pub type Supervisor =
supervisor.Supervisor
pub type SimpleChildSpec =
supervisor.SimpleChildSpec
pub type RestartStrategy =
supervisor.RestartStrategy
pub type ChildType =
supervisor.ChildType
// Re-export constructor values for convenience
pub const permanent = supervisor.Permanent
pub const temporary = supervisor.Temporary
pub const transient = supervisor.Transient
pub const worker = supervisor.Worker
pub const supervisor_child = supervisor.SupervisorChild
// Re-export error types
pub type GenServerError =
genserver.GenServerError
pub type AgentError =
agent.AgentError
pub type SupervisorError =
supervisor.SupervisorError
//
// SUPERVISOR FUNCTIONS
//
/// Start a supervisor with default options
pub fn start_supervisor() -> Result(Supervisor, SupervisorError) {
supervisor.start_link_default()
}
/// Start a simple supervisor with defaults
pub fn start_supervisor_simple() -> Result(Supervisor, SupervisorError) {
io.println("DEBUG: start_supervisor_simple called")
let result = supervisor.start_dynamic_supervisor_simple()
case result {
Ok(sup) -> {
io.println("DEBUG: simple supervisor started successfully")
Ok(sup)
}
Error(error) -> {
io.println(
"DEBUG: simple supervisor start failed with error: "
<> string.inspect(error),
)
Error(error)
}
}
}
/// Start a named supervisor using DynamicSupervisor
pub fn start_supervisor_named(
name: String,
_additional_options: List(#(String, Dynamic)),
) -> Result(Supervisor, SupervisorError) {
io.println("DEBUG: start_supervisor_named called with name: " <> name)
let result = supervisor.start_dynamic_supervisor_named(name)
case result {
Ok(sup) -> {
io.println("DEBUG: supervisor started successfully")
Ok(sup)
}
Error(error) -> {
io.println(
"DEBUG: supervisor start failed with error: " <> string.inspect(error),
)
Error(error)
}
}
}
/// Create a child specification
pub fn child_spec(
id id: String,
module module: String,
function function: String,
args args: List(Dynamic),
) -> SimpleChildSpec {
supervisor.child_spec(id, module, function, args)
}
/// Start a child process in the supervisor
pub fn start_child(
supervisor: Supervisor,
spec: SimpleChildSpec,
) -> Result(Pid, String) {
io.println("DEBUG: start_child called with spec id: " <> spec.id)
let result = supervisor.start_child_simple(supervisor, spec)
case result {
Ok(pid) -> {
io.println("DEBUG: child started successfully with PID")
Ok(pid)
}
Error(error) -> {
io.println("DEBUG: child start failed with error: " <> error)
Error(error)
}
}
}
/// Terminate a child process
pub fn terminate_child(
supervisor: Supervisor,
child_id: String,
) -> Result(Nil, supervisor.ChildOperationError) {
case supervisor.terminate_child(supervisor, child_id) {
supervisor.TerminateChildOk -> Ok(Nil)
supervisor.TerminateChildError(error) -> Error(error)
}
}
/// Restart a child process
pub fn restart_child(
supervisor: Supervisor,
child_id: String,
) -> Result(Pid, supervisor.ChildOperationError) {
case supervisor.restart_child(supervisor, child_id) {
supervisor.RestartChildOk(pid) -> Ok(pid)
supervisor.RestartChildOkAlreadyStarted(pid) -> Ok(pid)
supervisor.RestartChildError(error) -> Error(error)
}
}
/// Delete a child specification from the supervisor
pub fn delete_child(
supervisor: Supervisor,
child_id: String,
) -> Result(Nil, supervisor.ChildOperationError) {
case supervisor.delete_child(supervisor, child_id) {
supervisor.DeleteChildOk -> Ok(Nil)
supervisor.DeleteChildError(error) -> Error(error)
}
}
/// Get list of child processes
pub fn which_children(
supervisor: Supervisor,
) -> List(supervisor.ChildInfoResult) {
supervisor.which_children(supervisor)
}
/// Count children by status
pub fn count_children(supervisor: Supervisor) -> supervisor.ChildCounts {
supervisor.count_children(supervisor)
}
//
// GENSERVER FUNCTIONS
//
/// Start a GenServer using Module.start_link/1
pub fn start_genserver(
module: String,
args: a,
) -> Result(GenServer, GenServerError) {
genserver.start_link(module, args)
}
/// Start a named GenServer
pub fn start_genserver_named(
module: String,
name: String,
args: a,
) -> Result(GenServer, GenServerError) {
genserver.start_link_named(module, name, args)
}
/// Send a synchronous call to the GenServer (5s timeout)
pub fn call_genserver(
server: GenServer,
request: a,
) -> Result(b, GenServerError) {
genserver.call(server, request)
}
/// Send a synchronous call with custom timeout
pub fn call_genserver_timeout(
server: GenServer,
request: a,
timeout: Int,
) -> Result(b, GenServerError) {
genserver.call_timeout(server, request, timeout)
}
/// Call a named GenServer
pub fn call_genserver_named(
name: String,
request: a,
) -> Result(b, GenServerError) {
genserver.call_named(name, request)
}
/// Send an asynchronous cast to the GenServer
pub fn cast_genserver(
server: GenServer,
request: a,
) -> Result(Nil, GenServerError) {
genserver.cast(server, request)
}
/// Cast to a named GenServer
pub fn cast_genserver_named(
name: String,
request: a,
) -> Result(Nil, GenServerError) {
genserver.cast_named(name, request)
}
/// Look up a GenServer by registered name
pub fn lookup_genserver(name: String) -> Result(GenServer, GenServerError) {
genserver.lookup_name(name)
}
/// Stop a GenServer gracefully
pub fn stop_genserver(server: GenServer) -> Result(Nil, GenServerError) {
genserver.stop(server)
}
/// Get the PID of a GenServer
pub fn genserver_pid(server: GenServer) -> Pid {
genserver.pid(server)
}
//
// AGENT FUNCTIONS
//
/// Start a new Agent with initial state
pub fn start_agent(initial_fun: fn() -> a) -> Result(Agent, AgentError) {
agent.start(initial_fun)
}
/// Start an Agent with a name
pub fn start_agent_named(
name: String,
initial_fun: fn() -> a,
) -> Result(Agent, AgentError) {
agent.start_named(name, initial_fun)
}
/// Get the current state from an Agent
pub fn get_agent(
agent: Agent,
fun: fn(a) -> b,
decoder: Decoder(b),
) -> Result(b, AgentError) {
agent.get(agent, fun, decoder)
}
/// Get with custom timeout
pub fn get_agent_timeout(
agent: Agent,
fun: fn(a) -> b,
timeout: Int,
decoder: Decoder(b),
) -> Result(b, AgentError) {
agent.get_timeout(agent, fun, timeout, decoder)
}
/// Update the Agent state synchronously
pub fn update_agent(agent: Agent, fun: fn(a) -> a) -> Result(Nil, AgentError) {
agent.update(agent, fun)
}
/// Update the Agent state asynchronously
pub fn cast_agent(agent: Agent, fun: fn(a) -> a) -> Nil {
agent.cast(agent, fun)
}
/// Get and update in one operation
pub fn get_and_update_agent(
agent: Agent,
fun: fn(a) -> #(b, a),
decoder: Decoder(b),
) -> Result(b, AgentError) {
agent.get_and_update(agent, fun, decoder)
}
/// Stop an Agent
pub fn stop_agent(agent: Agent) -> Result(Nil, AgentError) {
agent.stop(agent)
}
/// Get the PID of an Agent
pub fn agent_pid(agent: Agent) -> Pid {
agent.pid(agent)
}
//
// CONVENIENCE FUNCTIONS
//
/// Create a simple worker child spec with defaults
pub fn worker_spec(
id: String,
module: String,
args: List(Dynamic),
) -> SimpleChildSpec {
supervisor.SimpleChildSpec(
id: id,
start_module: atom.create(module),
start_function: atom.create("start_link"),
start_args: args,
restart: permanent,
child_type: worker,
shutdown_timeout: 5000,
)
}
/// Create a supervisor child spec
pub fn supervisor_spec(
id: String,
module: String,
args: List(Dynamic),
) -> SimpleChildSpec {
supervisor.SimpleChildSpec(
id: id,
start_module: atom.create(module),
start_function: atom.create("start_link"),
start_args: args,
restart: permanent,
child_type: supervisor_child,
shutdown_timeout: 5000,
)
}