Skip to main content

newgit_core/
error.rs

1use camino::Utf8PathBuf;
2use thiserror::Error;
3
4pub type Result<T> = std::result::Result<T, NewgitError>;
5
6#[derive(Debug, Error)]
7pub enum NewgitError {
8    #[error("I/O error at {path}: {source}")]
9    Io {
10        path: Utf8PathBuf,
11        source: std::io::Error,
12    },
13
14    #[error("could not parse TOML at {path}: {source}")]
15    TomlRead {
16        path: Utf8PathBuf,
17        source: toml::de::Error,
18    },
19
20    #[error("could not serialize TOML for {label}: {source}")]
21    TomlWrite {
22        label: String,
23        source: toml::ser::Error,
24    },
25
26    #[error(
27        "newgit is not initialized here (no .newgit found from {0} upward); run `newgit init` in the project root"
28    )]
29    MissingMetadata(Utf8PathBuf),
30
31    #[error("already exists at {0}")]
32    AlreadyExists(Utf8PathBuf),
33
34    #[error(
35        "invalid name `{0}`; use letters, numbers, dots, underscores, hyphens, or slashes, starting with a letter or number"
36    )]
37    InvalidName(String),
38
39    #[error(
40        "branch instance `{name}` collides with existing record {path}; instance names must be unique after slugging"
41    )]
42    BranchInstanceExists { name: String, path: Utf8PathBuf },
43
44    #[error("no branch instance named `{0}`")]
45    UnknownBranchInstance(String),
46
47    #[error("workspace directory already exists at {0}; remove it or pick another name")]
48    WorkspaceExists(Utf8PathBuf),
49
50    #[error("{0} is not a Git repository; newgit v1 drives source through Git")]
51    NotAGitRepo(Utf8PathBuf),
52
53    #[error("`{command}` failed: {stderr}")]
54    SourceCommand { command: String, stderr: String },
55
56    #[error("tracker `{tracker}` is invalid: {reason}")]
57    InvalidDefinition { tracker: String, reason: String },
58
59    #[error("trackers `{left}` and `{right}` both own `{path}`; content lanes must be disjoint")]
60    TrackerPathConflict {
61        left: String,
62        right: String,
63        path: camino::Utf8PathBuf,
64    },
65
66    #[error("no tracker named `{0}` is defined in .newgit/trackers/")]
67    UnknownTracker(String),
68
69    #[error("unknown starter template `{0}`")]
70    UnknownTemplate(String),
71
72    #[error("tracker `{tracker}` has no captured content at rev `{rev}`")]
73    NoSnapshot { tracker: String, rev: String },
74
75    #[error("tracker `{0}` owns no paths; there is nothing to capture from a workspace")]
76    TrackerHasNoPaths(String),
77
78    #[error(
79        "tracker `{tracker}` owns nothing on disk in the store repo, so there is nothing to \
80         seed from: {paths}"
81    )]
82    NothingToSeed { tracker: String, paths: String },
83
84    #[error("no resource named `{0}` is defined in .newgit/resources/")]
85    UnknownResource(String),
86
87    #[error("resource `{resource}` has no action named `{action}`")]
88    UnknownAction { resource: String, action: String },
89
90    #[error("resource `{resource}` is already running (pid {pid}); stop it first")]
91    AlreadyRunning { resource: String, pid: u32 },
92
93    #[error("resource dependency cycle: {0:?}")]
94    DependencyCycle(Vec<String>),
95
96    #[error(
97        "resource `{resource}` depends on `{dependency}`, which is neither a tracker nor a resource"
98    )]
99    MissingDependency {
100        resource: String,
101        dependency: String,
102    },
103
104    #[error("no checkpoints exist for `{0}`; create one with `newgit checkpoint {0}`")]
105    NoCheckpoints(String),
106
107    #[error(
108        "no checkpoint `{id}` for `{instance}`; list them with `newgit checkpoints {instance}`"
109    )]
110    UnknownCheckpoint { instance: String, id: String },
111
112    #[error(
113        "checkpoint command for resource `{resource}` exited with {code} (log: {log}); \
114         the checkpoint was aborted — a checkpoint that missed a resource is not coherent"
115    )]
116    CheckpointCommandFailed {
117        resource: String,
118        code: i32,
119        log: Utf8PathBuf,
120    },
121
122    #[error(
123        "resource `{resource}` renders into `{path}`, but `{find}` occurs {found} time(s) there, \
124         not {expected}; the committed file changed under the definition, or the string is not \
125         unique — a multi-line `find` disambiguates, and `count` declares a repeat"
126    )]
127    RenderMatchCount {
128        resource: String,
129        path: Utf8PathBuf,
130        find: String,
131        expected: usize,
132        found: usize,
133    },
134
135    #[error(
136        "resource `{resource}` renders `{value}` into `{path}`, which would then occur {found} \
137         time(s) there, not {expected}; `newgit capture` could not reverse that unambiguously, \
138         so include more context in `find` and `with`"
139    )]
140    RenderNotInvertible {
141        resource: String,
142        path: Utf8PathBuf,
143        value: String,
144        expected: usize,
145        found: usize,
146    },
147
148    #[error(
149        "resource `{resource}` renders into `{path}` with unresolved `{placeholder}`; a render \
150         writes a file rather than a command line, so an unrendered placeholder would be \
151         committed-looking content nobody wrote"
152    )]
153    RenderUnresolved {
154        resource: String,
155        path: Utf8PathBuf,
156        placeholder: String,
157    },
158
159    #[error(
160        "resource `{resource}` renders into `{path}`, where `{left}` and `{right}` claim \
161         overlapping text; all finds are located in the committed content and applied at once, \
162         so there is no order in which one of them wins"
163    )]
164    RenderOverlappingFinds {
165        resource: String,
166        path: Utf8PathBuf,
167        left: String,
168        right: String,
169    },
170
171    #[error(
172        "resources `{left}` and `{right}` both render into `{path}`; render targets must be disjoint"
173    )]
174    RenderPathConflict {
175        left: String,
176        right: String,
177        path: Utf8PathBuf,
178    },
179
180    #[error(
181        "resource `{resource}` renders into `{path}`, which is neither tracked by Git nor owned \
182         by a tracker; a render substitutes into committed content, so there has to be some"
183    )]
184    RenderPathNotCommitted { resource: String, path: Utf8PathBuf },
185
186    #[error("{0}")]
187    Unsupported(String),
188
189    #[error("path is not valid UTF-8: {0}")]
190    NonUtf8Path(String),
191}
192
193impl NewgitError {
194    pub fn io(path: impl Into<Utf8PathBuf>, source: std::io::Error) -> Self {
195        Self::Io {
196            path: path.into(),
197            source,
198        }
199    }
200}