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}