summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-04-07 18:04:29 +0200
committerLaurenz <laurmaedje@gmail.com>2022-04-07 18:04:29 +0200
commit4bb6240b401605ef6d905273db07545e14f9a21f (patch)
treeb01163a5fce3fe62d16abcbdabf37bc373617ff1 /src
parent1192132dc0a9e991953fd29e93f87c8437a53ea0 (diff)
Make `Relative` generic
Diffstat (limited to 'src')
-rw-r--r--src/eval/layout.rs12
-rw-r--r--src/eval/ops.rs20
-rw-r--r--src/eval/value.rs4
-rw-r--r--src/export/pdf.rs4
-rw-r--r--src/font.rs2
-rw-r--r--src/frame.rs4
-rw-r--r--src/geom/angle.rs45
-rw-r--r--src/geom/em.rs17
-rw-r--r--src/geom/fraction.rs15
-rw-r--r--src/geom/length.rs61
-rw-r--r--src/geom/mod.rs24
-rw-r--r--src/geom/paint.rs36
-rw-r--r--src/geom/point.rs21
-rw-r--r--src/geom/ratio.rs16
-rw-r--r--src/geom/relative.rs158
-rw-r--r--src/geom/scalar.rs10
-rw-r--r--src/geom/sides.rs2
-rw-r--r--src/geom/spec.rs25
-rw-r--r--src/geom/transform.rs10
-rw-r--r--src/library/graphics/line.rs17
-rw-r--r--src/library/graphics/shape.rs2
-rw-r--r--src/library/graphics/transform.rs8
-rw-r--r--src/library/layout/columns.rs4
-rw-r--r--src/library/layout/grid.rs4
-rw-r--r--src/library/layout/pad.rs6
-rw-r--r--src/library/layout/page.rs14
-rw-r--r--src/library/layout/spacing.rs2
-rw-r--r--src/library/mod.rs13
-rw-r--r--src/library/structure/list.rs6
-rw-r--r--src/library/structure/table.rs2
-rw-r--r--src/library/text/deco.rs12
-rw-r--r--src/library/text/mod.rs2
-rw-r--r--src/library/text/par.rs6
-rw-r--r--src/library/utility/color.rs2
34 files changed, 305 insertions, 281 deletions
diff --git a/src/eval/layout.rs b/src/eval/layout.rs
index f541694c..9bf44194 100644
--- a/src/eval/layout.rs
+++ b/src/eval/layout.rs
@@ -8,7 +8,9 @@ use std::sync::Arc;
use super::{Barrier, StyleChain};
use crate::diag::TypResult;
use crate::frame::{Element, Frame, Geometry, Shape, Stroke};
-use crate::geom::{Align, Length, Paint, Point, Relative, Sides, Size, Spec, Transform};
+use crate::geom::{
+ Align, Length, Numeric, Paint, Point, Relative, Sides, Size, Spec, Transform,
+};
use crate::library::graphics::MoveNode;
use crate::library::layout::{AlignNode, PadNode};
use crate::util::Prehashed;
@@ -161,7 +163,7 @@ impl LayoutNode {
}
/// Force a size for this node.
- pub fn sized(self, sizing: Spec<Option<Relative>>) -> Self {
+ pub fn sized(self, sizing: Spec<Option<Relative<Length>>>) -> Self {
if sizing.any(Option::is_some) {
SizedNode { sizing, child: self }.pack()
} else {
@@ -189,7 +191,7 @@ impl LayoutNode {
}
/// Pad this node at the sides.
- pub fn padded(self, padding: Sides<Relative>) -> Self {
+ pub fn padded(self, padding: Sides<Relative<Length>>) -> Self {
if !padding.left.is_zero()
|| !padding.top.is_zero()
|| !padding.right.is_zero()
@@ -205,7 +207,7 @@ impl LayoutNode {
pub fn moved(self, offset: Point) -> Self {
if !offset.is_zero() {
MoveNode {
- transform: Transform::translation(offset.x, offset.y),
+ transform: Transform::translate(offset.x, offset.y),
child: self,
}
.pack()
@@ -292,7 +294,7 @@ impl Layout for EmptyNode {
#[derive(Debug, Hash)]
struct SizedNode {
/// How to size the node horizontally and vertically.
- sizing: Spec<Option<Relative>>,
+ sizing: Spec<Option<Relative<Length>>>,
/// The node to be sized.
child: LayoutNode,
}
diff --git a/src/eval/ops.rs b/src/eval/ops.rs
index 024de433..ff21d93f 100644
--- a/src/eval/ops.rs
+++ b/src/eval/ops.rs
@@ -2,7 +2,7 @@ use std::cmp::Ordering;
use super::{Dynamic, StrExt, Value};
use crate::diag::StrResult;
-use crate::geom::{Align, Spec, SpecAxis};
+use crate::geom::{Align, Numeric, Spec, SpecAxis};
use Value::*;
/// Bail with a type mismatch error.
@@ -66,12 +66,12 @@ pub fn add(lhs: Value, rhs: Value) -> StrResult<Value> {
(Angle(a), Angle(b)) => Angle(a + b),
(Length(a), Length(b)) => Length(a + b),
- (Length(a), Ratio(b)) => Relative(a + b),
- (Length(a), Relative(b)) => Relative(a + b),
+ (Length(a), Ratio(b)) => Relative(b + a),
+ (Length(a), Relative(b)) => Relative(b + a),
(Ratio(a), Length(b)) => Relative(a + b),
(Ratio(a), Ratio(b)) => Ratio(a + b),
- (Ratio(a), Relative(b)) => Relative(a + b),
+ (Ratio(a), Relative(b)) => Relative(b + a),
(Relative(a), Length(b)) => Relative(a + b),
(Relative(a), Ratio(b)) => Relative(a + b),
@@ -123,15 +123,15 @@ pub fn sub(lhs: Value, rhs: Value) -> StrResult<Value> {
(Angle(a), Angle(b)) => Angle(a - b),
(Length(a), Length(b)) => Length(a - b),
- (Length(a), Ratio(b)) => Relative(a - b),
- (Length(a), Relative(b)) => Relative(a - b),
+ (Length(a), Ratio(b)) => Relative(-b + a),
+ (Length(a), Relative(b)) => Relative(-b + a),
- (Ratio(a), Length(b)) => Relative(a - b),
+ (Ratio(a), Length(b)) => Relative(a + -b),
(Ratio(a), Ratio(b)) => Ratio(a - b),
- (Ratio(a), Relative(b)) => Relative(a - b),
+ (Ratio(a), Relative(b)) => Relative(-b + a),
- (Relative(a), Length(b)) => Relative(a - b),
- (Relative(a), Ratio(b)) => Relative(a - b),
+ (Relative(a), Length(b)) => Relative(a + -b),
+ (Relative(a), Ratio(b)) => Relative(a + -b),
(Relative(a), Relative(b)) => Relative(a - b),
(Fraction(a), Fraction(b)) => Fraction(a - b),
diff --git a/src/eval/value.rs b/src/eval/value.rs
index 774ae0b6..44df89e2 100644
--- a/src/eval/value.rs
+++ b/src/eval/value.rs
@@ -31,7 +31,7 @@ pub enum Value {
/// A ratio: `50%`.
Ratio(Ratio),
/// A relative length, combination of a ratio and a length: `20% + 5cm`.
- Relative(Relative),
+ Relative(Relative<Length>),
/// A fraction: `1fr`.
Fraction(Fraction),
/// A color value: `#f79143ff`.
@@ -549,7 +549,7 @@ primitive! { f64: "float", Float, Int(v) => v as f64 }
primitive! { Length: "length", Length }
primitive! { Angle: "angle", Angle }
primitive! { Ratio: "ratio", Ratio }
-primitive! { Relative: "relative length", Relative, Length(v) => v.into(), Ratio(v) => v.into() }
+primitive! { Relative<Length>: "relative length", Relative, Length(v) => v.into(), Ratio(v) => v.into() }
primitive! { Fraction: "fraction", Fraction }
primitive! { Color: "color", Color }
primitive! { EcoString: "string", Str }
diff --git a/src/export/pdf.rs b/src/export/pdf.rs
index 2550519b..95d20c51 100644
--- a/src/export/pdf.rs
+++ b/src/export/pdf.rs
@@ -17,7 +17,7 @@ use ttf_parser::{name_id, GlyphId, Tag};
use super::subset::subset;
use crate::font::{find_name, FaceId, FontStore};
use crate::frame::{Element, Frame, Geometry, Group, Shape, Stroke, Text};
-use crate::geom::{self, Color, Em, Length, Paint, Point, Size, Transform};
+use crate::geom::{self, Color, Em, Length, Numeric, Paint, Point, Size, Transform};
use crate::image::{Image, ImageId, ImageStore, RasterImage};
use crate::Context;
@@ -423,7 +423,7 @@ impl<'a> PageExporter<'a> {
}
fn write_group(&mut self, pos: Point, group: &Group) {
- let translation = Transform::translation(pos.x, pos.y);
+ let translation = Transform::translate(pos.x, pos.y);
self.save_state();
self.transform(translation.pre_concat(group.transform));
diff --git a/src/font.rs b/src/font.rs
index 46d6b8d7..e1d0c4e6 100644
--- a/src/font.rs
+++ b/src/font.rs
@@ -415,7 +415,7 @@ pub enum VerticalFontMetric {
Descender,
/// An font-size dependent distance from the baseline (positive goes up, negative
/// down).
- Relative(Relative),
+ Relative(Relative<Length>),
}
/// Properties of a single font face.
diff --git a/src/frame.rs b/src/frame.rs
index 7cc564cc..3c747f0d 100644
--- a/src/frame.rs
+++ b/src/frame.rs
@@ -4,7 +4,9 @@ use std::fmt::{self, Debug, Formatter};
use std::sync::Arc;
use crate::font::FaceId;
-use crate::geom::{Align, Em, Length, Paint, Path, Point, Size, Spec, Transform};
+use crate::geom::{
+ Align, Em, Length, Numeric, Paint, Path, Point, Size, Spec, Transform,
+};
use crate::image::ImageId;
/// A finished layout with elements at fixed positions.
diff --git a/src/geom/angle.rs b/src/geom/angle.rs
index b64ec77e..a0900ce5 100644
--- a/src/geom/angle.rs
+++ b/src/geom/angle.rs
@@ -10,6 +10,16 @@ impl Angle {
Self(Scalar(0.0))
}
+ /// Create an angle from a number of raw units.
+ pub const fn raw(raw: f64) -> Self {
+ Self(Scalar(raw))
+ }
+
+ /// Create an angle from a value in a unit.
+ pub fn with_unit(val: f64, unit: AngularUnit) -> Self {
+ Self(Scalar(val * unit.raw_scale()))
+ }
+
/// Create an angle from a number of radians.
pub fn rad(rad: f64) -> Self {
Self::with_unit(rad, AngularUnit::Rad)
@@ -20,9 +30,14 @@ impl Angle {
Self::with_unit(deg, AngularUnit::Deg)
}
- /// Create an angle from a number of raw units.
- pub const fn raw(raw: f64) -> Self {
- Self(Scalar(raw))
+ /// Get the value of this angle in raw units.
+ pub const fn to_raw(self) -> f64 {
+ (self.0).0
+ }
+
+ /// Get the value of this length in unit.
+ pub fn to_unit(self, unit: AngularUnit) -> f64 {
+ self.to_raw() / unit.raw_scale()
}
/// Convert this to a number of radians.
@@ -35,9 +50,9 @@ impl Angle {
self.to_unit(AngularUnit::Deg)
}
- /// Get the value of this angle in raw units.
- pub const fn to_raw(self) -> f64 {
- (self.0).0
+ /// The absolute value of the this angle.
+ pub fn abs(self) -> Self {
+ Self::raw(self.to_raw().abs())
}
/// Get the sine of this angle in radians.
@@ -49,20 +64,15 @@ impl Angle {
pub fn cos(self) -> f64 {
self.to_rad().cos()
}
+}
- /// Create an angle from a value in a unit.
- pub fn with_unit(val: f64, unit: AngularUnit) -> Self {
- Self(Scalar(val * unit.raw_scale()))
- }
-
- /// Get the value of this length in unit.
- pub fn to_unit(self, unit: AngularUnit) -> f64 {
- self.to_raw() / unit.raw_scale()
+impl Numeric for Angle {
+ fn zero() -> Self {
+ Self::zero()
}
- /// The absolute value of the this angle.
- pub fn abs(self) -> Self {
- Self::raw(self.to_raw().abs())
+ fn is_finite(self) -> bool {
+ self.0.is_finite()
}
}
@@ -132,6 +142,7 @@ impl Sum for Angle {
Self(iter.map(|s| s.0).sum())
}
}
+
/// Different units of angular measurement.
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub enum AngularUnit {
diff --git a/src/geom/em.rs b/src/geom/em.rs
index d1e90e04..3c772d96 100644
--- a/src/geom/em.rs
+++ b/src/geom/em.rs
@@ -32,19 +32,24 @@ impl Em {
Self(Scalar(length / font_size))
}
+ /// The number of em units.
+ pub const fn get(self) -> f64 {
+ (self.0).0
+ }
+
/// Convert to a length at the given font size.
pub fn resolve(self, font_size: Length) -> Length {
self.get() * font_size
}
+}
- /// The number of em units.
- pub const fn get(self) -> f64 {
- (self.0).0
+impl Numeric for Em {
+ fn zero() -> Self {
+ Self::zero()
}
- /// Whether the length is zero.
- pub fn is_zero(self) -> bool {
- self.0 == 0.0
+ fn is_finite(self) -> bool {
+ self.0.is_finite()
}
}
diff --git a/src/geom/fraction.rs b/src/geom/fraction.rs
index 7da85779..2f33a134 100644
--- a/src/geom/fraction.rs
+++ b/src/geom/fraction.rs
@@ -25,11 +25,6 @@ impl Fraction {
(self.0).0
}
- /// Whether the fraction is zero.
- pub fn is_zero(self) -> bool {
- self.0 == 0.0
- }
-
/// The absolute value of the this fraction.
pub fn abs(self) -> Self {
Self::new(self.get().abs())
@@ -46,6 +41,16 @@ impl Fraction {
}
}
+impl Numeric for Fraction {
+ fn zero() -> Self {
+ Self::zero()
+ }
+
+ fn is_finite(self) -> bool {
+ self.0.is_finite()
+ }
+}
+
impl Debug for Fraction {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}fr", round_2(self.get()))
diff --git a/src/geom/length.rs b/src/geom/length.rs
index c5fbfd76..838d33c0 100644
--- a/src/geom/length.rs
+++ b/src/geom/length.rs
@@ -15,6 +15,16 @@ impl Length {
Self(Scalar(f64::INFINITY))
}
+ /// Create a length from a number of raw units.
+ pub const fn raw(raw: f64) -> Self {
+ Self(Scalar(raw))
+ }
+
+ /// Create a length from a value in a unit.
+ pub fn with_unit(val: f64, unit: LengthUnit) -> Self {
+ Self(Scalar(val * unit.raw_scale()))
+ }
+
/// Create a length from a number of points.
pub fn pt(pt: f64) -> Self {
Self::with_unit(pt, LengthUnit::Pt)
@@ -35,9 +45,14 @@ impl Length {
Self::with_unit(inches, LengthUnit::In)
}
- /// Create a length from a number of raw units.
- pub const fn raw(raw: f64) -> Self {
- Self(Scalar(raw))
+ /// Get the value of this length in raw units.
+ pub const fn to_raw(self) -> f64 {
+ (self.0).0
+ }
+
+ /// Get the value of this length in unit.
+ pub fn to_unit(self, unit: LengthUnit) -> f64 {
+ self.to_raw() / unit.raw_scale()
}
/// Convert this to a number of points.
@@ -60,36 +75,6 @@ impl Length {
self.to_unit(LengthUnit::In)
}
- /// Get the value of this length in raw units.
- pub const fn to_raw(self) -> f64 {
- (self.0).0
- }
-
- /// Create a length from a value in a unit.
- pub fn with_unit(val: f64, unit: LengthUnit) -> Self {
- Self(Scalar(val * unit.raw_scale()))
- }
-
- /// Get the value of this length in unit.
- pub fn to_unit(self, unit: LengthUnit) -> f64 {
- self.to_raw() / unit.raw_scale()
- }
-
- /// Whether the length is zero.
- pub fn is_zero(self) -> bool {
- self.to_raw() == 0.0
- }
-
- /// Whether the length is finite.
- pub fn is_finite(self) -> bool {
- self.to_raw().is_finite()
- }
-
- /// Whether the length is infinite.
- pub fn is_infinite(self) -> bool {
- self.to_raw().is_infinite()
- }
-
/// The absolute value of the this length.
pub fn abs(self) -> Self {
Self::raw(self.to_raw().abs())
@@ -137,6 +122,16 @@ impl Length {
}
}
+impl Numeric for Length {
+ fn zero() -> Self {
+ Self::zero()
+ }
+
+ fn is_finite(self) -> bool {
+ self.0.is_finite()
+ }
+}
+
impl Debug for Length {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}pt", round_2(self.to_pt()))
diff --git a/src/geom/mod.rs b/src/geom/mod.rs
index 8d7759b4..bfb450a1 100644
--- a/src/geom/mod.rs
+++ b/src/geom/mod.rs
@@ -60,6 +60,30 @@ pub trait Get<Index> {
}
}
+/// A numeric type.
+pub trait Numeric:
+ Sized
+ + Debug
+ + Copy
+ + PartialEq
+ + Neg<Output = Self>
+ + Add<Output = Self>
+ + Sub<Output = Self>
+ + Mul<f64, Output = Self>
+ + Div<f64, Output = Self>
+{
+ /// The identity element.
+ fn zero() -> Self;
+
+ /// Whether `self` is the identity element.
+ fn is_zero(self) -> bool {
+ self == Self::zero()
+ }
+
+ /// Whether `self` contains only finite parts.
+ fn is_finite(self) -> bool;
+}
+
/// Round a float to two decimal places.
fn round_2(value: f64) -> f64 {
(value * 100.0).round() / 100.0
diff --git a/src/geom/paint.rs b/src/geom/paint.rs
index 8dee363c..3660d528 100644
--- a/src/geom/paint.rs
+++ b/src/geom/paint.rs
@@ -1,4 +1,3 @@
-use std::fmt::Display;
use std::str::FromStr;
use syntect::highlighting::Color as SynColor;
@@ -103,7 +102,7 @@ impl RgbaColor {
}
impl FromStr for RgbaColor {
- type Err = RgbaError;
+ type Err = &'static str;
/// Constructs a new color from hex strings like the following:
/// - `#aef` (shorthand, with leading hashtag),
@@ -113,8 +112,8 @@ impl FromStr for RgbaColor {
/// The hashtag is optional and both lower and upper case are fine.
fn from_str(hex_str: &str) -> Result<Self, Self::Err> {
let hex_str = hex_str.strip_prefix('#').unwrap_or(hex_str);
- if !hex_str.is_ascii() {
- return Err(RgbaError);
+ if hex_str.chars().any(|c| !c.is_ascii_hexdigit()) {
+ return Err("string contains non-hexadecimal letters");
}
let len = hex_str.len();
@@ -123,7 +122,7 @@ impl FromStr for RgbaColor {
let alpha = len == 4 || len == 8;
if !long && !short {
- return Err(RgbaError);
+ return Err("string has wrong length");
}
let mut values: [u8; 4] = [255; 4];
@@ -133,7 +132,7 @@ impl FromStr for RgbaColor {
let pos = elem * item_len;
let item = &hex_str[pos .. (pos + item_len)];
- values[elem] = u8::from_str_radix(item, 16).map_err(|_| RgbaError)?;
+ values[elem] = u8::from_str_radix(item, 16).unwrap();
if short {
// Duplicate number for shorthand notation, i.e. `a` -> `aa`
@@ -169,18 +168,6 @@ impl Debug for RgbaColor {
}
}
-/// The error when parsing an [`RgbaColor`] from a string fails.
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub struct RgbaError;
-
-impl Display for RgbaError {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.pad("invalid hex string")
- }
-}
-
-impl std::error::Error for RgbaError {}
-
/// An 8-bit CMYK color.
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct CmykColor {
@@ -268,13 +255,14 @@ mod tests {
#[test]
fn test_parse_invalid_colors() {
#[track_caller]
- fn test(hex: &str) {
- assert_eq!(RgbaColor::from_str(hex), Err(RgbaError));
+ fn test(hex: &str, message: &str) {
+ assert_eq!(RgbaColor::from_str(hex), Err(message));
}
- test("12345");
- test("a5");
- test("14B2AH");
- test("f075ff011");
+ test("a5", "string has wrong length");
+ test("12345", "string has wrong length");
+ test("f075ff011", "string has wrong length");
+ test("hmmm", "string contains non-hexadecimal letters");
+ test("14B2AH", "string contains non-hexadecimal letters");
}
}
diff --git a/src/geom/point.rs b/src/geom/point.rs
index 6d77507b..afce68ba 100644
--- a/src/geom/point.rs
+++ b/src/geom/point.rs
@@ -35,20 +35,25 @@ impl Point {
Self { x: Length::zero(), y }
}
- /// Whether both components are zero.
- pub fn is_zero(self) -> bool {
- self.x.is_zero() && self.y.is_zero()
- }
-
/// Transform the point with the given transformation.
- pub fn transform(self, transform: Transform) -> Self {
+ pub fn transform(self, ts: Transform) -> Self {
Self::new(
- transform.sx.resolve(self.x) + transform.kx.resolve(self.y) + transform.tx,
- transform.ky.resolve(self.x) + transform.sy.resolve(self.y) + transform.ty,
+ ts.sx.resolve(self.x) + ts.kx.resolve(self.y) + ts.tx,
+ ts.ky.resolve(self.x) + ts.sy.resolve(self.y) + ts.ty,
)
}
}
+impl Numeric for Point {
+ fn zero() -> Self {
+ Self::zero()
+ }
+
+ fn is_finite(self) -> bool {
+ self.x.is_finite() && self.y.is_finite()
+ }
+}
+
impl Get<SpecAxis> for Point {
type Component = Length;
diff --git a/src/geom/ratio.rs b/src/geom/ratio.rs
index d035c33e..7dca53c2 100644
--- a/src/geom/ratio.rs
+++ b/src/geom/ratio.rs
@@ -28,16 +28,6 @@ impl Ratio {
(self.0).0
}
- /// Resolve this relative to the given `length`.
- pub fn resolve(self, length: Length) -> Length {
- // We don't want NaNs.
- if length.is_infinite() {
- Length::zero()
- } else {
- self.get() * length
- }
- }
-
/// Whether the ratio is zero.
pub fn is_zero(self) -> bool {
self.0 == 0.0
@@ -52,6 +42,12 @@ impl Ratio {
pub fn abs(self) -> Self {
Self::new(self.get().abs())
}
+
+ /// Resolve this relative to the given `whole`.
+ pub fn resolve<T: Numeric>(self, whole: T) -> T {
+ let resolved = whole * self.get();
+ if resolved.is_finite() { resolved } else { T::zero() }
+ }
}
impl Debug for Ratio {
diff --git a/src/geom/relative.rs b/src/geom/relative.rs
index fa2ec7fc..8e8897e7 100644
--- a/src/geom/relative.rs
+++ b/src/geom/relative.rs
@@ -1,65 +1,60 @@
use super::*;
-/// A relative length.
+/// A value that is composed of a relative and an absolute part.
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct Relative {
+pub struct Relative<T: Numeric> {
/// The relative part.
pub rel: Ratio,
/// The absolute part.
- pub abs: Length,
+ pub abs: T,
}
-impl Relative {
- /// The zero relative length.
- pub const fn zero() -> Self {
- Self { rel: Ratio::zero(), abs: Length::zero() }
+impl<T: Numeric> Relative<T> {
+ /// The zero relative.
+ pub fn zero() -> Self {
+ Self { rel: Ratio::zero(), abs: T::zero() }
}
- /// A relative length with a ratio of `100%` and no absolute part.
- pub const fn one() -> Self {
- Self { rel: Ratio::one(), abs: Length::zero() }
+ /// A relative with a ratio of `100%` and no absolute part.
+ pub fn one() -> Self {
+ Self { rel: Ratio::one(), abs: T::zero() }
}
- /// Create a new relative length from its parts.
- pub const fn new(rel: Ratio, abs: Length) -> Self {
+ /// Create a new relative from its parts.
+ pub fn new(rel: Ratio, abs: T) -> Self {
Self { rel, abs }
}
- /// Resolve this length relative to the given `length`.
- pub fn resolve(self, length: Length) -> Length {
- self.rel.resolve(length) + self.abs
- }
-
/// Whether both parts are zero.
pub fn is_zero(self) -> bool {
self.rel.is_zero() && self.abs.is_zero()
}
- /// Whether there is a relative part.
- pub fn is_relative(self) -> bool {
- !self.rel.is_zero()
+ /// Resolve this relative to the given `whole`.
+ pub fn resolve(self, whole: T) -> T {
+ self.rel.resolve(whole) + self.abs
}
}
-impl Debug for Relative {
+impl<T: Numeric> Debug for Relative<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{:?} + {:?}", self.rel, self.abs)
}
}
-impl From<Length> for Relative {
- fn from(abs: Length) -> Self {
+impl<T: Numeric> From<T> for Relative<T> {
+ fn from(abs: T) -> Self {
Self { rel: Ratio::zero(), abs }
}
}
-impl From<Ratio> for Relative {
+impl<T: Numeric> From<Ratio> for Relative<T> {
fn from(rel: Ratio) -> Self {
- Self { rel, abs: Length::zero() }
+ Self { rel, abs: T::zero() }
}
}
-impl Neg for Relative {
+impl<T: Numeric> Neg for Relative<T> {
type Output = Self;
fn neg(self) -> Self {
@@ -67,10 +62,10 @@ impl Neg for Relative {
}
}
-impl Add for Relative {
+impl<T: Numeric> Add for Relative<T> {
type Output = Self;
- fn add(self, other: Self) -> Self {
+ fn add(self, other: Self) -> Self::Output {
Self {
rel: self.rel + other.rel,
abs: self.abs + other.abs,
@@ -78,97 +73,88 @@ impl Add for Relative {
}
}
-impl Add<Ratio> for Length {
- type Output = Relative;
+impl<T: Numeric> Sub for Relative<T> {
+ type Output = Self;
- fn add(self, other: Ratio) -> Relative {
- Relative { rel: other, abs: self }
+ fn sub(self, other: Self) -> Self::Output {
+ self + -other
}
}
-impl Add<Length> for Ratio {
- type Output = Relative;
+impl<T: Numeric> Mul<f64> for Relative<T> {
+ type Output = Self;
- fn add(self, other: Length) -> Relative {
- other + self
+ fn mul(self, other: f64) -> Self::Output {
+ Self {
+ rel: self.rel * other,
+ abs: self.abs * other,
+ }
}
}
-impl Add<Length> for Relative {
- type Output = Self;
+impl<T: Numeric> Mul<Relative<T>> for f64 {
+ type Output = Relative<T>;
- fn add(self, other: Length) -> Self {
- Self { rel: self.rel, abs: self.abs + other }
+ fn mul(self, other: Relative<T>) -> Self::Output {
+ other * self
}
}
-impl Add<Relative> for Length {
- type Output = Relative;
+impl<T: Numeric> Div<f64> for Relative<T> {
+ type Output = Self;
- fn add(self, other: Relative) -> Relative {
- other + self
+ fn div(self, other: f64) -> Self::Output {
+ Self {
+ rel: self.rel / other,
+ abs: self.abs / other,
+ }
}
}
-impl Add<Ratio> for Relative {
- type Output = Self;
-
- fn add(self, other: Ratio) -> Self {
- Self { rel: self.rel + other, abs: self.abs }
+impl<T: Numeric> AddAssign for Relative<T> {
+ fn add_assign(&mut self, other: Self) {
+ *self = *self + other;
}
}
-impl Add<Relative> for Ratio {
- type Output = Relative;
+impl<T: Numeric> SubAssign for Relative<T> {
+ fn sub_assign(&mut self, other: Self) {
+ *self = *self - other;
+ }
+}
- fn add(self, other: Relative) -> Relative {
- other + self
+impl<T: Numeric> MulAssign<f64> for Relative<T> {
+ fn mul_assign(&mut self, other: f64) {
+ *self = *self * other;
}
}
-sub_impl!(Relative - Relative -> Relative);
-sub_impl!(Length - Ratio -> Relative);
-sub_impl!(Ratio - Length -> Relative);
-sub_impl!(Relative - Length -> Relative);
-sub_impl!(Length - Relative -> Relative);
-sub_impl!(Relative - Ratio -> Relative);
-sub_impl!(Ratio - Relative -> Relative);
+impl<T: Numeric> DivAssign<f64> for Relative<T> {
+ fn div_assign(&mut self, other: f64) {
+ *self = *self * other;
+ }
+}
-impl Mul<f64> for Relative {
- type Output = Self;
+impl<T: Numeric> Add<T> for Ratio {
+ type Output = Relative<T>;
- fn mul(self, other: f64) -> Self {
- Self {
- rel: self.rel * other,
- abs: self.abs * other,
- }
+ fn add(self, other: T) -> Self::Output {
+ Relative::from(self) + Relative::from(other)
}
}
-impl Mul<Relative> for f64 {
- type Output = Relative;
+impl<T: Numeric> Add<T> for Relative<T> {
+ type Output = Self;
- fn mul(self, other: Relative) -> Relative {
- other * self
+ fn add(self, other: T) -> Self::Output {
+ self + Relative::from(other)
}
}
-impl Div<f64> for Relative {
+impl<T: Numeric> Add<Ratio> for Relative<T> {
type Output = Self;
- fn div(self, other: f64) -> Self {
- Self {
- rel: self.rel / other,
- abs: self.abs / other,
- }
+ fn add(self, other: Ratio) -> Self::Output {
+ self + Relative::from(other)
}
}
-
-assign_impl!(Relative += Relative);
-assign_impl!(Relative += Length);
-assign_impl!(Relative += Ratio);
-assign_impl!(Relative -= Relative);
-assign_impl!(Relative -= Length);
-assign_impl!(Relative -= Ratio);
-assign_impl!(Relative *= f64);
-assign_impl!(Relative /= f64);
diff --git a/src/geom/scalar.rs b/src/geom/scalar.rs
index 1435654d..91225a2b 100644
--- a/src/geom/scalar.rs
+++ b/src/geom/scalar.rs
@@ -6,6 +6,16 @@ use super::*;
#[derive(Default, Copy, Clone)]
pub struct Scalar(pub f64);
+impl Numeric for Scalar {
+ fn zero() -> Self {
+ Self(0.0)
+ }
+
+ fn is_finite(self) -> bool {
+ self.0.is_finite()
+ }
+}
+
impl From<f64> for Scalar {
fn from(float: f64) -> Self {
Self(float)
diff --git a/src/geom/sides.rs b/src/geom/sides.rs
index 45420c18..4539728f 100644
--- a/src/geom/sides.rs
+++ b/src/geom/sides.rs
@@ -43,7 +43,7 @@ where
}
}
-impl Sides<Relative> {
+impl Sides<Relative<Length>> {
/// Resolve the sides relative to the given `size`.
pub fn resolve(self, size: Size) -> Sides<Length> {
Sides {
diff --git a/src/geom/spec.rs b/src/geom/spec.rs
index 31f93a65..3fe1793a 100644
--- a/src/geom/spec.rs
+++ b/src/geom/spec.rs
@@ -201,7 +201,7 @@ pub type Size = Spec<Length>;
impl Size {
/// The zero value.
- pub fn zero() -> Self {
+ pub const fn zero() -> Self {
Self { x: Length::zero(), y: Length::zero() }
}
@@ -210,24 +210,19 @@ impl Size {
self.x.fits(other.x) && self.y.fits(other.y)
}
- /// Whether both components are zero.
- pub fn is_zero(self) -> bool {
- self.x.is_zero() && self.y.is_zero()
- }
-
- /// Whether both components are finite.
- pub fn is_finite(self) -> bool {
- self.x.is_finite() && self.y.is_finite()
+ /// Convert to a point.
+ pub fn to_point(self) -> Point {
+ Point::new(self.x, self.y)
}
+}
- /// Whether any of the two components is infinite.
- pub fn is_infinite(self) -> bool {
- self.x.is_infinite() || self.y.is_infinite()
+impl Numeric for Size {
+ fn zero() -> Self {
+ Self::zero()
}
- /// Convert to a point.
- pub fn to_point(self) -> Point {
- Point::new(self.x, self.y)
+ fn is_finite(self) -> bool {
+ self.x.is_finite() && self.y.is_finite()
}
}
diff --git a/src/geom/transform.rs b/src/geom/transform.rs
index 8d64ebcf..c0a06e33 100644
--- a/src/geom/transform.rs
+++ b/src/geom/transform.rs
@@ -24,18 +24,18 @@ impl Transform {
}
}
- /// A translation transform.
- pub const fn translation(tx: Length, ty: Length) -> Self {
+ /// A translate transform.
+ pub const fn translate(tx: Length, ty: Length) -> Self {
Self { tx, ty, ..Self::identity() }
}
- /// A scaling transform.
+ /// A scale transform.
pub const fn scale(sx: Ratio, sy: Ratio) -> Self {
Self { sx, sy, ..Self::identity() }
}
- /// A rotation transform.
- pub fn rotation(angle: Angle) -> Self {
+ /// A rotate transform.
+ pub fn rotate(angle: Angle) -> Self {
let cos = Ratio::new(angle.cos());
let sin = Ratio::new(angle.sin());
Self {
diff --git a/src/library/graphics/line.rs b/src/library/graphics/line.rs
index 1ca25bd9..571506c1 100644
--- a/src/library/graphics/line.rs
+++ b/src/library/graphics/line.rs
@@ -3,8 +3,10 @@ use crate::library::prelude::*;
/// Display a line without affecting the layout.
#[derive(Debug, Hash)]
pub struct LineNode {
- origin: Spec<Relative>,
- delta: Spec<Relative>,
+ /// Where the line starts.
+ origin: Spec<Relative<Length>>,
+ /// The offset from the `origin` where the line ends.
+ delta: Spec<Relative<Length>>,
}
#[node]
@@ -15,14 +17,15 @@ impl LineNode {
pub const THICKNESS: Length = Length::pt(1.0);
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
- let origin = args.named::<Spec<Relative>>("origin")?.unwrap_or_default();
- let delta = match args.named::<Spec<Relative>>("to")? {
+ let origin = args.named("origin")?.unwrap_or_default();
+ let delta = match args.named::<Spec<Relative<Length>>>("to")? {
Some(to) => to.zip(origin).map(|(to, from)| to - from),
None => {
- let length =
- args.named::<Relative>("length")?.unwrap_or(Length::cm(1.0).into());
- let angle = args.named::<Angle>("angle")?.unwrap_or_default();
+ let length = args
+ .named::<Relative<Length>>("length")?
+ .unwrap_or(Length::cm(1.0).into());
+ let angle = args.named::<Angle>("angle")?.unwrap_or_default();
let x = angle.cos() * length;
let y = angle.sin() * length;
diff --git a/src/library/graphics/shape.rs b/src/library/graphics/shape.rs
index 3f338d1a..9faa4c52 100644
--- a/src/library/graphics/shape.rs
+++ b/src/library/graphics/shape.rs
@@ -28,7 +28,7 @@ impl<const S: ShapeKind> ShapeNode<S> {
/// The stroke's thickness.
pub const THICKNESS: Length = Length::pt(1.0);
/// How much to pad the shape's content.
- pub const PADDING: Relative = Relative::zero();
+ pub const PADDING: Relative<Length> = Relative::zero();
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
let size = match S {
diff --git a/src/library/graphics/transform.rs b/src/library/graphics/transform.rs
index 1ce91f28..eb419a7e 100644
--- a/src/library/graphics/transform.rs
+++ b/src/library/graphics/transform.rs
@@ -29,11 +29,11 @@ impl<const T: TransformKind> TransformNode<T> {
MOVE => {
let tx = args.named("x")?.unwrap_or_default();
let ty = args.named("y")?.unwrap_or_default();
- Transform::translation(tx, ty)
+ Transform::translate(tx, ty)
}
ROTATE => {
let angle = args.named_or_find("angle")?.unwrap_or_default();
- Transform::rotation(angle)
+ Transform::rotate(angle)
}
SCALE | _ => {
let all = args.find()?;
@@ -62,9 +62,9 @@ impl<const T: TransformKind> Layout for TransformNode<T> {
for frame in &mut frames {
let Spec { x, y } = origin.zip(frame.size).map(|(o, s)| o.resolve(s));
- let transform = Transform::translation(x, y)
+ let transform = Transform::translate(x, y)
.pre_concat(self.transform)
- .pre_concat(Transform::translation(-x, -y));
+ .pre_concat(Transform::translate(-x, -y));
Arc::make_mut(frame).transform(transform);
}
diff --git a/src/library/layout/columns.rs b/src/library/layout/columns.rs
index 56e55d57..1cb45c37 100644
--- a/src/library/layout/columns.rs
+++ b/src/library/layout/columns.rs
@@ -14,7 +14,7 @@ pub struct ColumnsNode {
#[node]
impl ColumnsNode {
/// The size of the gutter space between each column.
- pub const GUTTER: Relative = Ratio::new(0.04).into();
+ pub const GUTTER: Relative<Length> = Ratio::new(0.04).into();
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
Ok(Content::block(Self {
@@ -33,7 +33,7 @@ impl Layout for ColumnsNode {
) -> TypResult<Vec<Arc<Frame>>> {
// Separating the infinite space into infinite columns does not make
// much sense.
- if regions.first.x.is_infinite() {
+ if !regions.first.x.is_finite() {
return self.child.layout(ctx, regions, styles);
}
diff --git a/src/library/layout/grid.rs b/src/library/layout/grid.rs
index ee485bd2..b1e5e54c 100644
--- a/src/library/layout/grid.rs
+++ b/src/library/layout/grid.rs
@@ -58,7 +58,7 @@ pub enum TrackSizing {
Auto,
/// A track size specified in absolute terms and relative to the parent's
/// size.
- Relative(Relative),
+ Relative(Relative<Length>),
/// A track size specified as a fraction of the remaining free space in the
/// parent.
Fractional(Fraction),
@@ -422,7 +422,7 @@ impl<'a> GridLayouter<'a> {
fn layout_relative_row(
&mut self,
ctx: &mut Context,
- v: Relative,
+ v: Relative<Length>,
y: usize,
) -> TypResult<()> {
let resolved = v.resolve(self.regions.base.y);
diff --git a/src/library/layout/pad.rs b/src/library/layout/pad.rs
index 1ec5f124..b7470540 100644
--- a/src/library/layout/pad.rs
+++ b/src/library/layout/pad.rs
@@ -4,7 +4,7 @@ use crate::library::prelude::*;
#[derive(Debug, Hash)]
pub struct PadNode {
/// The amount of padding.
- pub padding: Sides<Relative>,
+ pub padding: Sides<Relative<Length>>,
/// The child node whose sides to pad.
pub child: LayoutNode,
}
@@ -54,7 +54,7 @@ impl Layout for PadNode {
}
/// Shrink a size by padding relative to the size itself.
-fn shrink(size: Size, padding: Sides<Relative>) -> Size {
+fn shrink(size: Size, padding: Sides<Relative<Length>>) -> Size {
size - padding.resolve(size).sum_by_axis()
}
@@ -77,7 +77,7 @@ fn shrink(size: Size, padding: Sides<Relative>) -> Size {
/// <=> w - p.rel * w - p.abs = s
/// <=> (1 - p.rel) * w = s + p.abs
/// <=> w = (s + p.abs) / (1 - p.rel)
-fn grow(size: Size, padding: Sides<Relative>) -> Size {
+fn grow(size: Size, padding: Sides<Relative<Length>>) -> Size {
size.zip(padding.sum_by_axis())
.map(|(s, p)| (s + p.abs).safe_div(1.0 - p.rel.get()))
}
diff --git a/src/library/layout/page.rs b/src/library/layout/page.rs
index abe1786f..37a87ae2 100644
--- a/src/library/layout/page.rs
+++ b/src/library/layout/page.rs
@@ -16,13 +16,13 @@ impl PageNode {
/// Whether the page is flipped into landscape orientation.
pub const FLIPPED: bool = false;
/// The left margin.
- pub const LEFT: Smart<Relative> = Smart::Auto;
+ pub const LEFT: Smart<Relative<Length>> = Smart::Auto;
/// The right margin.
- pub const RIGHT: Smart<Relative> = Smart::Auto;
+ pub const RIGHT: Smart<Relative<Length>> = Smart::Auto;
/// The top margin.
- pub const TOP: Smart<Relative> = Smart::Auto;
+ pub const TOP: Smart<Relative<Length>> = Smart::Auto;
/// The bottom margin.
- pub const BOTTOM: Smart<Relative> = Smart::Auto;
+ pub const BOTTOM: Smart<Relative<Length>> = Smart::Auto;
/// The page's background color.
pub const FILL: Option<Paint> = None;
/// How many columns the page has.
@@ -85,7 +85,7 @@ impl PageNode {
}
let mut min = width.min(height);
- if min.is_infinite() {
+ if !min.is_finite() {
min = Paper::A4.width();
}
@@ -115,7 +115,7 @@ impl PageNode {
}
// Layout the child.
- let regions = Regions::repeat(size, size, size.map(Length::is_finite));
+ let regions = Regions::repeat(size, size, size.map(Numeric::is_finite));
let mut frames = child.layout(ctx, &regions, styles)?;
let header = styles.get(Self::HEADER);
@@ -133,7 +133,7 @@ impl PageNode {
let pos = Point::new(padding.left, y);
let w = size.x - padding.left - padding.right;
let area = Size::new(w, h);
- let pod = Regions::one(area, area, area.map(Length::is_finite));
+ let pod = Regions::one(area, area, area.map(Numeric::is_finite));
let sub = Layout::layout(&content, ctx, &pod, styles)?.remove(0);
Arc::make_mut(frame).push_frame(pos, sub);
}
diff --git a/src/library/layout/spacing.rs b/src/library/layout/spacing.rs
index 633093e9..e9837ef5 100644
--- a/src/library/layout/spacing.rs
+++ b/src/library/layout/spacing.rs
@@ -24,7 +24,7 @@ impl VNode {
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Spacing {
/// Spacing specified in absolute terms and relative to the parent's size.
- Relative(Relative),
+ Relative(Relative<Length>),
/// Spacing specified as a fraction of the remaining free space in the parent.
Fractional(Fraction),
}
diff --git a/src/library/mod.rs b/src/library/mod.rs
index af9ab575..7c5a519f 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -167,16 +167,13 @@ castable! {
}
castable! {
- Spec<Relative>,
+ Spec<Relative<Length>>,
Expected: "array of two relative lengths",
Value::Array(array) => {
- match array.as_slice() {
- [a, b] => {
- let a = a.clone().cast::<Relative>()?;
- let b = b.clone().cast::<Relative>()?;
- Spec::new(a, b)
- },
- _ => return Err("point array must contain exactly two entries".to_string()),
+ let mut iter = array.into_iter();
+ match (iter.next(), iter.next(), iter.next()) {
+ (Some(a), Some(b), None) => Spec::new(a.cast()?, b.cast()?),
+ _ => Err("point array must contain exactly two entries")?,
}
},
}
diff --git a/src/library/structure/list.rs b/src/library/structure/list.rs
index 532ec625..c58e8648 100644
--- a/src/library/structure/list.rs
+++ b/src/library/structure/list.rs
@@ -34,11 +34,11 @@ impl<const L: ListKind> ListNode<L> {
#[property(referenced)]
pub const LABEL: Label = Label::Default;
/// The spacing between the list items of a non-wide list.
- pub const SPACING: Relative = Relative::zero();
+ pub const SPACING: Relative<Length> = Relative::zero();
/// The indentation of each item's label.
- pub const INDENT: Relative = Ratio::new(0.0).into();
+ pub const INDENT: Relative<Length> = Ratio::new(0.0).into();
/// The space between the label and the body of each item.
- pub const BODY_INDENT: Relative = Ratio::new(0.5).into();
+ pub const BODY_INDENT: Relative<Length> = Ratio::new(0.5).into();
/// The extra padding above the list.
pub const ABOVE: Length = Length::zero();
/// The extra padding below the list.
diff --git a/src/library/structure/table.rs b/src/library/structure/table.rs
index 9317e43f..e01ae908 100644
--- a/src/library/structure/table.rs
+++ b/src/library/structure/table.rs
@@ -23,7 +23,7 @@ impl TableNode {
/// The stroke's thickness.
pub const THICKNESS: Length = Length::pt(1.0);
/// How much to pad the cells's content.
- pub const PADDING: Relative = Length::pt(5.0).into();
+ pub const PADDING: Relative<Length> = Length::pt(5.0).into();
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
let columns = args.named("columns")?.unwrap_or_default();
diff --git a/src/library/text/deco.rs b/src/library/text/deco.rs
index a81f0374..da1a1141 100644
--- a/src/library/text/deco.rs
+++ b/src/library/text/deco.rs
@@ -26,13 +26,13 @@ impl<const L: DecoLine> DecoNode<L> {
/// Thickness of the line's strokes (dependent on scaled font size), read
/// from the font tables if `None`.
#[property(shorthand)]
- pub const THICKNESS: Option<Relative> = None;
+ pub const THICKNESS: Option<Relative<Length>> = None;
/// Position of the line relative to the baseline (dependent on scaled font
/// size), read from the font tables if `None`.
- pub const OFFSET: Option<Relative> = None;
+ pub const OFFSET: Option<Relative<Length>> = None;
/// Amount that the line will be longer or shorter than its associated text
/// (dependent on scaled font size).
- pub const EXTENT: Relative = Relative::zero();
+ pub const EXTENT: Relative<Length> = Relative::zero();
/// Whether the line skips sections in which it would collide
/// with the glyphs. Does not apply to strikethrough.
pub const EVADE: bool = true;
@@ -66,9 +66,9 @@ impl<const L: DecoLine> Show for DecoNode<L> {
pub struct Decoration {
pub line: DecoLine,
pub stroke: Option<Paint>,
- pub thickness: Option<Relative>,
- pub offset: Option<Relative>,
- pub extent: Relative,
+ pub thickness: Option<Relative<Length>>,
+ pub offset: Option<Relative<Length>>,
+ pub extent: Relative<Length>,
pub evade: bool,
}
diff --git a/src/library/text/mod.rs b/src/library/text/mod.rs
index 975a4805..4a139fb3 100644
--- a/src/library/text/mod.rs
+++ b/src/library/text/mod.rs
@@ -188,7 +188,7 @@ castable! {
/// The size of text.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct FontSize(pub Relative);
+pub struct FontSize(pub Relative<Length>);
impl Fold for FontSize {
type Output = Length;
diff --git a/src/library/text/par.rs b/src/library/text/par.rs
index a05aff44..2d31cd11 100644
--- a/src/library/text/par.rs
+++ b/src/library/text/par.rs
@@ -42,11 +42,11 @@ impl ParNode {
/// will will be hyphenated if and only if justification is enabled.
pub const HYPHENATE: Smart<bool> = Smart::Auto;
/// The spacing between lines (dependent on scaled font size).
- pub const LEADING: Relative = Ratio::new(0.65).into();
+ pub const LEADING: Relative<Length> = Ratio::new(0.65).into();
/// The extra spacing between paragraphs (dependent on scaled font size).
- pub const SPACING: Relative = Ratio::new(0.55).into();
+ pub const SPACING: Relative<Length> = Ratio::new(0.55).into();
/// The indent the first line of a consecutive paragraph should have.
- pub const INDENT: Relative = Relative::zero();
+ pub const INDENT: Relative<Length> = Relative::zero();
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
// The paragraph constructor is special: It doesn't create a paragraph
diff --git a/src/library/utility/color.rs b/src/library/utility/color.rs
index 409af177..75410380 100644
--- a/src/library/utility/color.rs
+++ b/src/library/utility/color.rs
@@ -8,7 +8,7 @@ pub fn rgb(_: &mut Context, args: &mut Args) -> TypResult<Value> {
if let Some(string) = args.find::<Spanned<EcoString>>()? {
match RgbaColor::from_str(&string.v) {
Ok(color) => color,
- Err(_) => bail!(string.span, "invalid hex string"),
+ Err(msg) => bail!(string.span, msg),
}
} else {
struct Component(u8);