Skip to main content

mf2_build/
error.rs

1//! Everything that can stop a build.
2//!
3//! A *diagnostic* — a syntax error in a locale file, a lint — is not one of
4//! these: it is a [`Report`](crate::Report) entry with a file, a line and a
5//! column, and a build fails on the count of them. This type is for what
6//! stops the build before it can report anything: I/O, a malformed
7//! `mf2.toml`, a catalog the writer refuses.
8
9use std::io;
10use std::path::PathBuf;
11
12/// The result of a build step.
13pub type Result<T> = std::result::Result<T, Error>;
14
15/// What stops a build.
16#[derive(Debug, thiserror::Error)]
17#[non_exhaustive]
18pub enum Error {
19    /// A file could not be read or written.
20    #[error("{path}: {source}")]
21    Io {
22        /// The file.
23        path: PathBuf,
24        /// What the operating system said.
25        #[source]
26        source: io::Error,
27    },
28
29    /// `mf2.toml` is not valid TOML, or a key in it is wrong.
30    #[error("{path}: {message}")]
31    Config {
32        /// The configuration file.
33        path: PathBuf,
34        /// Which key, and what is wrong with it.
35        message: String,
36    },
37
38    /// A flat JSON corpus is not an object of strings.
39    #[error("{path}:{line}:{column}: {message}")]
40    Json {
41        /// The file.
42        path: PathBuf,
43        /// One-based line.
44        line: u32,
45        /// One-based column.
46        column: u32,
47        /// What is wrong.
48        message: String,
49    },
50
51    /// The locale directory names a tag the catalog format cannot write.
52    #[error("locale {locale:?}: {source}")]
53    Locale {
54        /// The tag.
55        locale: String,
56        /// What the locale data said.
57        #[source]
58        source: mf2_locale_data::Error,
59    },
60
61    /// The catalog writer refused a locale's messages.
62    #[error("catalog for {locale}: {source}")]
63    Write {
64        /// The tag.
65        locale: String,
66        /// What the writer said.
67        #[source]
68        source: mf2_catalog::WriteError,
69    },
70
71    /// The locale data could not be sliced.
72    #[error(transparent)]
73    LocaleData(#[from] mf2_locale_data::Error),
74
75    /// The manifest could not be written or read back.
76    #[error(transparent)]
77    Manifest(#[from] mf2_catalog::ManifestError),
78
79    /// A catalog could not be written.
80    #[error(transparent)]
81    Catalog(#[from] mf2_catalog::WriteError),
82
83    /// A catalog could not be compressed.
84    ///
85    /// Both encoders write into a `Vec`, so this is a compressor fault, not a
86    /// full disk. It is still an error rather than a shrug: a truncated
87    /// `.br` would be served immutable beside a `.mf2b` whose content hash
88    /// says it is intact, and the client would see a corrupt catalog.
89    #[error("catalog for {locale}: {format}: {source}")]
90    Compress {
91        /// The tag.
92        locale: String,
93        /// `brotli` or `gzip`.
94        format: &'static str,
95        /// What the encoder said.
96        #[source]
97        source: io::Error,
98    },
99
100    /// The layout on disk is not what a build needs.
101    #[error("{0}")]
102    Layout(String),
103
104    /// Two locales' tags give the generated `Locale` one variant: `pt-BR`
105    /// and `pt_br` are both `Locale::PtBr`.
106    #[error(
107        "locales {first:?} and {second:?} both give the generated `Locale::{variant}`: rename one"
108    )]
109    LocaleVariant {
110        /// The first of the two tags.
111        first: String,
112        /// The second.
113        second: String,
114        /// The variant they share.
115        variant: String,
116    },
117
118    /// Two markup names have one hash, so a style could not tell them
119    /// apart.
120    #[error("markup names {first:?} and {second:?} have one hash: rename one")]
121    MarkupHash {
122        /// The first of the two.
123        first: String,
124        /// The second.
125        second: String,
126    },
127
128    /// The build found errors in the corpus; they are in the report.
129    #[error("{errors} error{} in {locales} locale{}", plural(*errors), plural(*locales))]
130    Corpus {
131        /// How many errors.
132        errors: usize,
133        /// In how many locales.
134        locales: usize,
135    },
136
137    /// [`run`](crate::run) cannot see `mf2`'s features: cargo passes them
138    /// only to a crate that names `mf2` as a normal dependency.
139    #[error(
140        "mf2-build: this crate does not name `mf2` in its [dependencies], so its \
141         build script cannot see mf2's features (DEP_MF2_V2_FEATURES): add it \
142         there, as the crate that includes the generated module needs it anyway"
143    )]
144    NoMf2,
145
146    /// `mf2` and `mf2-build` are not the same version.
147    #[error(
148        "mf2-build {build} builds for mf2 {build}, but this crate's mf2 is {mf2}: \
149         name the same version of both"
150    )]
151    Mf2Version {
152        /// What `mf2` says its version is.
153        mf2: String,
154        /// This crate's.
155        build: String,
156    },
157
158    /// `mf2` has `datetime-icu`, and this `mf2-build` was built without
159    /// `icu-blob`, which writes ICU4X's date data into the catalogs. A
160    /// build-dependency's features cannot come through `links`.
161    #[error(
162        "mf2-build: `mf2` has `datetime-icu`, whose catalogs carry ICU4X's date \
163         data: add `features = [\"icu-blob\"]` to this crate's `mf2-build` \
164         build-dependency"
165    )]
166    IcuBlob,
167}
168
169fn plural(n: usize) -> &'static str {
170    if n == 1 { "" } else { "s" }
171}
172
173impl Error {
174    /// An I/O error that names its file.
175    #[doc(hidden)]
176    pub fn io(path: impl Into<PathBuf>, source: io::Error) -> Error {
177        Error::Io {
178            path: path.into(),
179            source,
180        }
181    }
182}