#[cfg(target_os = "linux")]
mod locale;
+#[cfg(feature = "serde")]
+mod serde;
+
include!(concat!(env!("OUT_DIR"), "/paperspecs.rs"));
static PAPERSIZE_FILENAME: &str = "papersize";
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct ParseUnitError;
+impl Error for ParseUnitError {}
+
+impl Display for ParseUnitError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "unknown unit")
+ }
+}
+
impl FromStr for Unit {
type Err = ParseUnitError;
///
/// To convert a quantity of unit `a` into unit `b`, multiply by
/// `a.as_unit(b)`.
- fn as_unit(&self, other: Unit) -> f64 {
+ pub fn as_unit(&self, other: Unit) -> f64 {
match (*self, other) {
(Unit::Point, Unit::Point) => 1.0,
(Unit::Point, Unit::Inch) => 1.0 / 72.0,
}
}
+/// A physical length with a [Unit].
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub struct Length {
+ /// The length.
+ pub value: f64,
+
+ /// The length's unit.
+ pub unit: Unit,
+}
+
+impl Length {
+ /// Constructs a new `Length` from `value` and `unit`.
+ pub fn new(value: f64, unit: Unit) -> Self {
+ Self { value, unit }
+ }
+
+ /// Returns this length converted to `unit`.
+ pub fn as_unit(&self, unit: Unit) -> Self {
+ Self {
+ value: self.value * unit.as_unit(Unit::Inch),
+ unit,
+ }
+ }
+
+ /// Returns the value of this length in `unit`.
+ pub fn into_unit(&self, unit: Unit) -> f64 {
+ self.as_unit(unit).value
+ }
+}
+
+/// An error parsing a [Length].
+#[derive(Copy, Clone, Debug)]
+pub enum ParseLengthError {
+ /// Missing unit.
+ MissingUnit,
+ /// Invalid unit.
+ InvalidUnit,
+ /// Invalid value
+ InvalidValue,
+}
+
+impl Error for ParseLengthError {}
+
+impl Display for ParseLengthError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ ParseLengthError::MissingUnit => write!(f, "Missing unit"),
+ ParseLengthError::InvalidUnit => write!(f, "Invalid unit of measurement"),
+ ParseLengthError::InvalidValue => write!(f, "Invalid length"),
+ }
+ }
+}
+
+impl FromStr for Length {
+ type Err = ParseLengthError;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ if let Some(index) = s.find(|c: char| c.is_alphabetic()) {
+ let (value, unit) = s.split_at(index);
+ let value = value.parse().map_err(|_| ParseLengthError::InvalidValue)?;
+ let unit = unit.parse().map_err(|_| ParseLengthError::InvalidUnit)?;
+ Ok(Self { value, unit })
+ } else {
+ Err(ParseLengthError::MissingUnit)
+ }
+ }
+}
+
+impl Display for Length {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}{}", self.value, self.unit)
+ }
+}
+
/// The size of a piece of paper.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct PaperSize {
let (bw, bh) = other.as_unit(unit).into_width_height();
aw.round() == bw.round() && ah.round() == bh.round()
}
+
+ /// Returns the paper's width as a [Length].
+ pub fn width(&self) -> Length {
+ Length::new(self.width, self.unit)
+ }
+
+ /// Returns the paper's height as a [Length].
+ pub fn height(&self) -> Length {
+ Length::new(self.height, self.unit)
+ }
}
/// An error parsing a [PaperSize].
}
}
-#[cfg(feature = "serde")]
-impl serde::Serialize for PaperSize {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: serde::Serializer,
- {
- self.to_string().serialize(serializer)
- }
-}
-
-#[cfg(feature = "serde")]
-impl<'de> serde::Deserialize<'de> for PaperSize {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: serde::Deserializer<'de>,
- {
- use serde::de::Error;
- String::deserialize(deserializer)?
- .parse()
- .map_err(D::Error::custom)
- }
-}
-
/// An error parsing a [PaperSpec].
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ParsePaperSpecError {
);
}
}
-
- #[cfg(feature = "serde")]
- #[test]
- fn test_serde() {
- assert_eq!(
- serde_json::to_string(&PaperSize::new(8.5, 11.0, Unit::Inch)).unwrap(),
- "\"8.5x11in\""
- );
- assert_eq!(
- serde_json::from_str::<PaperSize>("\"8.5x11in\"").unwrap(),
- PaperSize::new(8.5, 11.0, Unit::Inch)
- )
- }
}
--- /dev/null
+use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error};
+
+#[cfg(feature = "serde")]
+use crate::PaperSize;
+use crate::{Length, Unit};
+
+impl<'de> Deserialize<'de> for Length {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ String::deserialize(deserializer)?
+ .parse()
+ .map_err(D::Error::custom)
+ }
+}
+
+impl Serialize for Length {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ self.to_string().serialize(serializer)
+ }
+}
+
+impl Serialize for PaperSize {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ self.to_string().serialize(serializer)
+ }
+}
+
+impl<'de> Deserialize<'de> for PaperSize {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ String::deserialize(deserializer)?
+ .parse()
+ .map_err(D::Error::custom)
+ }
+}
+
+impl Serialize for Unit {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ self.to_string().serialize(serializer)
+ }
+}
+
+impl<'de> Deserialize<'de> for Unit {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ String::deserialize(deserializer)?
+ .parse()
+ .map_err(D::Error::custom)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::{Length, PaperSize, Unit};
+
+ #[test]
+ fn unit() {
+ assert_eq!(serde_json::to_string(&Unit::Point).unwrap(), "\"pt\"");
+ assert_eq!(serde_json::from_str::<Unit>("\"pt\"").unwrap(), Unit::Point)
+ }
+
+ #[test]
+ fn length() {
+ assert_eq!(
+ serde_json::to_string(&Length::new(123.0, Unit::Millimeter)).unwrap(),
+ "\"123mm\""
+ );
+ assert_eq!(
+ serde_json::from_str::<Length>("\"123mm\"").unwrap(),
+ Length::new(123.0, Unit::Millimeter)
+ )
+ }
+
+ #[test]
+ fn paper_size() {
+ assert_eq!(
+ serde_json::to_string(&PaperSize::new(8.5, 11.0, Unit::Inch)).unwrap(),
+ "\"8.5x11in\""
+ );
+ assert_eq!(
+ serde_json::from_str::<PaperSize>("\"8.5x11in\"").unwrap(),
+ PaperSize::new(8.5, 11.0, Unit::Inch)
+ )
+ }
+}