mirror of
https://github.com/typst/typst
synced 2025-06-28 16:22:53 +08:00
Restructure value conversions 🧱
This commit is contained in:
parent
6f111f9410
commit
2b660968aa
@ -99,7 +99,7 @@ impl Debug for RgbaColor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The error when parsing an `RgbaColor` fails.
|
/// The error when parsing an [`RgbaColor`] fails.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub struct ParseColorError;
|
pub struct ParseColorError;
|
||||||
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
//! Simplifies argument parsing.
|
//! Simplifies argument parsing.
|
||||||
|
|
||||||
use super::{Convert, EvalContext, RefKey, ValueDict};
|
use std::mem;
|
||||||
use crate::syntax::{SpanWith, Spanned};
|
|
||||||
|
use super::{Conv, EvalContext, RefKey, TryFromValue, Value, ValueDict};
|
||||||
|
use crate::diag::Diag;
|
||||||
|
use crate::syntax::{Span, SpanVec, SpanWith, Spanned};
|
||||||
|
|
||||||
/// A wrapper around a dictionary value that simplifies argument parsing in
|
/// A wrapper around a dictionary value that simplifies argument parsing in
|
||||||
/// functions.
|
/// functions.
|
||||||
@ -16,15 +19,11 @@ impl Args {
|
|||||||
pub fn get<'a, K, T>(&mut self, ctx: &mut EvalContext, key: K) -> Option<T>
|
pub fn get<'a, K, T>(&mut self, ctx: &mut EvalContext, key: K) -> Option<T>
|
||||||
where
|
where
|
||||||
K: Into<RefKey<'a>>,
|
K: Into<RefKey<'a>>,
|
||||||
T: Convert,
|
T: TryFromValue,
|
||||||
{
|
{
|
||||||
self.0.v.remove(key).and_then(|entry| {
|
self.0.v.remove(key).and_then(|entry| {
|
||||||
let span = entry.value.span;
|
let span = entry.value.span;
|
||||||
let (result, diag) = T::convert(entry.value);
|
conv_diag(T::try_from_value(entry.value), &mut ctx.f.diags, span)
|
||||||
if let Some(diag) = diag {
|
|
||||||
ctx.f.diags.push(diag.span_with(span))
|
|
||||||
}
|
|
||||||
result.ok()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,38 +37,30 @@ impl Args {
|
|||||||
) -> Option<T>
|
) -> Option<T>
|
||||||
where
|
where
|
||||||
K: Into<RefKey<'a>>,
|
K: Into<RefKey<'a>>,
|
||||||
T: Convert,
|
T: TryFromValue,
|
||||||
{
|
{
|
||||||
match self.0.v.remove(key) {
|
if let Some(entry) = self.0.v.remove(key) {
|
||||||
Some(entry) => {
|
|
||||||
let span = entry.value.span;
|
let span = entry.value.span;
|
||||||
let (result, diag) = T::convert(entry.value);
|
conv_diag(T::try_from_value(entry.value), &mut ctx.f.diags, span)
|
||||||
if let Some(diag) = diag {
|
} else {
|
||||||
ctx.f.diags.push(diag.span_with(span))
|
|
||||||
}
|
|
||||||
result.ok()
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
ctx.f.diags.push(error!(self.0.span, "missing argument: {}", name));
|
ctx.f.diags.push(error!(self.0.span, "missing argument: {}", name));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve and remove the first matching positional argument.
|
/// Retrieve and remove the first matching positional argument.
|
||||||
pub fn find<T>(&mut self) -> Option<T>
|
pub fn find<T>(&mut self) -> Option<T>
|
||||||
where
|
where
|
||||||
T: Convert,
|
T: TryFromValue,
|
||||||
{
|
{
|
||||||
for (&key, entry) in self.0.v.nums_mut() {
|
for (&key, entry) in self.0.v.nums_mut() {
|
||||||
let span = entry.value.span;
|
let span = entry.value.span;
|
||||||
match T::convert(std::mem::take(&mut entry.value)).0 {
|
let slot = &mut entry.value;
|
||||||
Ok(t) => {
|
let conv = conv_put_back(T::try_from_value(mem::take(slot)), slot, span);
|
||||||
|
if let Some(t) = conv {
|
||||||
self.0.v.remove(key);
|
self.0.v.remove(key);
|
||||||
return Some(t);
|
return Some(t);
|
||||||
}
|
}
|
||||||
Err(v) => entry.value = v.span_with(span),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -77,19 +68,18 @@ impl Args {
|
|||||||
/// Retrieve and remove all matching positional arguments.
|
/// Retrieve and remove all matching positional arguments.
|
||||||
pub fn find_all<T>(&mut self) -> impl Iterator<Item = T> + '_
|
pub fn find_all<T>(&mut self) -> impl Iterator<Item = T> + '_
|
||||||
where
|
where
|
||||||
T: Convert,
|
T: TryFromValue,
|
||||||
{
|
{
|
||||||
let mut skip = 0;
|
let mut skip = 0;
|
||||||
std::iter::from_fn(move || {
|
std::iter::from_fn(move || {
|
||||||
for (&key, entry) in self.0.v.nums_mut().skip(skip) {
|
for (&key, entry) in self.0.v.nums_mut().skip(skip) {
|
||||||
let span = entry.value.span;
|
let span = entry.value.span;
|
||||||
match T::convert(std::mem::take(&mut entry.value)).0 {
|
let slot = &mut entry.value;
|
||||||
Ok(t) => {
|
let conv = conv_put_back(T::try_from_value(mem::take(slot)), slot, span);
|
||||||
|
if let Some(t) = conv {
|
||||||
self.0.v.remove(key);
|
self.0.v.remove(key);
|
||||||
return Some(t);
|
return Some(t);
|
||||||
}
|
}
|
||||||
Err(v) => entry.value = v.span_with(span),
|
|
||||||
}
|
|
||||||
skip += 1;
|
skip += 1;
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@ -99,20 +89,19 @@ impl Args {
|
|||||||
/// Retrieve and remove all matching keyword arguments.
|
/// Retrieve and remove all matching keyword arguments.
|
||||||
pub fn find_all_str<T>(&mut self) -> impl Iterator<Item = (String, T)> + '_
|
pub fn find_all_str<T>(&mut self) -> impl Iterator<Item = (String, T)> + '_
|
||||||
where
|
where
|
||||||
T: Convert,
|
T: TryFromValue,
|
||||||
{
|
{
|
||||||
let mut skip = 0;
|
let mut skip = 0;
|
||||||
std::iter::from_fn(move || {
|
std::iter::from_fn(move || {
|
||||||
for (key, entry) in self.0.v.strs_mut().skip(skip) {
|
for (key, entry) in self.0.v.strs_mut().skip(skip) {
|
||||||
let span = entry.value.span;
|
let span = entry.value.span;
|
||||||
match T::convert(std::mem::take(&mut entry.value)).0 {
|
let slot = &mut entry.value;
|
||||||
Ok(t) => {
|
let conv = conv_put_back(T::try_from_value(mem::take(slot)), slot, span);
|
||||||
|
if let Some(t) = conv {
|
||||||
let key = key.clone();
|
let key = key.clone();
|
||||||
self.0.v.remove(&key);
|
self.0.v.remove(&key);
|
||||||
return Some((key, t));
|
return Some((key, t));
|
||||||
}
|
}
|
||||||
Err(v) => entry.value = v.span_with(span),
|
|
||||||
}
|
|
||||||
skip += 1;
|
skip += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,6 +118,31 @@ impl Args {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn conv_diag<T>(conv: Conv<T>, diags: &mut SpanVec<Diag>, span: Span) -> Option<T> {
|
||||||
|
match conv {
|
||||||
|
Conv::Ok(t) => Some(t),
|
||||||
|
Conv::Warn(t, warn) => {
|
||||||
|
diags.push(warn.span_with(span));
|
||||||
|
Some(t)
|
||||||
|
}
|
||||||
|
Conv::Err(_, err) => {
|
||||||
|
diags.push(err.span_with(span));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn conv_put_back<T>(conv: Conv<T>, slot: &mut Spanned<Value>, span: Span) -> Option<T> {
|
||||||
|
match conv {
|
||||||
|
Conv::Ok(t) => Some(t),
|
||||||
|
Conv::Warn(t, _) => Some(t),
|
||||||
|
Conv::Err(v, _) => {
|
||||||
|
*slot = v.span_with(span);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::super::{Dict, SpannedEntry, Value};
|
use super::super::{Dict, SpannedEntry, Value};
|
||||||
|
@ -1,170 +0,0 @@
|
|||||||
//! Conversion from values into other types.
|
|
||||||
|
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use fontdock::{FontStretch, FontStyle, FontWeight};
|
|
||||||
|
|
||||||
use super::{Value, ValueDict, ValueFunc};
|
|
||||||
use crate::diag::Diag;
|
|
||||||
use crate::geom::{Dir, Length, Linear, Relative};
|
|
||||||
use crate::paper::Paper;
|
|
||||||
use crate::syntax::{Ident, SpanWith, Spanned, SynTree};
|
|
||||||
|
|
||||||
/// Types that values can be converted into.
|
|
||||||
pub trait Convert: Sized {
|
|
||||||
/// Convert a value into `Self`.
|
|
||||||
///
|
|
||||||
/// If the conversion works out, this should return `Ok(...)` with an
|
|
||||||
/// instance of `Self`. If it doesn't, it should return `Err(...)` giving
|
|
||||||
/// back the original value.
|
|
||||||
///
|
|
||||||
/// In addition to the result, the method can return an optional diagnostic
|
|
||||||
/// to warn even when the conversion succeeded or to explain the problem when
|
|
||||||
/// the conversion failed.
|
|
||||||
///
|
|
||||||
/// The function takes a `Spanned<Value>` instead of just a `Value` so that
|
|
||||||
/// this trait can be blanket implemented for `Spanned<T>` where `T:
|
|
||||||
/// Convert`.
|
|
||||||
fn convert(value: Spanned<Value>) -> (Result<Self, Value>, Option<Diag>);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Convert> Convert for Spanned<T> {
|
|
||||||
fn convert(value: Spanned<Value>) -> (Result<Self, Value>, Option<Diag>) {
|
|
||||||
let span = value.span;
|
|
||||||
let (result, diag) = T::convert(value);
|
|
||||||
(result.map(|v| v.span_with(span)), diag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! convert_match {
|
|
||||||
($type:ty, $name:expr, $($p:pat => $r:expr),* $(,)?) => {
|
|
||||||
impl $crate::eval::Convert for $type {
|
|
||||||
fn convert(
|
|
||||||
value: $crate::syntax::Spanned<$crate::eval::Value>
|
|
||||||
) -> (Result<Self, $crate::eval::Value>, Option<$crate::diag::Diag>) {
|
|
||||||
#[allow(unreachable_patterns)]
|
|
||||||
match value.v {
|
|
||||||
$($p => (Ok($r), None)),*,
|
|
||||||
v => {
|
|
||||||
let err = $crate::error!("expected {}, found {}", $name, v.ty());
|
|
||||||
(Err(v), Some(err))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! convert_ident {
|
|
||||||
($type:ty, $name:expr, $parse:expr) => {
|
|
||||||
impl $crate::eval::Convert for $type {
|
|
||||||
fn convert(
|
|
||||||
value: $crate::syntax::Spanned<$crate::eval::Value>,
|
|
||||||
) -> (
|
|
||||||
Result<Self, $crate::eval::Value>,
|
|
||||||
Option<$crate::diag::Diag>,
|
|
||||||
) {
|
|
||||||
match value.v {
|
|
||||||
Value::Ident(id) => {
|
|
||||||
if let Some(thing) = $parse(&id) {
|
|
||||||
(Ok(thing), None)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
Err($crate::eval::Value::Ident(id)),
|
|
||||||
Some($crate::error!("invalid {}", $name)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v => {
|
|
||||||
let err = $crate::error!("expected {}, found {}", $name, v.ty());
|
|
||||||
(Err(v), Some(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A value type that matches [identifier](Value::Ident) and [string](Value::Str) values.
|
|
||||||
pub struct StringLike(pub String);
|
|
||||||
|
|
||||||
impl From<StringLike> for String {
|
|
||||||
fn from(like: StringLike) -> String {
|
|
||||||
like.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for StringLike {
|
|
||||||
type Target = str;
|
|
||||||
|
|
||||||
fn deref(&self) -> &str {
|
|
||||||
self.0.as_str()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
convert_match!(Value, "value", v => v);
|
|
||||||
convert_match!(Ident, "identifier", Value::Ident(v) => v);
|
|
||||||
convert_match!(bool, "bool", Value::Bool(v) => v);
|
|
||||||
convert_match!(i64, "integer", Value::Int(v) => v);
|
|
||||||
convert_match!(f64, "float",
|
|
||||||
Value::Int(v) => v as f64,
|
|
||||||
Value::Float(v) => v,
|
|
||||||
);
|
|
||||||
convert_match!(Length, "length", Value::Length(v) => v);
|
|
||||||
convert_match!(Relative, "relative", Value::Relative(v) => v);
|
|
||||||
convert_match!(Linear, "linear",
|
|
||||||
Value::Linear(v) => v,
|
|
||||||
Value::Length(v) => v.into(),
|
|
||||||
Value::Relative(v) => v.into(),
|
|
||||||
);
|
|
||||||
convert_match!(String, "string", Value::Str(v) => v);
|
|
||||||
convert_match!(SynTree, "tree", Value::Content(v) => v);
|
|
||||||
convert_match!(ValueDict, "dictionary", Value::Dict(v) => v);
|
|
||||||
convert_match!(ValueFunc, "function", Value::Func(v) => v);
|
|
||||||
convert_match!(StringLike, "identifier or string",
|
|
||||||
Value::Ident(Ident(v)) => StringLike(v),
|
|
||||||
Value::Str(v) => StringLike(v),
|
|
||||||
);
|
|
||||||
|
|
||||||
convert_ident!(Dir, "direction", |v| match v {
|
|
||||||
"ltr" => Some(Self::LTR),
|
|
||||||
"rtl" => Some(Self::RTL),
|
|
||||||
"ttb" => Some(Self::TTB),
|
|
||||||
"btt" => Some(Self::BTT),
|
|
||||||
_ => None,
|
|
||||||
});
|
|
||||||
|
|
||||||
convert_ident!(FontStyle, "font style", Self::from_str);
|
|
||||||
convert_ident!(FontStretch, "font stretch", Self::from_str);
|
|
||||||
convert_ident!(Paper, "paper", Self::from_name);
|
|
||||||
|
|
||||||
impl Convert for FontWeight {
|
|
||||||
fn convert(value: Spanned<Value>) -> (Result<Self, Value>, Option<Diag>) {
|
|
||||||
match value.v {
|
|
||||||
Value::Int(number) => {
|
|
||||||
let [min, max] = [100, 900];
|
|
||||||
let warning = if number < min {
|
|
||||||
Some(warning!("the minimum font weight is {}", min))
|
|
||||||
} else if number > max {
|
|
||||||
Some(warning!("the maximum font weight is {}", max))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let weight = Self::from_number(number.min(max).max(min) as u16);
|
|
||||||
(Ok(weight), warning)
|
|
||||||
}
|
|
||||||
Value::Ident(id) => {
|
|
||||||
if let Some(thing) = FontWeight::from_str(&id) {
|
|
||||||
(Ok(thing), None)
|
|
||||||
} else {
|
|
||||||
(Err(Value::Ident(id)), Some(error!("invalid font weight")))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v => {
|
|
||||||
let err =
|
|
||||||
error!("expected font weight (name or number), found {}", v.ty());
|
|
||||||
(Err(v), Some(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +1,13 @@
|
|||||||
//! Evaluation of syntax trees.
|
//! Evaluation of syntax trees.
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod convert;
|
mod value;
|
||||||
mod args;
|
mod args;
|
||||||
mod dict;
|
mod dict;
|
||||||
mod scope;
|
mod scope;
|
||||||
mod state;
|
mod state;
|
||||||
mod value;
|
|
||||||
|
|
||||||
pub use args::*;
|
pub use args::*;
|
||||||
pub use convert::*;
|
|
||||||
pub use dict::*;
|
pub use dict::*;
|
||||||
pub use scope::*;
|
pub use scope::*;
|
||||||
pub use state::*;
|
pub use state::*;
|
||||||
|
@ -4,10 +4,14 @@ use std::fmt::{self, Debug, Formatter};
|
|||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use fontdock::{FontStretch, FontStyle, FontWeight};
|
||||||
|
|
||||||
use super::{Args, Dict, Eval, EvalContext, SpannedEntry};
|
use super::{Args, Dict, Eval, EvalContext, SpannedEntry};
|
||||||
use crate::color::RgbaColor;
|
use crate::color::RgbaColor;
|
||||||
use crate::geom::{Length, Linear, Relative};
|
use crate::diag::Diag;
|
||||||
use crate::syntax::{Ident, SynTree};
|
use crate::geom::{Dir, Length, Linear, Relative};
|
||||||
|
use crate::paper::Paper;
|
||||||
|
use crate::syntax::{Ident, SpanWith, Spanned, SynTree};
|
||||||
|
|
||||||
/// A computational value.
|
/// A computational value.
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
@ -170,3 +174,160 @@ impl Debug for ValueFunc {
|
|||||||
f.pad("<function>")
|
f.pad("<function>")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try to convert a value into a more specific type.
|
||||||
|
pub trait TryFromValue: Sized {
|
||||||
|
/// Try to convert the value into yourself.
|
||||||
|
fn try_from_value(value: Spanned<Value>) -> Conv<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The result of a conversion.
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Conv<T> {
|
||||||
|
/// Success conversion.
|
||||||
|
Ok(T),
|
||||||
|
/// Sucessful conversion with a warning.
|
||||||
|
Warn(T, Diag),
|
||||||
|
/// Unsucessful conversion, gives back the value alongside the error.
|
||||||
|
Err(Value, Diag),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Conv<T> {
|
||||||
|
/// Map the conversion result.
|
||||||
|
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Conv<U> {
|
||||||
|
match self {
|
||||||
|
Conv::Ok(t) => Conv::Ok(f(t)),
|
||||||
|
Conv::Warn(t, warn) => Conv::Warn(f(t), warn),
|
||||||
|
Conv::Err(v, err) => Conv::Err(v, err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: TryFromValue> TryFromValue for Spanned<T> {
|
||||||
|
fn try_from_value(value: Spanned<Value>) -> Conv<Self> {
|
||||||
|
let span = value.span;
|
||||||
|
T::try_from_value(value).map(|v| v.span_with(span))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A value type that matches [identifier](Value::Ident) and [string](Value::Str) values.
|
||||||
|
pub struct StringLike(pub String);
|
||||||
|
|
||||||
|
impl From<StringLike> for String {
|
||||||
|
fn from(like: StringLike) -> String {
|
||||||
|
like.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for StringLike {
|
||||||
|
type Target = str;
|
||||||
|
|
||||||
|
fn deref(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement [`TryFromValue`] through a match.
|
||||||
|
macro_rules! try_from_match {
|
||||||
|
($type:ty[$name:literal] $(@ $span:ident)?: $($pattern:pat => $output:expr),* $(,)?) => {
|
||||||
|
impl $crate::eval::TryFromValue for $type {
|
||||||
|
fn try_from_value(value: Spanned<Value>) -> $crate::eval::Conv<Self> {
|
||||||
|
use $crate::eval::Conv;
|
||||||
|
#[allow(unused)]
|
||||||
|
$(let $span = value.span;)?
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
match value.v {
|
||||||
|
$($pattern => Conv::Ok($output)),*,
|
||||||
|
v => {
|
||||||
|
let e = error!("expected {}, found {}", $name, v.ty());
|
||||||
|
Conv::Err(v, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement [`TryFromValue`] through a function parsing an identifier.
|
||||||
|
macro_rules! try_from_id {
|
||||||
|
($type:ty[$name:literal]: $from_str:expr) => {
|
||||||
|
impl $crate::eval::TryFromValue for $type {
|
||||||
|
fn try_from_value(value: Spanned<Value>) -> $crate::eval::Conv<Self> {
|
||||||
|
use $crate::eval::Conv;
|
||||||
|
let v = value.v;
|
||||||
|
if let Value::Ident(id) = v {
|
||||||
|
if let Some(v) = $from_str(&id) {
|
||||||
|
Conv::Ok(v)
|
||||||
|
} else {
|
||||||
|
Conv::Err(Value::Ident(id), error!("invalid {}", $name))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let e = error!("expected identifier, found {}", v.ty());
|
||||||
|
Conv::Err(v, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try_from_match!(Value["value"]: v => v);
|
||||||
|
try_from_match!(Ident["identifier"]: Value::Ident(v) => v);
|
||||||
|
try_from_match!(bool["bool"]: Value::Bool(v) => v);
|
||||||
|
try_from_match!(i64["integer"]: Value::Int(v) => v);
|
||||||
|
try_from_match!(f64["float"]:
|
||||||
|
Value::Int(v) => v as f64,
|
||||||
|
Value::Float(v) => v,
|
||||||
|
);
|
||||||
|
try_from_match!(Length["length"]: Value::Length(v) => v);
|
||||||
|
try_from_match!(Relative["relative"]: Value::Relative(v) => v);
|
||||||
|
try_from_match!(Linear["linear"]:
|
||||||
|
Value::Linear(v) => v,
|
||||||
|
Value::Length(v) => v.into(),
|
||||||
|
Value::Relative(v) => v.into(),
|
||||||
|
);
|
||||||
|
try_from_match!(String["string"]: Value::Str(v) => v);
|
||||||
|
try_from_match!(SynTree["tree"]: Value::Content(v) => v);
|
||||||
|
try_from_match!(ValueDict["dictionary"]: Value::Dict(v) => v);
|
||||||
|
try_from_match!(ValueFunc["function"]: Value::Func(v) => v);
|
||||||
|
try_from_match!(StringLike["identifier or string"]:
|
||||||
|
Value::Ident(Ident(v)) => Self(v),
|
||||||
|
Value::Str(v) => Self(v),
|
||||||
|
);
|
||||||
|
try_from_id!(Dir["direction"]: |v| match v {
|
||||||
|
"ltr" | "left-to-right" => Some(Self::LTR),
|
||||||
|
"rtl" | "right-to-left" => Some(Self::RTL),
|
||||||
|
"ttb" | "top-to-bottom" => Some(Self::TTB),
|
||||||
|
"btt" | "bottom-to-top" => Some(Self::BTT),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
try_from_id!(FontStyle["font style"]: Self::from_str);
|
||||||
|
try_from_id!(FontStretch["font stretch"]: Self::from_str);
|
||||||
|
try_from_id!(Paper["paper"]: Self::from_name);
|
||||||
|
|
||||||
|
impl TryFromValue for FontWeight {
|
||||||
|
fn try_from_value(value: Spanned<Value>) -> Conv<Self> {
|
||||||
|
match value.v {
|
||||||
|
Value::Int(number) => {
|
||||||
|
let [min, max] = [Self::THIN, Self::BLACK];
|
||||||
|
if number < i64::from(min.to_number()) {
|
||||||
|
Conv::Warn(min, warning!("the minimum font weight is {:#?}", min))
|
||||||
|
} else if number > i64::from(max.to_number()) {
|
||||||
|
Conv::Warn(max, warning!("the maximum font weight is {:#?}", max))
|
||||||
|
} else {
|
||||||
|
Conv::Ok(Self::from_number(number as u16))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::Ident(id) => {
|
||||||
|
if let Some(weight) = Self::from_str(&id) {
|
||||||
|
Conv::Ok(weight)
|
||||||
|
} else {
|
||||||
|
Conv::Err(Value::Ident(id), error!("invalid font weight"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v => {
|
||||||
|
let e = error!("expected font weight, found {}", v.ty());
|
||||||
|
Conv::Err(v, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -116,7 +116,7 @@ enum SpecAlign {
|
|||||||
Center,
|
Center,
|
||||||
}
|
}
|
||||||
|
|
||||||
convert_ident!(SpecAlign, "alignment", |v| match v {
|
try_from_id!(SpecAlign["alignment"]: |v| match v {
|
||||||
"left" => Some(Self::Left),
|
"left" => Some(Self::Left),
|
||||||
"right" => Some(Self::Right),
|
"right" => Some(Self::Right),
|
||||||
"top" => Some(Self::Top),
|
"top" => Some(Self::Top),
|
||||||
|
@ -39,7 +39,7 @@ fn main() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
let mut index = FsIndex::new();
|
let mut index = FsIndex::new();
|
||||||
index.search_dir("fonts");
|
index.search_dir("fonts");
|
||||||
index.search_os();
|
index.search_system();
|
||||||
|
|
||||||
let (files, descriptors) = index.into_vecs();
|
let (files, descriptors) = index.into_vecs();
|
||||||
let env = Rc::new(RefCell::new(Env {
|
let env = Rc::new(RefCell::new(Env {
|
||||||
|
@ -7,5 +7,5 @@ pub use crate::geom::*;
|
|||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use crate::layout::LayoutNode;
|
pub use crate::layout::LayoutNode;
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use crate::syntax::{Span, Spanned, SynTree};
|
pub use crate::syntax::{Span, SpanWith, Spanned, SynTree};
|
||||||
pub use crate::{error, warning};
|
pub use crate::{error, warning};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user