mirror of
https://github.com/typst/typst
synced 2025-05-13 12:36:23 +08:00
Derive Cast
for enums
This commit is contained in:
parent
cb3c263c4a
commit
880b1847bd
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1269,6 +1269,7 @@ dependencies = [
|
||||
name = "typst-macros"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
|
@ -193,30 +193,15 @@ impl Resolve for HorizontalAlign {
|
||||
}
|
||||
|
||||
/// How to determine line breaks in a paragraph.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
|
||||
pub enum Linebreaks {
|
||||
/// Determine the line breaks in a simple first-fit style.
|
||||
Simple,
|
||||
/// Optimize the line breaks for the whole paragraph.
|
||||
Optimized,
|
||||
}
|
||||
|
||||
cast_from_value! {
|
||||
Linebreaks,
|
||||
/// Determine the line breaks in a simple first-fit style.
|
||||
"simple" => Self::Simple,
|
||||
/// Optimize the line breaks for the whole paragraph.
|
||||
///
|
||||
/// Typst will try to produce more evenly filled lines of text by
|
||||
/// considering the whole paragraph when calculating line breaks.
|
||||
"optimized" => Self::Optimized,
|
||||
}
|
||||
|
||||
cast_to_value! {
|
||||
v: Linebreaks => Value::from(match v {
|
||||
Linebreaks::Simple => "simple",
|
||||
Linebreaks::Optimized => "optimized",
|
||||
})
|
||||
Optimized,
|
||||
}
|
||||
|
||||
/// A paragraph break.
|
||||
|
@ -169,12 +169,22 @@ impl LayoutMath for CasesNode {
|
||||
}
|
||||
|
||||
/// A vector / matrix delimiter.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
|
||||
pub enum Delimiter {
|
||||
/// Delimit with parentheses.
|
||||
#[string("(")]
|
||||
Paren,
|
||||
/// Delimit with brackets.
|
||||
#[string("[")]
|
||||
Bracket,
|
||||
/// Delimit with curly braces.
|
||||
#[string("{")]
|
||||
Brace,
|
||||
/// Delimit with vertical bars.
|
||||
#[string("|")]
|
||||
Bar,
|
||||
/// Delimit with double vertical bars.
|
||||
#[string("||")]
|
||||
DoubleBar,
|
||||
}
|
||||
|
||||
@ -202,30 +212,6 @@ impl Delimiter {
|
||||
}
|
||||
}
|
||||
|
||||
cast_from_value! {
|
||||
Delimiter,
|
||||
/// Delimit with parentheses.
|
||||
"(" => Self::Paren,
|
||||
/// Delimit with brackets.
|
||||
"[" => Self::Bracket,
|
||||
/// Delimit with curly braces.
|
||||
"{" => Self::Brace,
|
||||
/// Delimit with vertical bars.
|
||||
"|" => Self::Bar,
|
||||
/// Delimit with double vertical bars.
|
||||
"||" => Self::DoubleBar,
|
||||
}
|
||||
|
||||
cast_to_value! {
|
||||
v: Delimiter => Value::from(match v {
|
||||
Delimiter::Paren => "(",
|
||||
Delimiter::Bracket => "[",
|
||||
Delimiter::Brace => "{",
|
||||
Delimiter::Bar => "|",
|
||||
Delimiter::DoubleBar => "||",
|
||||
})
|
||||
}
|
||||
|
||||
/// Layout the inner contents of a vector.
|
||||
fn layout_vec_body(
|
||||
ctx: &mut MathContext,
|
||||
|
@ -246,7 +246,7 @@ cast_from_value! {
|
||||
}
|
||||
|
||||
/// A case transformation on text.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
|
||||
pub enum Case {
|
||||
/// Everything is lowercased.
|
||||
Lower,
|
||||
@ -264,19 +264,6 @@ impl Case {
|
||||
}
|
||||
}
|
||||
|
||||
cast_from_value! {
|
||||
Case,
|
||||
"lower" => Self::Lower,
|
||||
"upper" => Self::Upper,
|
||||
}
|
||||
|
||||
cast_to_value! {
|
||||
v: Case => Value::from(match v {
|
||||
Case::Lower => "lower",
|
||||
Case::Upper => "upper",
|
||||
})
|
||||
}
|
||||
|
||||
/// Display text in small capitals.
|
||||
///
|
||||
/// _Note:_ This enables the OpenType `smcp` feature for the font. Not all fonts
|
||||
|
@ -686,53 +686,23 @@ cast_to_value! {
|
||||
}
|
||||
|
||||
/// Which kind of numbers / figures to select.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
|
||||
pub enum NumberType {
|
||||
/// Numbers that fit well with capital text. ("lnum")
|
||||
/// Numbers that fit well with capital text (the OpenType `lnum`
|
||||
/// font feature).
|
||||
Lining,
|
||||
/// Numbers that fit well into a flow of upper- and lowercase text. ("onum")
|
||||
/// Numbers that fit well into a flow of upper- and lowercase text (the
|
||||
/// OpenType `onum` font feature).
|
||||
OldStyle,
|
||||
}
|
||||
|
||||
cast_from_value! {
|
||||
NumberType,
|
||||
/// Numbers that fit well with capital text (the OpenType `lnum`
|
||||
/// font feature).
|
||||
"lining" => Self::Lining,
|
||||
// Numbers that fit well into a flow of upper- and lowercase text (the
|
||||
/// OpenType `onum` font feature).
|
||||
"old-style" => Self::OldStyle,
|
||||
}
|
||||
|
||||
cast_to_value! {
|
||||
v: NumberType => Value::from(match v {
|
||||
NumberType::Lining => "lining",
|
||||
NumberType::OldStyle => "old-style",
|
||||
})
|
||||
}
|
||||
|
||||
/// The width of numbers / figures.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
|
||||
pub enum NumberWidth {
|
||||
/// Number widths are glyph specific. ("pnum")
|
||||
Proportional,
|
||||
/// All numbers are of equal width / monospaced. ("tnum")
|
||||
Tabular,
|
||||
}
|
||||
|
||||
cast_from_value! {
|
||||
NumberWidth,
|
||||
/// Numbers with glyph-specific widths (the OpenType `pnum` font feature).
|
||||
"proportional" => Self::Proportional,
|
||||
Proportional,
|
||||
/// Numbers of equal width (the OpenType `tnum` font feature).
|
||||
"tabular" => Self::Tabular,
|
||||
}
|
||||
|
||||
cast_to_value! {
|
||||
v: NumberWidth => Value::from(match v {
|
||||
NumberWidth::Proportional => "proportional",
|
||||
NumberWidth::Tabular => "tabular",
|
||||
})
|
||||
Tabular,
|
||||
}
|
||||
|
||||
/// OpenType font features settings.
|
||||
|
@ -113,33 +113,15 @@ impl Layout for ImageNode {
|
||||
}
|
||||
|
||||
/// How an image should adjust itself to a given area.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
|
||||
pub enum ImageFit {
|
||||
/// The image should completely cover the area.
|
||||
/// The image should completely cover the area. This is the default.
|
||||
Cover,
|
||||
/// The image should be fully contained in the area.
|
||||
Contain,
|
||||
/// The image should be stretched so that it exactly fills the area.
|
||||
Stretch,
|
||||
}
|
||||
|
||||
cast_from_value! {
|
||||
ImageFit,
|
||||
/// The image should completely cover the area. This is the default.
|
||||
"cover" => Self::Cover,
|
||||
/// The image should be fully contained in the area.
|
||||
"contain" => Self::Contain,
|
||||
/// The image should be stretched so that it exactly fills the area, even if
|
||||
/// this means that the image will be distorted.
|
||||
"stretch" => Self::Stretch,
|
||||
}
|
||||
|
||||
cast_to_value! {
|
||||
fit: ImageFit => Value::from(match fit {
|
||||
ImageFit::Cover => "cover",
|
||||
ImageFit::Contain => "contain",
|
||||
ImageFit::Stretch => "stretch",
|
||||
})
|
||||
Stretch,
|
||||
}
|
||||
|
||||
/// Load an image from a path.
|
||||
|
@ -15,3 +15,4 @@ proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = { version = "1", features = ["full", "extra-traits"] }
|
||||
unscanny = "0.1"
|
||||
heck = "0.4"
|
||||
|
@ -1,5 +1,67 @@
|
||||
use super::*;
|
||||
|
||||
/// Expand the `#[derive(Cast)]` macro.
|
||||
pub fn cast(item: DeriveInput) -> Result<TokenStream> {
|
||||
let ty = &item.ident;
|
||||
|
||||
let syn::Data::Enum(data) = &item.data else {
|
||||
bail!(item, "only enums are supported");
|
||||
};
|
||||
|
||||
let mut variants = vec![];
|
||||
for variant in &data.variants {
|
||||
if let Some((_, expr)) = &variant.discriminant {
|
||||
bail!(expr, "explicit discriminant is not allowed");
|
||||
}
|
||||
|
||||
let string = if let Some(attr) =
|
||||
variant.attrs.iter().find(|attr| attr.path.is_ident("string"))
|
||||
{
|
||||
attr.parse_args::<syn::LitStr>()?.value()
|
||||
} else {
|
||||
kebab_case(&variant.ident)
|
||||
};
|
||||
|
||||
variants.push(Variant {
|
||||
ident: variant.ident.clone(),
|
||||
string,
|
||||
docs: documentation(&variant.attrs),
|
||||
});
|
||||
}
|
||||
|
||||
let strs_to_variants = variants.iter().map(|Variant { ident, string, docs }| {
|
||||
quote! {
|
||||
#[doc = #docs]
|
||||
#string => Self::#ident
|
||||
}
|
||||
});
|
||||
|
||||
let variants_to_strs = variants.iter().map(|Variant { ident, string, .. }| {
|
||||
quote! {
|
||||
#ty::#ident => #string
|
||||
}
|
||||
});
|
||||
|
||||
Ok(quote! {
|
||||
::typst::eval::cast_from_value! {
|
||||
#ty,
|
||||
#(#strs_to_variants),*
|
||||
}
|
||||
|
||||
::typst::eval::cast_to_value! {
|
||||
v: #ty => ::typst::eval::Value::from(match v {
|
||||
#(#variants_to_strs),*
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
struct Variant {
|
||||
ident: Ident,
|
||||
string: String,
|
||||
docs: String,
|
||||
}
|
||||
|
||||
/// Expand the `cast_from_value!` macro.
|
||||
pub fn cast_from_value(stream: TokenStream) -> Result<TokenStream> {
|
||||
let castable: Castable = syn::parse2(stream)?;
|
||||
|
@ -15,7 +15,7 @@ use quote::quote;
|
||||
use syn::ext::IdentExt;
|
||||
use syn::parse::{Parse, ParseStream, Parser};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::{parse_quote, Ident, Result, Token};
|
||||
use syn::{parse_quote, DeriveInput, Ident, Result, Token};
|
||||
|
||||
use self::util::*;
|
||||
|
||||
@ -35,6 +35,15 @@ pub fn node(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Implement `Cast` for an enum.
|
||||
#[proc_macro_derive(Cast, attributes(string))]
|
||||
pub fn cast(item: BoundaryStream) -> BoundaryStream {
|
||||
let item = syn::parse_macro_input!(item as DeriveInput);
|
||||
castable::cast(item)
|
||||
.unwrap_or_else(|err| err.to_compile_error())
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Implement `Cast` and optionally `Type` for a type.
|
||||
#[proc_macro]
|
||||
pub fn cast_from_value(stream: BoundaryStream) -> BoundaryStream {
|
||||
|
@ -1,3 +1,5 @@
|
||||
use heck::ToKebabCase;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Return an error at the given item.
|
||||
@ -55,7 +57,7 @@ pub fn validate_attrs(attrs: &[syn::Attribute]) -> Result<()> {
|
||||
|
||||
/// Convert an identifier to a kebab-case string.
|
||||
pub fn kebab_case(name: &Ident) -> String {
|
||||
name.to_string().to_lowercase().replace('_', "-")
|
||||
name.to_string().to_kebab_case()
|
||||
}
|
||||
|
||||
/// Extract documentation comments from an attribute list.
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub use typst_macros::{cast_from_value, cast_to_value};
|
||||
pub use typst_macros::{cast_from_value, cast_to_value, Cast};
|
||||
|
||||
use std::num::{NonZeroI64, NonZeroUsize};
|
||||
use std::ops::Add;
|
||||
|
@ -12,7 +12,7 @@ use std::sync::Arc;
|
||||
|
||||
use ttf_parser::GlyphId;
|
||||
|
||||
use crate::eval::{cast_from_value, cast_to_value, Value};
|
||||
use crate::eval::Cast;
|
||||
use crate::geom::Em;
|
||||
use crate::util::Buffer;
|
||||
|
||||
@ -231,12 +231,9 @@ pub struct LineMetrics {
|
||||
}
|
||||
|
||||
/// Identifies a vertical metric of a font.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
|
||||
pub enum VerticalFontMetric {
|
||||
/// The typographic ascender.
|
||||
///
|
||||
/// Corresponds to the typographic ascender from the `OS/2` table if present
|
||||
/// and falls back to the ascender from the `hhea` table otherwise.
|
||||
/// The font's ascender, which typically exceeds the height of all glyphs.
|
||||
Ascender,
|
||||
/// The approximate height of uppercase letters.
|
||||
CapHeight,
|
||||
@ -244,33 +241,6 @@ pub enum VerticalFontMetric {
|
||||
XHeight,
|
||||
/// The baseline on which the letters rest.
|
||||
Baseline,
|
||||
/// The typographic descender.
|
||||
///
|
||||
/// Corresponds to the typographic descender from the `OS/2` table if
|
||||
/// present and falls back to the descender from the `hhea` table otherwise.
|
||||
/// The font's ascender, which typically exceeds the depth of all glyphs.
|
||||
Descender,
|
||||
}
|
||||
|
||||
cast_from_value! {
|
||||
VerticalFontMetric,
|
||||
/// The font's ascender, which typically exceeds the height of all glyphs.
|
||||
"ascender" => Self::Ascender,
|
||||
/// The approximate height of uppercase letters.
|
||||
"cap-height" => Self::CapHeight,
|
||||
/// The approximate height of non-ascending lowercase letters.
|
||||
"x-height" => Self::XHeight,
|
||||
/// The baseline on which the letters rest.
|
||||
"baseline" => Self::Baseline,
|
||||
/// The font's ascender, which typically exceeds the depth of all glyphs.
|
||||
"descender" => Self::Descender,
|
||||
}
|
||||
|
||||
cast_to_value! {
|
||||
v: VerticalFontMetric => Value::from(match v {
|
||||
VerticalFontMetric::Ascender => "ascender",
|
||||
VerticalFontMetric::CapHeight => "cap-height",
|
||||
VerticalFontMetric::XHeight => "x-height",
|
||||
VerticalFontMetric::Baseline => "baseline" ,
|
||||
VerticalFontMetric::Descender => "descender",
|
||||
})
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::eval::{cast_from_value, cast_to_value, Value};
|
||||
use crate::eval::{cast_from_value, cast_to_value, Cast, Value};
|
||||
use crate::geom::Ratio;
|
||||
|
||||
/// Properties that distinguish a font from other fonts in the same family.
|
||||
@ -32,7 +32,7 @@ impl Debug for FontVariant {
|
||||
|
||||
/// The style of a font.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, Cast)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum FontStyle {
|
||||
/// The default, typically upright style.
|
||||
@ -62,24 +62,6 @@ impl Default for FontStyle {
|
||||
}
|
||||
}
|
||||
|
||||
cast_from_value! {
|
||||
FontStyle,
|
||||
/// The default, typically upright style.
|
||||
"normal" => Self::Normal,
|
||||
/// A cursive style with custom letterform.
|
||||
"italic" => Self::Italic,
|
||||
/// Just a slanted version of the normal style.
|
||||
"oblique" => Self::Oblique,
|
||||
}
|
||||
|
||||
cast_to_value! {
|
||||
v: FontStyle => Value::from(match v {
|
||||
FontStyle::Normal => "normal",
|
||||
FontStyle::Italic => "italic",
|
||||
FontStyle::Oblique => "oblique",
|
||||
})
|
||||
}
|
||||
|
||||
/// The weight of a font.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
|
Loading…
x
Reference in New Issue
Block a user