1use serde::{Deserialize, Serialize};
2use std::path::PathBuf;
3
4#[derive(Clone, Debug, Eq, PartialEq)]
6pub enum ActionMode {
7 Push,
10
11 Pull,
14}
15
16#[derive(Clone, Debug, Eq, PartialEq)]
19pub enum InitError {
20 IoError { kind: std::io::ErrorKind },
22
23 AlreadyInitialized,
26
27 NotEmpty,
29}
30
31impl From<std::io::Error> for InitError {
32 fn from(err: std::io::Error) -> Self {
33 InitError::IoError { kind: err.kind() }
34 }
35}
36
37impl ToString for InitError {
38 fn to_string(&self) -> String {
39 match self {
40 InitError::IoError { kind: e_kind } => {
41 format!("IO error - {e_kind}")
42 }
43 InitError::AlreadyInitialized => {
44 String::from("This folder is already a Tendrils repo")
45 }
46 InitError::NotEmpty => {
47 String::from(
48 "This folder is not empty. Creating a Tendrils \
49 folder here may interfere with the existing \
50 contents.")
51 }
52 }
53 }
54}
55
56#[derive(Clone, Debug, Eq, PartialEq)]
59pub enum GetTendrilsRepoError {
60 GivenInvalid { path: PathBuf },
62
63 DefaultInvalid { path: PathBuf },
65
66 DefaultNotSet,
68
69 ConfigError(GetConfigError),
71}
72
73impl ToString for GetTendrilsRepoError {
74 fn to_string(&self) -> String {
75 match self {
76 GetTendrilsRepoError::GivenInvalid { path } => {
77 format!("{} is not a Tendrils repo", path.to_string_lossy())
78 }
79 GetTendrilsRepoError::DefaultInvalid { path } => {
80 format!("The default path \"{}\" is not a Tendrils repo", path.to_string_lossy())
81 }
82 GetTendrilsRepoError::DefaultNotSet => {
83 String::from("The default Tendrils repo path is not set")
84 }
85 GetTendrilsRepoError::ConfigError(err) => err.to_string(),
86 }
87 }
88}
89
90impl From<GetConfigError> for GetTendrilsRepoError {
91 fn from(err: GetConfigError) -> Self {
92 GetTendrilsRepoError::ConfigError(err.with_cfg_type(ConfigType::Global))
93 }
94}
95
96#[derive(Clone, Debug, Eq, PartialEq)]
99pub enum GetConfigError {
100 IoError { cfg_type: ConfigType, kind: std::io::ErrorKind },
102
103 ParseError { cfg_type: ConfigType, msg: String },
105}
106
107impl GetConfigError {
108 pub fn with_cfg_type(self, cfg_type: ConfigType) -> GetConfigError {
110 match self {
111 GetConfigError::IoError { kind, .. } => {
112 GetConfigError::IoError { cfg_type, kind }
113 }
114 GetConfigError::ParseError { msg, .. } => {
115 GetConfigError::ParseError { cfg_type, msg }
116 }
117 }
118 }
119}
120
121impl ToString for GetConfigError {
122 fn to_string(&self) -> String {
123 match self {
124 GetConfigError::IoError { cfg_type, kind } => format!(
125 "IO error while reading the {} file:\n{kind}",
126 cfg_type.file_name(),
127 ),
128 GetConfigError::ParseError { cfg_type, msg } => format!(
129 "Could not parse the {} file:\n{msg}",
130 cfg_type.file_name(),
131 ),
132 }
133 }
134}
135
136impl From<std::io::Error> for GetConfigError {
137 fn from(err: std::io::Error) -> Self {
138 GetConfigError::IoError { cfg_type: ConfigType::Repo, kind: err.kind() }
139 }
140}
141
142impl From<serde_json::Error> for GetConfigError {
143 fn from(err: serde_json::Error) -> Self {
144 GetConfigError::ParseError {
145 cfg_type: ConfigType::Repo,
146 msg: err.to_string()
147 }
148 }
149}
150
151#[derive(Clone, Debug, Eq, PartialEq)]
153pub enum ConfigType {
154 Repo,
156
157 Global,
159}
160
161impl ConfigType {
162 fn file_name(&self) -> &str {
163 match self {
164 ConfigType::Repo => "tendrils.json",
165 ConfigType::Global => "global-config.json"
166 }
167 }
168}
169
170#[derive(Clone, Debug, Eq, PartialEq)]
172pub enum SetupError {
173 CannotSymlink,
175 ConfigError(GetConfigError),
177 NoValidTendrilsRepo(GetTendrilsRepoError),
179}
180
181impl ToString for SetupError {
182 fn to_string(&self) -> String {
183 match self {
184 SetupError::CannotSymlink => String::from(
185 "Missing the permissions required to create symlinks on \
186 Windows. Consider:\n \
187 - Running this command in an elevated terminal\n \
188 - Enabling developer mode (this allows creating symlinks \
189 without requiring administrator priviledges)\n \
190 - Changing these tendrils to non-link modes instead"
191 ),
192 SetupError::ConfigError(err) => err.to_string(),
193 SetupError::NoValidTendrilsRepo(err) => err.to_string(),
194 }
195 }
196}
197
198impl From<GetTendrilsRepoError> for SetupError {
199 fn from(err: GetTendrilsRepoError) -> Self {
200 SetupError::NoValidTendrilsRepo(err)
201 }
202}
203
204impl From<GetConfigError> for SetupError {
205 fn from(err: GetConfigError) -> Self {
206 SetupError::ConfigError(err)
207 }
208}
209
210#[derive(Clone, Debug, Eq, PartialEq)]
212pub enum TendrilActionSuccess {
213 New,
220
221 Overwrite,
224
225 NewSkipped,
228
229 OverwriteSkipped,
232}
233
234impl ToString for TendrilActionSuccess {
235 fn to_string(&self) -> String {
236 match self {
237 TendrilActionSuccess::New => String::from("Created"),
238 TendrilActionSuccess::NewSkipped => String::from("Skipped creation"),
239 TendrilActionSuccess::Overwrite => String::from("Overwritten"),
240 TendrilActionSuccess::OverwriteSkipped => {
241 String::from("Skipped overwrite")
242 }
243 }
244 }
245}
246
247#[derive(Clone, Debug, Eq, PartialEq)]
249pub enum TendrilActionError {
250 IoError {
254 kind: std::io::ErrorKind,
256 loc: Location,
258 },
259
260 ModeMismatch,
264
265 TypeMismatch {
271 mistype: FsoType,
273 loc: Location,
275 },
276}
277
278impl From<std::io::Error> for TendrilActionError {
279 fn from(err: std::io::Error) -> Self {
280 TendrilActionError::IoError { kind: err.kind(), loc: Location::Unknown }
281 }
282}
283
284impl From<std::io::ErrorKind> for TendrilActionError {
285 fn from(e_kind: std::io::ErrorKind) -> Self {
286 TendrilActionError::IoError { kind: e_kind, loc: Location::Unknown }
287 }
288}
289
290impl ToString for TendrilActionError {
291 fn to_string(&self) -> String {
292 use std::io::ErrorKind::NotFound;
293 use FsoType::{Dir, File, SymDir, SymFile, BrokenSym};
294 use Location::{Dest, Source, Unknown};
295 match self {
296 TendrilActionError::IoError { kind: NotFound, loc: Source } => {
297 String::from("Source not found")
298 }
299 TendrilActionError::IoError { kind: NotFound, loc: Dest } => {
300 String::from("Destination not found")
301 }
302 TendrilActionError::IoError { kind: NotFound, loc: Unknown } => {
303 String::from("Not found")
304 }
305 TendrilActionError::IoError { kind: e_kind, loc: Source } => {
306 format!("{:?} error at source", e_kind)
307 }
308 TendrilActionError::IoError { kind: e_kind, loc: Dest } => {
309 format!("{:?} error at destination", e_kind)
310 }
311 TendrilActionError::IoError { kind: e_kind, loc: Unknown } => {
312 format!("{:?} error", e_kind)
313 }
314 TendrilActionError::ModeMismatch => {
315 String::from("Wrong tendril type")
316 }
317 TendrilActionError::TypeMismatch { loc: Source, mistype: File } => {
318 String::from("Unexpected file at source")
319 }
320 TendrilActionError::TypeMismatch { loc: Source, mistype: Dir } => {
321 String::from("Unexpected directory at source")
322 }
323 TendrilActionError::TypeMismatch {
324 loc: Source,
325 mistype: SymFile | SymDir | BrokenSym,
326 } => String::from("Unexpected symlink at source"),
327 TendrilActionError::TypeMismatch { loc: Dest, mistype: File } => {
328 String::from("Unexpected file at destination")
329 }
330 TendrilActionError::TypeMismatch { loc: Dest, mistype: Dir } => {
331 String::from("Unexpected directory at destination")
332 }
333 TendrilActionError::TypeMismatch {
334 loc: Dest,
335 mistype: SymFile | SymDir | BrokenSym,
336 } => String::from("Unexpected symlink at destination"),
337 TendrilActionError::TypeMismatch { loc: Unknown, mistype: _ } => {
338 String::from("Unexpected file system object")
339 }
340 }
341 }
342}
343
344#[derive(Clone, Debug, Eq, PartialEq)]
346pub enum Location {
347 Source,
348 Dest,
349 Unknown,
350}
351
352#[derive(Clone, Debug, Eq, PartialEq)]
354pub enum FsoType {
355 File,
357 Dir,
359 SymFile,
361 SymDir,
363 BrokenSym,
365}
366
367impl FsoType {
368 pub fn is_file(&self) -> bool {
369 self == &FsoType::File || self == &FsoType::SymFile
370 }
371
372 pub fn is_dir(&self) -> bool {
373 self == &FsoType::Dir || self == &FsoType::SymDir
374 }
375
376 pub fn is_symlink(&self) -> bool {
377 self == &FsoType::SymFile
378 || self == &FsoType::SymDir
379 || self == &FsoType::BrokenSym
380 }
381}
382
383#[derive(Copy, Clone, Debug, PartialEq, Eq)]
386pub enum TendrilMode {
387 CopyMerge,
392
393 CopyOverwrite,
397
398 Link,
401}
402
403impl TendrilMode {
404 pub fn requires_symlink(&self) -> bool {
405 *self == TendrilMode::Link
406 }
407}
408
409impl ToString for TendrilMode {
410 fn to_string(&self) -> String {
411 match &self {
412 TendrilMode::CopyMerge => String::from("Copy - Directory merge"),
413 TendrilMode::CopyOverwrite => String::from("Copy - Directory overwrite"),
414 TendrilMode::Link => String::from("Link"),
415 }
416 }
417}
418
419#[derive(Clone, Debug, Eq, PartialEq)]
421pub enum InvalidTendrilError {
422 InvalidLocal,
423
424 Recursion,
429}
430
431#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
432#[serde(untagged)]
433pub(crate) enum OneOrMany<T> {
434 One(T),
437
438 Vec(Vec<T>),
440}
441
442impl<T> From<OneOrMany<T>> for Vec<T> {
443 fn from(from: OneOrMany<T>) -> Self {
444 match from {
445 OneOrMany::One(val) => vec![val],
446 OneOrMany::Vec(vec) => vec,
447 }
448 }
449}
450
451impl<T> From<Vec<T>> for OneOrMany<T> {
452 fn from(mut from: Vec<T>) -> Self {
453 match from.len() {
454 1 => OneOrMany::One(from.remove(0)),
455 _ => OneOrMany::Vec(from),
456 }
457 }
458}