lds_pack/error.rs
1//! Error type for pack creation, inspection, and restore.
2
3use std::path::PathBuf;
4
5use thiserror::Error;
6
7/// Errors produced by the pack module.
8#[derive(Debug, Error)]
9pub enum PackError {
10 /// An I/O error while reading the project or writing the archive.
11 #[error("pack I/O error: {0}")]
12 Io(#[from] std::io::Error),
13
14 /// The tree walk failed (permission denied, vanished directory, cycle).
15 #[error("pack walk error: {0}")]
16 Walk(#[from] walkdir::Error),
17
18 /// The given path is not a directory.
19 #[error("not a directory: {0}")]
20 NotADirectory(PathBuf),
21
22 /// A configured glob could not be compiled.
23 ///
24 /// Carries the offending pattern so a typo in `[pack]` points at itself
25 /// rather than silently matching nothing.
26 #[error("invalid pack pattern '{pattern}': {message}")]
27 BadPattern {
28 /// The pattern as written in configuration.
29 pattern: String,
30 /// Why it failed to compile.
31 message: String,
32 },
33
34 /// The manifest could not be serialized.
35 #[error("manifest serialize error: {0}")]
36 ManifestSerialize(#[from] toml::ser::Error),
37
38 /// The manifest could not be parsed.
39 #[error("manifest parse error: {0}")]
40 ManifestParse(#[from] toml::de::Error),
41
42 /// The manifest is larger than this build will read into memory.
43 ///
44 /// Reading is what happens before the archive has been judged, so the
45 /// archive cannot be the one deciding how much memory that takes.
46 /// Compression makes the difference enormous: a small file expands into as
47 /// much filler as its author cares to write.
48 #[error("manifest is larger than the {limit} byte limit; refusing the archive")]
49 ManifestTooLarge {
50 /// The largest manifest this build reads, in bytes.
51 limit: u64,
52 },
53
54 /// The archive did not begin with a `pack.toml` entry.
55 #[error("archive has no {name} entry — not an lds pack?", name = crate::manifest::MANIFEST_NAME)]
56 MissingManifest,
57
58 /// The archive was written by a newer pack format than this build reads.
59 #[error("pack format version {found} is newer than supported version {supported}")]
60 UnsupportedFormat {
61 /// Version recorded in the archive.
62 found: u32,
63 /// Highest version this build understands.
64 supported: u32,
65 },
66
67 /// The restore destination already exists and would be overwritten.
68 #[error("destination already exists: {0}")]
69 DestinationExists(PathBuf),
70
71 /// An archive entry names a path that would land outside the restore
72 /// destination (`..` or an absolute component).
73 ///
74 /// A pack produced by this crate never contains such an entry, so one that
75 /// does is not a pack with a blemish — it is not trustworthy at all, and
76 /// the restore stops rather than extracting the rest of it.
77 #[error("archive entry '{0}' escapes the restore destination; refusing the archive")]
78 EscapingArchivePath(String),
79
80 /// A manifest field names a path that would land outside the restore
81 /// destination.
82 ///
83 /// The manifest travels in the archive, so its paths are the archive's
84 /// claims about the project just as much as the entry names are — worktree
85 /// locations and symlink paths are all joined onto the destination. Checked
86 /// with the same rule and refused the same way; the field is named because
87 /// the value alone does not say which claim carried it.
88 #[error(
89 "manifest field '{field}' names '{value}', which escapes the restore destination; refusing the archive"
90 )]
91 EscapingManifestPath {
92 /// The manifest key that carried the path.
93 field: String,
94 /// The path as the manifest wrote it.
95 value: String,
96 },
97
98 /// An archive entry is of a type a restore will not materialize.
99 ///
100 /// This crate's writer produces directories, regular files and symlinks and
101 /// nothing else, so any other type came from another producer. Device
102 /// nodes and fifos are not project content, and a hard link names a file
103 /// the pack does not carry — see [`crate::restore::HardLinkRecord`] for why
104 /// that one is reported rather than followed.
105 #[error(
106 "archive entry '{path}' is a {kind}, which a pack never contains; refusing the archive"
107 )]
108 UnusableArchiveEntry {
109 /// The entry as the archive names it.
110 path: String,
111 /// What it is, in the words the report uses.
112 kind: String,
113 },
114
115 /// An archive entry would be written through a symlink, which would land
116 /// it outside the restore destination.
117 ///
118 /// The scan never descends into a symlinked directory, so a legitimate
119 /// pack has no file entries underneath a link. An archive that routes a
120 /// write through one is crafted, and the restore stops.
121 #[error("archive entry '{path}' would write through symlink '{via}'; refusing the archive")]
122 WriteThroughSymlink {
123 /// The entry that would have been written.
124 path: String,
125 /// The symlink it would have been written through.
126 via: PathBuf,
127 },
128}