From: Ben Pfaff Date: Mon, 6 Oct 2025 01:10:54 +0000 (-0700) Subject: rust: Add `PaperSize` parsing and formatting in paper-sizes crate. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d769f302e6620b9a84fe12ab44319ca7d145df4f;p=pspp rust: Add `PaperSize` parsing and formatting in paper-sizes crate. --- diff --git a/rust/paper-sizes/src/lib.rs b/rust/paper-sizes/src/lib.rs index 175b083ab2..f795356e04 100644 --- a/rust/paper-sizes/src/lib.rs +++ b/rust/paper-sizes/src/lib.rs @@ -172,6 +172,74 @@ impl PaperSize { } } +/// An error parsing a [PaperSize]. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum ParsePaperSizeError { + /// Invalid paper height. + InvalidHeight, + + /// Invalid paper width. + InvalidWidth, + + /// Invalid unit of measurement. + InvalidUnit, + + /// Missing unit of measurement. + MissingUnit, + + /// Missing delimiter. + MissingDelimiter, +} + +impl Error for ParsePaperSizeError {} + +impl Display for ParsePaperSizeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ParsePaperSizeError::InvalidHeight => write!(f, "Invalid paper height"), + ParsePaperSizeError::InvalidWidth => write!(f, "Invalid paper width"), + ParsePaperSizeError::InvalidUnit => write!(f, "Invalid unit of measurement"), + ParsePaperSizeError::MissingUnit => write!(f, "Missing unit in paper size"), + ParsePaperSizeError::MissingDelimiter => write!(f, "Missing delimiter in paper size"), + } + } +} + +impl FromStr for PaperSize { + type Err = ParsePaperSizeError; + + /// Parses a paper size that takes one of the forms `8.5x11in` or `8.5,11in` + /// or `8.5,11,in`, with optional white space. + fn from_str(s: &str) -> Result { + let Some((width, rest)) = s.split_once([',', 'x']) else { + return Err(ParsePaperSizeError::MissingDelimiter); + }; + let (height, unit) = if let Some(result) = rest.split_once(',') { + result + } else if let Some(alpha) = rest.find(|c: char| c.is_alphabetic()) { + rest.split_at(alpha) + } else { + return Err(ParsePaperSizeError::MissingUnit); + }; + + let width = f64::from_str(width.trim()).map_err(|_| ParsePaperSizeError::InvalidWidth)?; + let height = + f64::from_str(height.trim()).map_err(|_| ParsePaperSizeError::InvalidHeight)?; + let unit = Unit::from_str(unit.trim()).map_err(|_| ParsePaperSizeError::InvalidUnit)?; + Ok(Self { + width, + height, + unit, + }) + } +} + +impl Display for PaperSize { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}x{}{}", self.width, self.height, self.unit) + } +} + /// An error parsing a [PaperSpec]. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ParsePaperSpecError { @@ -188,6 +256,18 @@ pub enum ParsePaperSpecError { MissingField, } +impl From for ParsePaperSpecError { + fn from(value: ParsePaperSizeError) -> Self { + match value { + ParsePaperSizeError::InvalidHeight => Self::InvalidHeight, + ParsePaperSizeError::InvalidWidth => Self::InvalidWidth, + ParsePaperSizeError::InvalidUnit => Self::InvalidUnit, + ParsePaperSizeError::MissingUnit => Self::MissingField, + ParsePaperSizeError::MissingDelimiter => Self::MissingField, + } + } +} + impl Error for ParsePaperSpecError {} impl Display for ParsePaperSpecError { @@ -224,23 +304,16 @@ impl PaperSpec { impl FromStr for PaperSpec { type Err = ParsePaperSpecError; + /// Parses a paper specification as `name,`, where `` is one of + /// the formats supported by [PaperSize::from_str]. + /// + /// The canonical form of a paper specification is `name,width,height,unit`. fn from_str(s: &str) -> Result { - let mut tokens = s.split(',').map(|s| s.trim()); - if let Some(name) = tokens.next() - && let Some(width) = tokens.next() - && let Some(height) = tokens.next() - && let Some(unit) = tokens.next() - { - let width = f64::from_str(width).map_err(|_| ParsePaperSpecError::InvalidWidth)?; - let height = f64::from_str(height).map_err(|_| ParsePaperSpecError::InvalidHeight)?; - let unit = Unit::from_str(unit).map_err(|_| ParsePaperSpecError::InvalidUnit)?; - Ok(Self { - name: String::from(name).into(), - size: PaperSize::new(width, height, unit), - }) - } else { - Err(ParsePaperSpecError::MissingField) - } + let (name, size) = s.split_once(',').ok_or(ParsePaperSpecError::MissingField)?; + Ok(Self { + name: String::from(name).into(), + size: size.parse()?, + }) } } @@ -660,10 +733,11 @@ impl Catalog { #[cfg(test)] mod tests { - use std::{borrow::Cow, path::Path}; + use std::{borrow::Cow, path::Path, str::FromStr}; use crate::{ - CatalogBuildError, CatalogBuilder, PaperSize, PaperSpec, ParsePaperSpecError, Unit, locale, + A4, CatalogBuildError, CatalogBuilder, PaperSize, PaperSpec, ParsePaperSizeError, + ParsePaperSpecError, Unit, locale, }; #[test] @@ -714,6 +788,47 @@ mod tests { ); } + #[test] + fn papersize() { + assert_eq!( + "8.5x11in".parse(), + Ok(PaperSize::new(8.5, 11.0, Unit::Inch)) + ); + assert_eq!( + "8.5,11in".parse(), + Ok(PaperSize::new(8.5, 11.0, Unit::Inch)) + ); + assert_eq!( + " 8.5 x 11 in ".parse(), + Ok(PaperSize::new(8.5, 11.0, Unit::Inch)) + ); + assert_eq!( + PaperSize::from_str("8.5x.in"), + Err(ParsePaperSizeError::InvalidHeight) + ); + assert_eq!( + PaperSize::from_str(".x11in"), + Err(ParsePaperSizeError::InvalidWidth) + ); + assert_eq!( + PaperSize::from_str("8.5x11xyzzy"), + Err(ParsePaperSizeError::InvalidUnit) + ); + assert_eq!( + PaperSize::from_str("8.5x11"), + Err(ParsePaperSizeError::MissingUnit) + ); + assert_eq!( + PaperSize::from_str(" 8.5 11 in "), + Err(ParsePaperSizeError::MissingDelimiter) + ); + assert_eq!( + PaperSize::new(8.5, 11.0, Unit::Inch).to_string(), + "8.5x11in" + ); + assert_eq!(A4.size.to_string(), "210x297mm"); + } + #[test] fn paperspec() { assert_eq!( @@ -723,6 +838,13 @@ mod tests { PaperSize::new(8.5, 11.0, Unit::Inch) )) ); + assert_eq!( + "Letter,8.5x11in".parse(), + Ok(PaperSpec::new( + Cow::from("Letter"), + PaperSize::new(8.5, 11.0, Unit::Inch) + )) + ); } #[test] diff --git a/rust/paper-sizes/testdata/td4/paperspecs b/rust/paper-sizes/testdata/td4/paperspecs index 8123450613..7a76c72256 100644 --- a/rust/paper-sizes/testdata/td4/paperspecs +++ b/rust/paper-sizes/testdata/td4/paperspecs @@ -1,4 +1,4 @@ -not,enough,fields +not enough fields OK,not a width,11,in OK,8.5,not a height,in -OK,8.5,11,not a unit \ No newline at end of file +OK,8.5,11,not a unit