prov_graph/error.rs
1//! Error and result types.
2
3use std::path::PathBuf;
4
5use thiserror::Error;
6
7/// Errors produced by prov.
8#[derive(Debug, Error)]
9pub enum Error {
10 /// The embedded-metadata backend (`fig`) failed to parse or serialize.
11 #[error("metadata error: {0}")]
12 Meta(#[from] fig::Error),
13
14 /// A structural invariant was violated (e.g. malformed frontmatter fence).
15 #[error("{0}")]
16 Structure(String),
17
18 /// A document a workspace operation names is not on disk — the typed form of
19 /// the many "X does not exist" guards the mutation ops make before touching a
20 /// document (`reparent`, `rename`, `duplicate`, `register`, …). A caller can
21 /// tell a genuinely-missing target from a malformed one by matching the
22 /// variant, rather than sniffing the message text.
23 #[error("{0} does not exist")]
24 NotFound(PathBuf),
25
26 /// A workspace operation would create a document where one already exists, and
27 /// refused rather than overwrite it — the typed form of the "X already exists"
28 /// guards in `create`/`rename`/`attach`. A destination collision is a distinct
29 /// outcome from a missing source, and now distinguishable as one.
30 #[error("{0} already exists")]
31 AlreadyExists(PathBuf),
32
33 /// The storage backend failed.
34 #[error("io error: {0}")]
35 Io(#[from] std::io::Error),
36
37 /// The `twig` body parser failed — see `content.rs`.
38 #[error("content error: {0}")]
39 Content(String),
40
41 /// A record store — the id registry, the recycle-bin index, or a flat
42 /// vocabulary — was found in a **markdown** carrier (fenced frontmatter)
43 /// rather than a whole-file config document (`.yaml`/`.json`/`.figl`). prov
44 /// imposes a sorted, one-record-per-line layout on these stores (DESIGN §5),
45 /// so a prose carrier has no stable home for its records and is refused. Make
46 /// it a bare config file. See [`crate::document::require_whole_file`].
47 #[error(
48 "record store must be a whole-file config document (.yaml/.json/.figl), \
49 not markdown frontmatter: {0}"
50 )]
51 MarkdownStore(PathBuf),
52
53 /// A path handed to a workspace read or write resolved *outside* the
54 /// workspace root — an absolute path, or one that climbs above the root with
55 /// `..`. prov clamps every I/O to the tree it was pointed at (a link
56 /// target is data, and data must never be able to name `/etc/passwd` or a
57 /// sibling repo), so such a path is refused rather than followed. See
58 /// [`crate::link::escapes_root`], the guard at `prov`'s `Workspace`'s `load`
59 /// and `prov`'s `ChangeSet::apply`.
60 #[error("path escapes the workspace root: {0}")]
61 Escape(PathBuf),
62
63 /// A `prov`'s `ChangeSet` was applied while a previous change's
64 /// write-ahead journal was still on disk — an earlier mutation was
65 /// interrupted (a crash) and never recovered. Landing this set would
66 /// overwrite that journal and lose the record needed to complete the
67 /// interrupted change, so the apply refuses: run recovery
68 /// (`prov`'s `journal::recover`, which `prov check` performs) first, then
69 /// retry.
70 #[error(
71 "a previous change was interrupted and not yet recovered (found {0}); \
72 recover it first (run `prov check`), then retry"
73 )]
74 StaleJournal(PathBuf),
75
76 /// A staged write failed *and* the rollback that should have undone it also
77 /// failed — see `prov`'s `ChangeSet::apply`. The one case where
78 /// prov cannot say what is on disk, so it says exactly that instead of
79 /// reporting the original failure as if the workspace were untouched.
80 #[error(
81 "{cause}; and rolling back failed too: {rollback}. \
82 The workspace may be partially written — run `prov check`."
83 )]
84 Torn {
85 /// The failure that triggered the rollback.
86 cause: String,
87 /// The failure the rollback itself hit.
88 rollback: String,
89 },
90
91 /// An operation would have registered an ID across a registration the index
92 /// already holds — see [`Collision`](crate::index::Collision). Refused rather
93 /// than resolved, because the displaced document still spells the ID in its
94 /// own frontmatter and only its author can say which one should keep it.
95 #[error("{0}; refusing to displace it")]
96 Collision(crate::index::Collision),
97}
98
99impl From<crate::index::Collision> for Error {
100 fn from(collision: crate::index::Collision) -> Self {
101 Error::Collision(collision)
102 }
103}
104
105/// Convenience alias for results in this crate.
106pub type Result<T> = std::result::Result<T, Error>;