mirror of
https://github.com/typst/typst
synced 2025-05-19 11:35:27 +08:00
Use FromStr trait and formatting ✨
This commit is contained in:
parent
1683ca87f7
commit
e96f3830f1
@ -3,6 +3,7 @@
|
|||||||
use std::fmt::{self, Write, Debug, Formatter};
|
use std::fmt::{self, Write, Debug, Formatter};
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
use std::str::FromStr;
|
||||||
use std::u8;
|
use std::u8;
|
||||||
|
|
||||||
use crate::error::Errors;
|
use crate::error::Errors;
|
||||||
@ -141,11 +142,16 @@ impl RgbaColor {
|
|||||||
RgbaColor { r, g, b, a, healed: true }
|
RgbaColor { r, g, b, a, healed: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for RgbaColor {
|
||||||
|
type Err = ParseColorError;
|
||||||
|
|
||||||
/// Constructs a new color from a hex string like `7a03c2`.
|
/// Constructs a new color from a hex string like `7a03c2`.
|
||||||
/// Do not specify a leading `#`.
|
/// Do not specify a leading `#`.
|
||||||
pub fn from_str(hex_str: &str) -> Option<RgbaColor> {
|
fn from_str(hex_str: &str) -> Result<RgbaColor, Self::Err> {
|
||||||
if !hex_str.is_ascii() {
|
if !hex_str.is_ascii() {
|
||||||
return None;
|
return Err(ParseColorError);
|
||||||
}
|
}
|
||||||
|
|
||||||
let len = hex_str.len();
|
let len = hex_str.len();
|
||||||
@ -154,7 +160,7 @@ impl RgbaColor {
|
|||||||
let alpha = len == 4 || len == 8;
|
let alpha = len == 4 || len == 8;
|
||||||
|
|
||||||
if !long && !short {
|
if !long && !short {
|
||||||
return None;
|
return Err(ParseColorError);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut values: [u8; 4] = [255; 4];
|
let mut values: [u8; 4] = [255; 4];
|
||||||
@ -164,7 +170,8 @@ impl RgbaColor {
|
|||||||
let pos = elem * item_len;
|
let pos = elem * item_len;
|
||||||
|
|
||||||
let item = &hex_str[pos..(pos+item_len)];
|
let item = &hex_str[pos..(pos+item_len)];
|
||||||
values[elem] = u8::from_str_radix(item, 16).ok()?;
|
values[elem] = u8::from_str_radix(item, 16)
|
||||||
|
.map_err(|_| ParseColorError)?;
|
||||||
|
|
||||||
if short {
|
if short {
|
||||||
// Duplicate number for shorthand notation, i.e. `a` -> `aa`
|
// Duplicate number for shorthand notation, i.e. `a` -> `aa`
|
||||||
@ -172,9 +179,7 @@ impl RgbaColor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(
|
Ok(RgbaColor::new(values[0], values[1], values[2], values[3]))
|
||||||
RgbaColor::new(values[0], values[1], values[2], values[3])
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,24 +187,35 @@ impl Debug for RgbaColor {
|
|||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
if f.alternate() {
|
if f.alternate() {
|
||||||
f.write_str("rgba(")?;
|
f.write_str("rgba(")?;
|
||||||
f.write_fmt(format_args!("r: {:02}, ", self.r))?;
|
write!(f, "r: {:02}, ", self.r)?;
|
||||||
f.write_fmt(format_args!("g: {:02}, ", self.g))?;
|
write!(f, "g: {:02}, ", self.g)?;
|
||||||
f.write_fmt(format_args!("b: {:02}, ", self.b))?;
|
write!(f, "b: {:02}, ", self.b)?;
|
||||||
f.write_fmt(format_args!("a: {:02}", self.a))?;
|
write!(f, "a: {:02}", self.a)?;
|
||||||
f.write_char(')')?;
|
f.write_char(')')?;
|
||||||
} else {
|
} else {
|
||||||
f.write_char('#')?;
|
f.write_char('#')?;
|
||||||
f.write_fmt(format_args!("{:02x}", self.r))?;
|
write!(f, "{:02x}", self.r)?;
|
||||||
f.write_fmt(format_args!("{:02x}", self.g))?;
|
write!(f, "{:02x}", self.g)?;
|
||||||
f.write_fmt(format_args!("{:02x}", self.b))?;
|
write!(f, "{:02x}", self.b)?;
|
||||||
f.write_fmt(format_args!("{:02x}", self.a))?;
|
write!(f, "{:02x}", self.a)?;
|
||||||
}
|
}
|
||||||
if self.healed {
|
if self.healed {
|
||||||
f.write_fmt(format_args!(" [healed]"))
|
f.write_fmt(format_args!(" [healed]"))?;
|
||||||
} else {
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The error returned when parsing a [`RgbaColor`] from a string fails.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
pub struct ParseColorError;
|
||||||
|
|
||||||
|
impl std::error::Error for ParseColorError {}
|
||||||
|
|
||||||
|
impl fmt::Display for ParseColorError {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
f.write_str("invalid color")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An untyped sequence of expressions.
|
/// An untyped sequence of expressions.
|
||||||
@ -296,7 +312,7 @@ impl Debug for Tuple {
|
|||||||
/// ```typst
|
/// ```typst
|
||||||
/// hsl(93, 10, 19.4)
|
/// hsl(93, 10, 19.4)
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct NamedTuple {
|
pub struct NamedTuple {
|
||||||
/// The name of the tuple and where it is in the user source.
|
/// The name of the tuple and where it is in the user source.
|
||||||
pub name: Spanned<Ident>,
|
pub name: Spanned<Ident>,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
//! Parsing of source code into syntax models.
|
//! Parsing of source code into syntax models.
|
||||||
|
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::{Pass, Feedback};
|
use crate::{Pass, Feedback};
|
||||||
use super::func::{FuncHeader, FuncArgs, FuncArg};
|
use super::func::{FuncHeader, FuncArgs, FuncArg};
|
||||||
@ -253,12 +254,11 @@ impl<'s> FuncParser<'s> {
|
|||||||
Token::ExprSize(s) => take!(Expr::Size(s)),
|
Token::ExprSize(s) => take!(Expr::Size(s)),
|
||||||
Token::ExprBool(b) => take!(Expr::Bool(b)),
|
Token::ExprBool(b) => take!(Expr::Bool(b)),
|
||||||
Token::ExprHex(s) => {
|
Token::ExprHex(s) => {
|
||||||
if let Some(color) = RgbaColor::from_str(s) {
|
if let Ok(color) = RgbaColor::from_str(s) {
|
||||||
take!(Expr::Color(color))
|
take!(Expr::Color(color))
|
||||||
} else {
|
} else {
|
||||||
// Heal color by assuming black
|
// Heal color by assuming black
|
||||||
self.feedback.errors.push(err!(first.span;
|
self.feedback.errors.push(err!(first.span; "invalid color"));
|
||||||
"invalid color"));
|
|
||||||
take!(Expr::Color(RgbaColor::new_healed(0, 0, 0, 255)))
|
take!(Expr::Color(RgbaColor::new_healed(0, 0, 0, 255)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -284,9 +284,8 @@ impl<'s> FuncParser<'s> {
|
|||||||
/// Parse a tuple expression: `name(<expr>, ...)` with a given identifier.
|
/// Parse a tuple expression: `name(<expr>, ...)` with a given identifier.
|
||||||
fn parse_named_tuple(&mut self, name: Spanned<Ident>) -> Spanned<NamedTuple> {
|
fn parse_named_tuple(&mut self, name: Spanned<Ident>) -> Spanned<NamedTuple> {
|
||||||
let tuple = self.parse_tuple();
|
let tuple = self.parse_tuple();
|
||||||
let start = name.span.start;
|
let span = Span::merge(name.span, tuple.span);
|
||||||
let end = tuple.span.end;
|
Spanned::new(NamedTuple::new(name, tuple), span)
|
||||||
Spanned::new(NamedTuple::new(name, tuple), Span::new(start, end))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an object expression: `{ <key>: <value>, ... }`.
|
/// Parse an object expression: `{ <key>: <value>, ... }`.
|
||||||
@ -548,20 +547,17 @@ mod tests {
|
|||||||
fn Str(text: &str) -> Expr { Expr::Str(text.to_string()) }
|
fn Str(text: &str) -> Expr { Expr::Str(text.to_string()) }
|
||||||
fn Pt(points: f32) -> Expr { Expr::Size(Size::pt(points)) }
|
fn Pt(points: f32) -> Expr { Expr::Size(Size::pt(points)) }
|
||||||
|
|
||||||
fn ClrS(color: &str) -> Expr {
|
|
||||||
Expr::Color(
|
|
||||||
RgbaColor::from_str(color).expect("Test color invalid")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
fn ClrS_Healed() -> Expr {
|
|
||||||
let mut c = RgbaColor::from_str("000f")
|
|
||||||
.expect("Test color invalid");
|
|
||||||
c.healed = true;
|
|
||||||
Expr::Color(c)
|
|
||||||
}
|
|
||||||
fn Clr(r: u8, g: u8, b: u8, a: u8) -> Expr {
|
fn Clr(r: u8, g: u8, b: u8, a: u8) -> Expr {
|
||||||
Expr::Color(RgbaColor::new(r, g, b, a))
|
Expr::Color(RgbaColor::new(r, g, b, a))
|
||||||
}
|
}
|
||||||
|
fn ClrStr(color: &str) -> Expr {
|
||||||
|
Expr::Color(RgbaColor::from_str(color).expect("invalid test color"))
|
||||||
|
}
|
||||||
|
fn ClrStrHealed() -> Expr {
|
||||||
|
let mut c = RgbaColor::from_str("000f").expect("invalid test color");
|
||||||
|
c.healed = true;
|
||||||
|
Expr::Color(c)
|
||||||
|
}
|
||||||
|
|
||||||
fn T(text: &str) -> Node { Node::Text(text.to_string()) }
|
fn T(text: &str) -> Node { Node::Text(text.to_string()) }
|
||||||
|
|
||||||
@ -665,11 +661,11 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_color_strings() {
|
fn parse_color_strings() {
|
||||||
assert_eq!(Clr(0xf6, 0x12, 0x43, 0xff), ClrS("f61243ff"));
|
assert_eq!(Clr(0xf6, 0x12, 0x43, 0xff), ClrStr("f61243ff"));
|
||||||
assert_eq!(Clr(0xb3, 0xd8, 0xb3, 0xff), ClrS("b3d8b3"));
|
assert_eq!(Clr(0xb3, 0xd8, 0xb3, 0xff), ClrStr("b3d8b3"));
|
||||||
assert_eq!(Clr(0xfc, 0xd2, 0xa9, 0xad), ClrS("fCd2a9AD"));
|
assert_eq!(Clr(0xfc, 0xd2, 0xa9, 0xad), ClrStr("fCd2a9AD"));
|
||||||
assert_eq!(Clr(0x22, 0x33, 0x33, 0xff), ClrS("233"));
|
assert_eq!(Clr(0x22, 0x33, 0x33, 0xff), ClrStr("233"));
|
||||||
assert_eq!(Clr(0x11, 0x11, 0x11, 0xbb), ClrS("111b"));
|
assert_eq!(Clr(0x11, 0x11, 0x11, 0xbb), ClrStr("111b"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -816,7 +812,7 @@ mod tests {
|
|||||||
p!("[val: 3.14]" => [func!("val": (Num(3.14)), {})]);
|
p!("[val: 3.14]" => [func!("val": (Num(3.14)), {})]);
|
||||||
p!("[val: 4.5cm]" => [func!("val": (Sz(Size::cm(4.5))), {})]);
|
p!("[val: 4.5cm]" => [func!("val": (Sz(Size::cm(4.5))), {})]);
|
||||||
p!("[val: 12e1pt]" => [func!("val": (Pt(12e1)), {})]);
|
p!("[val: 12e1pt]" => [func!("val": (Pt(12e1)), {})]);
|
||||||
p!("[val: #f7a20500]" => [func!("val": (ClrS("f7a20500")), {})]);
|
p!("[val: #f7a20500]" => [func!("val": (ClrStr("f7a20500")), {})]);
|
||||||
|
|
||||||
// Unclosed string.
|
// Unclosed string.
|
||||||
p!("[val: \"hello]" => [func!("val": (Str("hello]")), {})], [
|
p!("[val: \"hello]" => [func!("val": (Str("hello]")), {})], [
|
||||||
@ -825,16 +821,16 @@ mod tests {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
//Invalid colors
|
//Invalid colors
|
||||||
p!("[val: #12345]" => [func!("val": (ClrS_Healed()), {})], [
|
p!("[val: #12345]" => [func!("val": (ClrStrHealed()), {})], [
|
||||||
(0:6, 0:12, "invalid color"),
|
(0:6, 0:12, "invalid color"),
|
||||||
]);
|
]);
|
||||||
p!("[val: #a5]" => [func!("val": (ClrS_Healed()), {})], [
|
p!("[val: #a5]" => [func!("val": (ClrStrHealed()), {})], [
|
||||||
(0:6, 0:9, "invalid color"),
|
(0:6, 0:9, "invalid color"),
|
||||||
]);
|
]);
|
||||||
p!("[val: #14b2ah]" => [func!("val": (ClrS_Healed()), {})], [
|
p!("[val: #14b2ah]" => [func!("val": (ClrStrHealed()), {})], [
|
||||||
(0:6, 0:13, "invalid color"),
|
(0:6, 0:13, "invalid color"),
|
||||||
]);
|
]);
|
||||||
p!("[val: #f075ff011]" => [func!("val": (ClrS_Healed()), {})], [
|
p!("[val: #f075ff011]" => [func!("val": (ClrStrHealed()), {})], [
|
||||||
(0:6, 0:16, "invalid color"),
|
(0:6, 0:16, "invalid color"),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -881,7 +877,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
p!("[val: items(\"fire\", #f93a6d)]" =>
|
p!("[val: items(\"fire\", #f93a6d)]" =>
|
||||||
[func!("val": (named_tuple!(
|
[func!("val": (named_tuple!(
|
||||||
"items", Str("fire"), ClrS("f93a6d")
|
"items", Str("fire"), ClrStr("f93a6d")
|
||||||
)), {})]
|
)), {})]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -389,12 +389,10 @@ impl<'s> Tokens<'s> {
|
|||||||
fn parse_hex_value(&mut self) -> Token<'s> {
|
fn parse_hex_value(&mut self) -> Token<'s> {
|
||||||
// This will parse more than the permissable 0-9, a-f, A-F character
|
// This will parse more than the permissable 0-9, a-f, A-F character
|
||||||
// ranges to provide nicer error messages later.
|
// ranges to provide nicer error messages later.
|
||||||
let payload = self.read_string_until(
|
ExprHex(self.read_string_until(
|
||||||
|n| !n.is_ascii_alphanumeric(),
|
|n| !n.is_ascii_alphanumeric(),
|
||||||
false, 0, 0
|
false, 0, 0
|
||||||
).0;
|
).0)
|
||||||
|
|
||||||
ExprHex(payload)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_expr(&mut self, text: &'s str) -> Token<'s> {
|
fn parse_expr(&mut self, text: &'s str) -> Token<'s> {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user