Skip to main content

nap_core/
error.rs

1//! Error types for the Narrative Addressing Protocol.
2//!
3//! All NAP errors are surfaced through [`NapError`], which captures the exact
4//! failure domain (URI parsing, manifest I/O, VCS operations, resolution, etc.)
5//! with enough context for callers to produce actionable diagnostics.
6
7use thiserror::Error;
8
9/// Top-level error type for all NAP operations.
10#[derive(Error, Debug)]
11pub enum NapError {
12    // ── URI Errors ──────────────────────────────────────────────────────
13    #[error("invalid NAP URI '{uri}': {reason}")]
14    InvalidUri { uri: String, reason: String },
15
16    // ── Manifest Errors ─────────────────────────────────────────────────
17    #[error("manifest not found: {0}")]
18    ManifestNotFound(String),
19
20    #[error("manifest parse error for '{path}': {source}")]
21    ManifestParseError {
22        path: String,
23        source: serde_yaml::Error,
24    },
25
26    #[error("manifest validation error: {0}")]
27    ManifestValidationError(String),
28
29    #[error("manifest write error for '{path}': {source}")]
30    ManifestWriteError {
31        path: String,
32        source: std::io::Error,
33    },
34
35    // ── Query Errors ────────────────────────────────────────────────────
36    #[error("query path not found: '{path}' in manifest '{manifest_id}'")]
37    QueryPathNotFound { path: String, manifest_id: String },
38
39    #[error("invalid query path: '{0}'")]
40    InvalidQueryPath(String),
41
42    // ── Repository Errors ───────────────────────────────────────────────
43    #[error("repository not found at '{0}'")]
44    RepositoryNotFound(String),
45
46    #[error(
47        "operation requires a local NAP working tree for '{repository}'; use 'nap pull {repository}' first"
48    )]
49    LocalWorkingTreeRequired { repository: String },
50
51    #[error("repository already exists at '{0}'")]
52    RepositoryAlreadyExists(String),
53
54    // ── Entity Type Errors ─────────────────────────────────────────────
55    #[error("entity type not found: '{0}'")]
56    EntityTypeNotFound(String),
57
58    // ── VCS Errors ──────────────────────────────────────────────────────
59    #[error("VCS error: {0}")]
60    VcsError(String),
61
62    #[error("ref not found: '{0}'")]
63    RefNotFound(String),
64
65    // ── Version-control backend errors ──────────────────────────────────
66    #[error(
67        "cannot {operation}: no version-control backend is configured. \
68         Filesystem state is preserved, but history/sync is unavailable. \
69         Configure one with 'nap backend configure'."
70    )]
71    BackendNotConfigured { operation: String },
72
73    #[error("version-control backend is unavailable: {message}")]
74    BackendUnavailable { message: String },
75
76    #[error("version-control backend operation failed ({operation}): {message}")]
77    BackendOperationFailed { operation: String, message: String },
78
79    // ── Content Addressing Errors ───────────────────────────────────────
80    #[error("content hash mismatch: expected {expected}, got {actual}")]
81    ContentHashMismatch { expected: String, actual: String },
82
83    // ── I/O ─────────────────────────────────────────────────────────────
84    #[error("I/O error: {0}")]
85    Io(#[from] std::io::Error),
86
87    // ── Merge Errors ────────────────────────────────────────────────────
88    #[error("merge conflict at '{path}': {details}")]
89    MergeConflict { path: String, details: String },
90
91    #[error("SDL parse error: {0}")]
92    SdlParseError(String),
93
94    #[error("SDL validation error: {reason}")]
95    SdlValidationError { reason: String },
96
97    #[error("merge strategy error at '{path}': {reason}")]
98    MergeStrategyError { path: String, reason: String },
99
100    #[error("merge validation error: {reason}")]
101    MergeValidationError { reason: String },
102
103    // ── Resolution Errors ───────────────────────────────────────────────
104    #[error("no branch or commit specified and no default_branch configured. ")]
105    NoDefaultBranch,
106
107    #[error("unable to resolve resource '{address}': {message}")]
108    ResolutionFailed { address: String, message: String },
109
110    // ── Permission ──────────────────────────────────────────────────────
111    #[error("permission denied: {0}")]
112    PermissionDenied(String),
113
114    // ── gRPC ─────────────────────────────────────────────────────────────
115    #[error("gRPC error: {0}")]
116    GrpcError(String),
117
118    // ── Catch-all ───────────────────────────────────────────────────────
119    #[error("{0}")]
120    Other(String),
121}
122
123/// Result type alias using [`NapError`].
124pub type NapResult<T> = Result<T, NapError>;