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}