Skip to main content

meshdb_storage/
error.rs

1use meshdb_core::{EdgeId, NodeId};
2use thiserror::Error;
3
4#[derive(Debug, Error)]
5#[non_exhaustive]
6pub enum Error {
7    #[error("rocksdb: {0}")]
8    RocksDb(#[from] rocksdb::Error),
9
10    #[error("serialization: {0}")]
11    Serde(#[from] serde_json::Error),
12
13    #[error("core: {0}")]
14    Core(#[from] meshdb_core::Error),
15
16    #[error("missing column family: {0}")]
17    MissingColumnFamily(&'static str),
18
19    #[error("corrupt bytes in {cf}: expected {expected}, got {actual}")]
20    CorruptBytes {
21        cf: &'static str,
22        expected: usize,
23        actual: usize,
24    },
25
26    #[error("node not found: {0}")]
27    NodeNotFound(NodeId),
28
29    #[error("edge not found: {0}")]
30    EdgeNotFound(EdgeId),
31
32    #[error("property {property} of type {kind} is not indexable")]
33    UnindexableValue {
34        property: String,
35        kind: &'static str,
36    },
37
38    /// Two constraint declarations collided on the same name with
39    /// incompatible specs. Raised by `create_property_constraint` when
40    /// `IF NOT EXISTS` is absent and the name is already taken by a
41    /// different `(label, property, kind)`. The resolver is "name wins"
42    /// so the caller can't transparently re-declare under a different
43    /// shape — they have to DROP first.
44    #[error(
45        "a constraint named `{name}` already exists with a different definition; \
46         drop it before re-declaring"
47    )]
48    ConstraintNameConflict { name: String },
49
50    /// `DROP CONSTRAINT` targeted a name that isn't registered and the
51    /// `IF EXISTS` escape wasn't supplied. Callers wrap this for
52    /// user-facing surfaces.
53    #[error("no constraint named `{name}`")]
54    ConstraintNotFound { name: String },
55
56    /// Property-arity mismatch: the caller passed a property list
57    /// whose length is wrong for the constraint kind — e.g. two
58    /// properties to a `UNIQUE` (which accepts exactly one) or an
59    /// empty list to any kind. Surfaced so the Cypher surface can
60    /// give a clear error instead of silently clipping the list.
61    #[error("invalid property arity for {kind}: {details}")]
62    ConstraintArity { kind: String, details: String },
63
64    /// A write would put the store into a state that violates a
65    /// registered constraint. The `kind` field carries the constraint
66    /// type (e.g. `UNIQUE`, `NOT NULL`, `IS :: STRING`) so callers can
67    /// format a clear message. `kind` is `String` rather than
68    /// `&'static str` because `PropertyConstraintKind::PropertyType`
69    /// carries a runtime-selected type name.
70    #[error("constraint `{name}` violated: {kind} on {label}.{property} {details}")]
71    ConstraintViolation {
72        name: String,
73        kind: String,
74        label: String,
75        property: String,
76        details: String,
77    },
78
79    /// `DROP INDEX` targeted a property index that backs an active
80    /// `UNIQUE` or `NODE KEY` constraint. Without this guard, dropping
81    /// the backing index silently defeats the constraint — the
82    /// enforcement path seeks through the same index, so a missing
83    /// index collapses the uniqueness check to "always passes". The
84    /// caller must `DROP CONSTRAINT` first.
85    #[error(
86        "cannot drop property index: it backs active constraint `{constraint}`; \
87         drop the constraint first"
88    )]
89    IndexInUse { constraint: String },
90
91    /// A storage operation that the backing engine doesn't
92    /// support reached the trait. Lets default trait impls (e.g.
93    /// the trigger-registry methods) return a clear error
94    /// without forcing every backend to implement every
95    /// optional surface.
96    #[error("operation not supported by this backend: {0}")]
97    Unsupported(String),
98}
99
100pub type Result<T> = std::result::Result<T, Error>;