Skip to main content

vaultdb_core/
error.rs

1//! Error types: [`VaultdbError`] for fatal failures, [`ParseError`] for
2//! non-fatal per-file diagnostics returned by [`crate::LoadResult`].
3
4use std::path::PathBuf;
5use thiserror::Error;
6
7/// A non-fatal parse failure for a single file.
8///
9/// Returned in `LoadResult::parse_errors` when `Vault::load_records` encounters
10/// a file with malformed frontmatter. The application layer decides whether to
11/// surface, log, or ignore these.
12#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
13pub struct ParseError {
14    pub file: PathBuf,
15    pub message: String,
16}
17
18#[derive(Error, Debug)]
19#[non_exhaustive]
20pub enum VaultdbError {
21    #[error("vault not found: no .obsidian/ directory in {0} or any parent")]
22    VaultNotFound(String),
23
24    #[error("folder not found: {0}")]
25    FolderNotFound(String),
26
27    #[error("no frontmatter in file: {0}")]
28    NoFrontmatter(String),
29
30    #[error("invalid frontmatter YAML in {file}: {reason}")]
31    InvalidFrontmatter { file: String, reason: String },
32
33    #[error("invalid where expression: {0}")]
34    InvalidWhereExpr(String),
35
36    #[error("type mismatch: field '{field}' is {actual}, cannot compare as {expected}")]
37    TypeMismatch {
38        field: String,
39        actual: String,
40        expected: String,
41    },
42
43    #[error("regex error in pattern '{pattern}': {reason}")]
44    RegexError { pattern: String, reason: String },
45
46    #[error("schema error: {0}")]
47    SchemaError(String),
48
49    #[error("operation refused: {reason}")]
50    SafetyRefused { reason: String },
51
52    /// Vaultdb's own internal invariant was violated (e.g. a journal file
53    /// failed to round-trip through serde, a lock sentinel couldn't be
54    /// created because of an unexpected filesystem state). These are bugs
55    /// in vaultdb-core or unrecoverable filesystem situations, not user
56    /// errors.
57    #[error("internal error: {0}")]
58    Internal(String),
59
60    #[error(transparent)]
61    Io(#[from] std::io::Error),
62}
63
64pub type Result<T> = std::result::Result<T, VaultdbError>;
65
66#[cfg(test)]
67mod parse_error_tests {
68    use super::ParseError;
69    use std::path::PathBuf;
70
71    #[test]
72    fn parse_error_serializes() {
73        let err = ParseError {
74            file: PathBuf::from("foo.md"),
75            message: "bad yaml".into(),
76        };
77        let json = serde_json::to_string(&err).unwrap();
78        assert!(json.contains("foo.md"));
79        assert!(json.contains("bad yaml"));
80    }
81
82    #[test]
83    fn parse_error_round_trips() {
84        let err = ParseError {
85            file: PathBuf::from("notes/x.md"),
86            message: "oops".into(),
87        };
88        let json = serde_json::to_string(&err).unwrap();
89        let back: ParseError = serde_json::from_str(&json).unwrap();
90        assert_eq!(back.file, err.file);
91        assert_eq!(back.message, err.message);
92    }
93}