Current section

Files

Jump to
chip src chip.gleam
Raw

src/chip.gleam

import gleam/io
import gleam/map.{Map}
import gleam/erlang/process.{ProcessDown, ProcessMonitor, Subject}
import gleam/otp/actor.{StartError}
pub fn main() {
io.println("Hello from chip!")
}
pub opaque type Message(name, message) {
Register(name: name, subject: Subject(message))
Unregister(name: name)
Find(client: Subject(Result(Subject(message), Errors)), name: name)
}
pub opaque type Record(message) {
Record(subject: Subject(message), monitor: ProcessMonitor)
}
pub type Errors {
// TODO: NameTaken
NotFound
}
pub fn start() -> Result(Subject(Message(name, message)), StartError) {
actor.start(map.new(), handle_message)
}
pub fn register(registry, name: name, subject: Subject(message)) -> Nil {
process.send(registry, Register(name, subject))
}
pub fn unregister(registry, name: name) -> Nil {
process.send(registry, Unregister(name))
}
pub fn find(registry, name: name) -> Result(Subject(message), Errors) {
process.call(registry, fn(self) { Find(self, name) }, 100)
}
fn handle_message(
message: Message(name, subject_message),
state: Map(name, Record(subject_message)),
) {
case message {
Register(name, subject) -> {
// Start monitoring the pid within this process
let monitor =
subject
|> process.subject_owner()
|> process.monitor_process()
// TODO: temporarily stored internally here.
// Eventually dispatch to a store (GenServer, ets, DB)
let state = map.insert(state, name, Record(subject, monitor))
// When a process down message is received map it to an unregister message
let handle_down = fn(_down: ProcessDown) { Unregister(name) }
let handle_process_down =
process.new_selector()
|> process.selecting_process_down(monitor, handle_down)
// Continue with handle down selector
actor.continue(state)
|> actor.with_selector(handle_process_down)
}
Unregister(name) -> {
case map.get(state, name) {
Ok(Record(_subject, monitor)) -> {
process.demonitor_process(monitor)
let state = map.delete(state, name)
actor.continue(state)
}
Error(Nil) -> {
actor.continue(state)
}
}
}
Find(client, name) -> {
case map.get(state, name) {
Ok(Record(subject, _monitor)) -> {
process.send(client, Ok(subject))
actor.continue(state)
}
Error(Nil) -> {
process.send(client, Error(NotFound))
actor.continue(state)
}
}
}
}
}