Files
pdfcraft/crates/print/src/error.rs
T
aiongg 0af6cf2418 refactor(print): split lib.rs into error, settings, sheets, imposition
New modules: error.rs, settings.rs, sheets.rs, imposition.rs.
2026-10-26 22:18:50 +00:00

85 lines
3.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// What can go wrong when printing.
///
/// The three things a *person* can be wrong about — there is nothing to print, a page number past
/// the end, a range that is not a page — each have their own variant with the numbers apart from
/// the words, so an interface can say them in its own language with [`PrintError::said`]. The rest
/// are faults rather than mistakes and carry their text as it is.
#[derive(Debug, thiserror::Error, PartialEq)]
pub enum PrintError {
#[error("there are no pages to print")]
NoPages,
/// A page number past the end of the document: the number asked for, and the page count.
#[error("page {0} is out of range (1–{1})")]
OutOfRange(usize, usize),
/// A range token that is neither a page number nor a page label.
#[error("{0:?} is not a page number or label")]
NotAPage(String),
#[error("{0}")]
Invalid(String),
#[error("{0}")]
Spool(String),
#[error(transparent)]
Cos(#[from] pdfcraft_cos::CosError),
}
/// How an interface says one of our wordings in its own language, holes and all: given the English
/// in [`WORDINGS`], the same wording in that language. [`in_english`] keeps the English.
pub type Words<'a> = &'a dyn Fn(&str) -> String;
/// The [`Words`] that translate nothing: the wording as it is written here.
pub fn in_english(wording: &str) -> String {
wording.to_owned()
}
/// What a person can get wrong about what to print, in English, with their holes still in them.
///
/// An interface that reports these in another language translates these three, which is the whole
/// reason they are gathered here; `wordings_are_said` keeps them in step with the errors.
pub const WORDINGS: &[&str] = &["there are no pages to print", "page {n} is out of range (1–{count})", "{page} is not a page number or label"];
impl PrintError {
/// This error as one sentence, said in the language `words` translates into.
///
/// The wording is translated and the numbers go in afterwards, in a single pass, so what the
/// person typed cannot be read as a hole. With [`in_english`] this is the error's own
/// `Display`, which is what the command line and scripts report.
pub fn said(&self, words: Words) -> String {
match self {
PrintError::NoPages => words("there are no pages to print"),
PrintError::OutOfRange(n, count) => {
say(&words("page {n} is out of range (1–{count})"), &[("{n}", n.to_string()), ("{count}", count.to_string())])
}
// The token goes in quoted, the way it is typed, as the error itself prints it.
PrintError::NotAPage(token) => say(&words("{page} is not a page number or label"), &[("{page}", format!("{token:?}"))]),
fault => fault.to_string(),
}
}
}
/// A wording with its holes filled.
///
/// A hole is a name in braces, such as `{n}`; what is not in `values` is left as it is written.
/// Values go in during a single pass, so a value with braces in it is never read as a hole.
fn say(wording: &str, values: &[(&str, String)]) -> String {
let mut out = String::with_capacity(wording.len() + 8);
let mut rest = wording;
while let Some(open) = rest.find('{') {
let (before, from_brace) = rest.split_at(open);
out.push_str(before);
// `find` counts from the brace and `}` is one byte, so this is a char boundary.
let Some(close) = from_brace.find('}') else {
// A brace with nothing closing it is just text.
out.push_str(from_brace);
return out;
};
let Some(hole) = from_brace.get(..=close) else { return out + from_brace };
match values.iter().find(|(name, _)| *name == hole) {
Some((_, value)) => out.push_str(value),
None => out.push_str(hole),
}
rest = from_brace.get(close + 1..).unwrap_or_default();
}
out.push_str(rest);
out
}