Skip to main content

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}