Skip to main content

cairn_mod/
error.rs

1//! Crate-wide error type.
2
3use thiserror::Error;
4
5/// Top-level error type for the `cairn-mod` library.
6#[derive(Error, Debug)]
7#[non_exhaustive]
8pub enum Error {
9    /// Configuration loading or validation failed.
10    #[error("config error: {0}")]
11    Config(String),
12
13    /// I/O failure (filesystem, stdio, etc.).
14    #[error(transparent)]
15    Io(#[from] std::io::Error),
16
17    /// Figment config-source failure (TOML parse, env-var extraction).
18    #[error(transparent)]
19    Figment(Box<figment::Error>),
20
21    /// SQLx query or connection failure.
22    #[error(transparent)]
23    Sqlx(#[from] sqlx::Error),
24
25    /// Embedded migration runner failed.
26    #[error(transparent)]
27    Migrate(#[from] sqlx::migrate::MigrateError),
28
29    /// DAG-CBOR encode / decode failure from `proto-blue-lex-cbor`.
30    #[error(transparent)]
31    Cbor(#[from] proto_blue_lex_cbor::CborError),
32
33    /// Cryptographic primitive failure (signing, hashing, key parse).
34    #[error(transparent)]
35    Crypto(#[from] proto_blue_crypto::CryptoError),
36
37    /// Invariant violation specific to label signing (e.g. missing `sig`,
38    /// wrong algorithm in the multibase key, sig-length mismatch).
39    #[error("signing: {0}")]
40    Signing(String),
41
42    /// Another Cairn instance holds a fresh lease on the same SQLite file
43    /// (§F5 single-instance invariant). Refusing to start — the other
44    /// instance must shut down or its lease must age past 60s.
45    #[error(
46        "server_instance_lease held by another instance (instance_id={instance_id}, last heartbeat {age_secs}s ago)"
47    )]
48    LeaseHeld {
49        /// Instance identifier of the rival process holding the lease.
50        instance_id: String,
51        /// Seconds since that instance last heartbeated.
52        age_secs: u64,
53    },
54
55    /// Negating a label that does not currently apply. Either the tuple
56    /// `(src, uri, val)` has no apply events, or the most recent event for
57    /// the tuple is already a negation (§F6).
58    #[error("no applied label for ({src}, {uri}, {val}) — nothing to negate")]
59    LabelNotFound {
60        /// Labeler DID (Cairn's own service DID for emissions).
61        src: String,
62        /// Subject AT-URI or DID.
63        uri: String,
64        /// Label value.
65        val: String,
66    },
67
68    /// `resolveReport` (§F12) targeted a report id that doesn't exist.
69    /// Surface via the handler as `ReportNotFound` (declared lexicon error).
70    #[error("report not found: id={id}")]
71    ReportNotFound {
72        /// Report primary key that didn't resolve.
73        id: i64,
74    },
75
76    /// `resolveReport` (§F12) targeted a report that is already resolved.
77    /// Surface via the handler as `InvalidRequest` with a generic message
78    /// (no timestamps or resolver DID per the anti-leak principle).
79    #[error("report already resolved: id={id}")]
80    ReportAlreadyResolved {
81        /// Report primary key that was already resolved.
82        id: i64,
83    },
84}
85
86impl From<figment::Error> for Error {
87    fn from(e: figment::Error) -> Self {
88        Self::Figment(Box::new(e))
89    }
90}
91
92/// Crate-wide `Result` alias using [`enum@Error`] as the error type.
93pub type Result<T> = std::result::Result<T, Error>;