Validate config on load; document the SDK's core traits
Some checks failed
CI / check (push) Failing after 53s

Config::load now checks the SID is 3 alphanumeric chars and that
server.name and uplink.host are set, failing with a clear message
instead of a cryptic link-time error. Give the Service and Protocol
traits and ServiceCtx proper rustdoc so cargo doc is useful to module
authors.
This commit is contained in:
Jean Chevronnet 2026-07-14 23:31:44 +00:00
parent 955e55d47f
commit 0ed6684522
No known key found for this signature in database
2 changed files with 29 additions and 6 deletions

View file

@ -121,9 +121,9 @@ pub enum RegReply {
NickServ { agent: String, uid: String, nick: String }, NickServ { agent: String, uid: String, nick: String },
} }
// The ircd link layer. The engine only ever sees NetEvent / NetAction; raw /// The ircd link layer. The engine only ever sees [`NetEvent`] / [`NetAction`];
// server-to-server lines live entirely behind a Protocol impl, so a new ircd is /// raw server-to-server lines live entirely behind a `Protocol` impl, so a new
// one new module and the engine is untouched. /// ircd is one new module and the engine is untouched.
pub trait Protocol: Send { pub trait Protocol: Send {
/// Lines to send immediately on connect (auth / capability negotiation). /// Lines to send immediately on connect (auth / capability negotiation).
fn handshake(&mut self) -> Vec<String>; fn handshake(&mut self) -> Vec<String>;
@ -226,8 +226,9 @@ pub struct Sender<'a> {
pub privs: Privs, pub privs: Privs,
} }
// The intent sink a service writes to. A service never mutates the network or /// The intent sink a service writes to. A service never mutates the network or
// the store itself; it pushes normalized actions the engine drains and applies. /// the store itself; it pushes normalized actions (via [`ServiceCtx::notice`]
/// and friends) that the engine drains and applies.
#[derive(Default)] #[derive(Default)]
pub struct ServiceCtx { pub struct ServiceCtx {
pub actions: Vec<NetAction>, pub actions: Vec<NetAction>,
@ -1075,6 +1076,10 @@ pub struct IncidentView {
// A pseudo-client (NickServ, ChanServ, ...). Introduced at burst, receives the // A pseudo-client (NickServ, ChanServ, ...). Introduced at burst, receives the
// commands users message it, reads/writes the store, and pushes actions. // commands users message it, reads/writes the store, and pushes actions.
/// A pseudo-client such as NickServ or ChanServ. Implement this, register the
/// crate in the daemon, and the engine routes a PRIVMSG addressed to the
/// service into [`Service::on_command`]. A service touches the network and the
/// store only through [`NetView`] and [`Store`].
pub trait Service: Send { pub trait Service: Send {
fn nick(&self) -> &str; fn nick(&self) -> &str;
fn uid(&self) -> &str; fn uid(&self) -> &str;

View file

@ -278,6 +278,24 @@ fn default_scram_iterations() -> u32 {
impl Config { impl Config {
pub fn load(path: &str) -> anyhow::Result<Self> { pub fn load(path: &str) -> anyhow::Result<Self> {
let raw = std::fs::read_to_string(path)?; let raw = std::fs::read_to_string(path)?;
Ok(toml::from_str(&raw)?) let cfg: Config = toml::from_str(&raw)?;
cfg.validate()?;
Ok(cfg)
}
// Catch the common misconfigurations up front with a clear message, rather
// than failing cryptically at link time.
fn validate(&self) -> anyhow::Result<()> {
let sid = &self.server.sid;
if sid.len() != 3 || !sid.chars().all(|c| c.is_ascii_alphanumeric()) {
anyhow::bail!("server.sid must be exactly 3 alphanumeric characters (got {sid:?})");
}
if self.server.name.trim().is_empty() {
anyhow::bail!("server.name must not be empty");
}
if self.uplink.host.trim().is_empty() {
anyhow::bail!("uplink.host must not be empty");
}
Ok(())
} }
} }