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>;