Autogenerate default cast in#[ty] unless cast is specified

This commit is contained in:
Laurenz 2024-01-12 14:36:41 +01:00
parent 1834ebc529
commit 37249c20f7
42 changed files with 58 additions and 92 deletions

View File

@ -116,6 +116,8 @@ pub fn func(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream {
/// You can customize some properties of the resulting type:
/// - `scope`: Indicates that the type has an associated scope defined by the
/// `#[scope]` macro
/// - `cast`: Indicates that the type has a custom `cast!` implementation.
/// The macro will then not autogenerate one.
/// - `name`: The type's normal name (e.g. `str`). Defaults to the Rust name in
/// kebab-case.
/// - `title`: The type's title case name (e.g. `String`). Defaults to the

View File

@ -28,18 +28,18 @@ pub fn ty(stream: TokenStream, item: syn::Item) -> Result<TokenStream> {
/// Holds all relevant parsed data about a type.
struct Type {
meta: Meta,
ident: Ident,
name: String,
long: String,
scope: bool,
title: String,
docs: String,
keywords: Vec<String>,
}
/// The `..` in `#[ty(..)]`.
struct Meta {
scope: bool,
cast: bool,
name: Option<String>,
title: Option<String>,
keywords: Vec<String>,
@ -49,6 +49,7 @@ impl Parse for Meta {
fn parse(input: ParseStream) -> Result<Self> {
Ok(Self {
scope: parse_flag::<kw::scope>(input)?,
cast: parse_flag::<kw::cast>(input)?,
name: parse_string::<kw::name>(input)?,
title: parse_string::<kw::title>(input)?,
keywords: parse_string_array::<kw::keywords>(input)?,
@ -59,37 +60,35 @@ impl Parse for Meta {
/// Parse details about the type from its definition.
fn parse(meta: Meta, ident: Ident, attrs: &[Attribute]) -> Result<Type> {
let docs = documentation(attrs);
let (name, title) = determine_name_and_title(meta.name, meta.title, &ident, None)?;
let (name, title) =
determine_name_and_title(meta.name.clone(), meta.title.clone(), &ident, None)?;
let long = title.to_lowercase();
Ok(Type {
ident,
name,
long,
scope: meta.scope,
keywords: meta.keywords,
title,
docs,
})
Ok(Type { meta, ident, name, long, title, docs })
}
/// Produce the output of the macro.
fn create(ty: &Type, item: Option<&syn::Item>) -> TokenStream {
let Type {
ident, name, long, title, docs, keywords, scope, ..
} = ty;
let Type { ident, name, long, title, docs, meta, .. } = ty;
let Meta { keywords, .. } = meta;
let constructor = if *scope {
let constructor = if meta.scope {
quote! { <#ident as #foundations::NativeScope>::constructor() }
} else {
quote! { None }
};
let scope = if *scope {
let scope = if meta.scope {
quote! { <#ident as #foundations::NativeScope>::scope() }
} else {
quote! { #foundations::Scope::new() }
};
let cast = (!meta.cast).then(|| {
quote! {
#foundations::cast! { type #ident, }
}
});
let data = quote! {
#foundations::NativeTypeData {
name: #name,
@ -104,6 +103,7 @@ fn create(ty: &Type, item: Option<&syn::Item>) -> TokenStream {
quote! {
#item
#cast
impl #foundations::NativeType for #ident {
const NAME: &'static str = #name;

View File

@ -255,6 +255,7 @@ pub mod kw {
syn::custom_keyword!(span);
syn::custom_keyword!(title);
syn::custom_keyword!(scope);
syn::custom_keyword!(cast);
syn::custom_keyword!(constructor);
syn::custom_keyword!(keywords);
syn::custom_keyword!(parent);

View File

@ -38,7 +38,7 @@ use crate::syntax::{Span, Spanned};
/// #let dict = (fill: blue)
/// #text(..dict)[Hello]
/// ```
#[ty(scope, name = "arguments")]
#[ty(scope, cast, name = "arguments")]
#[derive(Clone, Hash)]
#[allow(clippy::derived_hash_with_manual_eq)]
pub struct Args {

View File

@ -67,7 +67,7 @@ pub use crate::__array as array;
/// #(("A", "B", "C")
/// .join(", ", last: " and "))
/// ```
#[ty(scope)]
#[ty(scope, cast)]
#[derive(Default, Clone, PartialEq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Array(EcoVec<Value>);

View File

@ -15,7 +15,7 @@ use crate::foundations::{
/// contextual behaviour. A good example is the [text direction]($text.dir)
/// parameter. Setting it to `{auto}` lets Typst automatically determine the
/// direction from the [text language]($text.lang).
#[ty(name = "auto")]
#[ty(cast, name = "auto")]
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct AutoValue;

View File

@ -13,7 +13,7 @@ use crate::foundations::{ty, Repr};
/// #true \
/// #(1 < 2)
/// ```
#[ty(title = "Boolean")]
#[ty(cast, title = "Boolean")]
type bool;
impl Repr for bool {

View File

@ -37,7 +37,7 @@ use crate::foundations::{cast, func, scope, ty, Array, Reflect, Repr, Str, Value
/// #array(data.slice(0, 4)) \
/// #str(data.slice(1, 4))
/// ```
#[ty(scope)]
#[ty(scope, cast)]
#[derive(Clone, Hash, Eq, PartialEq)]
pub struct Bytes(Arc<Prehashed<Cow<'static, [u8]>>>);

View File

@ -65,7 +65,7 @@ use crate::util::fat;
/// In the web app, you can hover over a content variable to see exactly which
/// elements the content is composed of and what fields they have.
/// Alternatively, you can inspect the output of the [`repr`]($repr) function.
#[ty(scope)]
#[ty(scope, cast)]
#[derive(Clone, Hash)]
#[allow(clippy::derived_hash_with_manual_eq)]
pub struct Content(Arc<dyn NativeElement>);

View File

@ -111,7 +111,7 @@ use crate::World;
/// will be stored as a plain date internally, meaning that you cannot use
/// components such as `hour` or `minute`, which would only work on datetimes
/// that have a specified time.
#[ty(scope)]
#[ty(scope, cast)]
#[derive(Debug, Clone, Copy, PartialEq, Hash)]
pub enum Datetime {
/// Representation as a date.

View File

@ -62,7 +62,7 @@ pub use crate::__dict as dict;
/// #dict.insert("city", "Berlin ")
/// #("name" in dict)
/// ```
#[ty(scope, name = "dictionary")]
#[ty(scope, cast, name = "dictionary")]
#[derive(Default, Clone, PartialEq)]
pub struct Dict(Arc<IndexMap<Str, Value>>);

View File

@ -7,7 +7,7 @@ use time::ext::NumericalDuration;
use crate::foundations::{func, repr, scope, ty, Repr};
/// Represents a positive or negative span of time.
#[ty(scope)]
#[ty(scope, cast)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Duration(time::Duration);

View File

@ -19,7 +19,7 @@ use crate::layout::Ratio;
/// #1e4 \
/// #(10 / 4)
/// ```
#[ty(scope, name = "float")]
#[ty(scope, cast, name = "float")]
type f64;
#[scope]

View File

@ -123,7 +123,7 @@ pub use typst_macros::func;
/// The only exception are built-in methods like
/// [`array.push(value)`]($array.push). These can modify the values they are
/// called on.
#[ty(scope, name = "function")]
#[ty(scope, cast, name = "function")]
#[derive(Clone, Hash)]
#[allow(clippy::derived_hash_with_manual_eq)]
pub struct Func {

View File

@ -25,7 +25,7 @@ use crate::foundations::{cast, func, repr, scope, ty, Repr, Str, Value};
/// #0o10 \
/// #0b1001
/// ```
#[ty(scope, name = "int", title = "Integer")]
#[ty(scope, cast, name = "int", title = "Integer")]
type i64;
#[scope]

View File

@ -31,7 +31,7 @@ use crate::util::PicoStr;
///
/// Currently, labels can only be attached to elements in markup mode, not in
/// code mode. This might change in the future.
#[ty(scope)]
#[ty(scope, cast)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Label(PicoStr);

View File

@ -23,7 +23,7 @@ use crate::foundations::{repr, ty, Content, Scope, Value};
/// >>>
/// >>> #(-3)
/// ```
#[ty]
#[ty(cast)]
#[derive(Clone, Hash)]
#[allow(clippy::derived_hash_with_manual_eq)]
pub struct Module {

View File

@ -20,7 +20,7 @@ use crate::foundations::{
/// ```example
/// Not visible: #none
/// ```
#[ty(name = "none")]
#[ty(cast, name = "none")]
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct NoneValue;

View File

@ -115,7 +115,7 @@ use crate::World;
/// - Wrappers to help you write your plugin in Rust (Zig wrapper in
/// development)
/// - A stubber for WASI
#[ty(scope)]
#[ty(scope, cast)]
#[derive(Clone)]
pub struct Plugin(Arc<Repr>);

View File

@ -76,7 +76,7 @@ pub use crate::__select_where as select_where;
/// == So will this
/// === But this will not.
/// ```
#[ty(scope)]
#[ty(scope, cast)]
#[derive(Debug, Clone, PartialEq, Hash)]
pub enum Selector {
/// Matches a specific type of element.

View File

@ -67,7 +67,7 @@ pub use ecow::eco_format;
/// - `[\r]` for a carriage return
/// - `[\t]` for a tab
/// - `[\u{1f600}]` for a hexadecimal Unicode escape sequence
#[ty(scope, title = "String")]
#[ty(scope, cast, title = "String")]
#[derive(Default, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
@ -898,10 +898,6 @@ impl Hash for Regex {
}
}
cast! {
type Regex,
}
/// A pattern which can be searched for in a string.
#[derive(Debug, Clone)]
pub enum StrPattern {

View File

@ -64,7 +64,7 @@ impl Show for StyleElem {
}
/// A list of style properties.
#[ty]
#[ty(cast)]
#[derive(Default, PartialEq, Clone, Hash)]
pub struct Styles(EcoVec<Prehashed<Style>>);

View File

@ -53,7 +53,7 @@ pub use typst_macros::{scope, ty};
/// - Adding/joining a type and string will yield a string
/// - The `{in}` operator on a type and a dictionary will evaluate to `{true}`
/// if the dictionary has a string key matching the type's name
#[ty(scope)]
#[ty(scope, cast)]
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Type(Static<NativeTypeData>);

View File

@ -20,7 +20,7 @@ use crate::foundations::{cast, func, repr, scope, ty, Repr};
///
/// You can convert a version to an array of explicitly given components using
/// the [`array`]($array) constructor.
#[ty(scope)]
#[ty(scope, cast)]
#[derive(Debug, Default, Clone, Hash)]
#[allow(clippy::derived_hash_with_manual_eq)]
pub struct Version(EcoVec<u32>);

View File

@ -475,10 +475,6 @@ impl Repr for Counter {
}
}
cast! {
type Counter,
}
/// Identifies a counter.
#[derive(Debug, Clone, PartialEq, Hash)]
pub enum CounterKey {
@ -521,7 +517,7 @@ impl Repr for CounterKey {
}
/// An update to perform on a counter.
#[ty]
#[ty(cast)]
#[derive(Debug, Clone, PartialEq, Hash)]
pub enum CounterUpdate {
/// Set the counter to the specified state.

View File

@ -3,7 +3,7 @@ use std::num::NonZeroUsize;
use ecow::EcoString;
use crate::engine::Engine;
use crate::foundations::{cast, func, scope, ty, Dict, Repr};
use crate::foundations::{func, scope, ty, Dict, Repr};
use crate::model::Numbering;
/// Identifies an element in the document.
@ -79,9 +79,5 @@ impl Repr for Location {
}
}
cast! {
type Location,
}
/// Makes this element locatable through `engine.locate`.
pub trait Locatable {}

View File

@ -26,8 +26,7 @@ use ecow::{eco_format, EcoString};
use smallvec::SmallVec;
use crate::foundations::{
cast, category, elem, ty, Behave, Behaviour, Category, Content, Repr, Scope,
Unlabellable,
category, elem, ty, Behave, Behaviour, Category, Content, Repr, Scope, Unlabellable,
};
use crate::layout::PdfPageLabel;
use crate::model::{Destination, Numbering};
@ -89,10 +88,6 @@ pub enum Meta {
Hide,
}
cast! {
type Meta,
}
impl Debug for Meta {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {

View File

@ -351,12 +351,8 @@ impl Repr for State {
}
}
cast! {
type State,
}
/// An update to perform on a state.
#[ty]
#[ty(cast)]
#[derive(Debug, Clone, PartialEq, Hash)]
pub enum StateUpdate {
/// Set the state to the specified value.

View File

@ -245,10 +245,6 @@ impl From<Side> for Alignment {
}
}
cast! {
type Alignment,
}
/// Where to align something horizontally.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub enum HAlignment {

View File

@ -19,7 +19,7 @@ use crate::util::{Numeric, Scalar};
/// ```example
/// #rotate(10deg)[Hello there!]
/// ```
#[ty(scope)]
#[ty(scope, cast)]
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Angle(Scalar);

View File

@ -1,6 +1,6 @@
use ecow::EcoString;
use crate::foundations::{cast, func, scope, ty, Repr};
use crate::foundations::{func, scope, ty, Repr};
use crate::layout::{Axis, Side};
/// The four directions into which content can be laid out.
@ -130,7 +130,3 @@ impl Repr for Dir {
}
}
}
cast! {
type Dir,
}

View File

@ -20,7 +20,7 @@ use crate::util::{Numeric, Scalar};
/// ```example
/// Left #h(1fr) Left-ish #h(2fr) Right
/// ```
#[ty(name = "fraction")]
#[ty(cast, name = "fraction")]
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Fr(Scalar);

View File

@ -38,7 +38,7 @@ use crate::util::Numeric;
/// - `abs`: A length with just the absolute component of the current length
/// (that is, excluding the `em` component).
/// - `em`: The amount of `em` units in this length, as a [float]($float).
#[ty(scope)]
#[ty(scope, cast)]
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Length {
/// The absolute part.

View File

@ -17,7 +17,7 @@ use crate::util::{Numeric, Scalar};
/// Scaled apart.
/// ]
/// ```
#[ty]
#[ty(cast)]
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Ratio(Scalar);

View File

@ -25,7 +25,7 @@ use crate::util::Numeric;
/// A relative length has the following fields:
/// - `length`: Its length component.
/// - `ratio`: Its ratio component.
#[ty(name = "relative", title = "Relative Length")]
#[ty(cast, name = "relative", title = "Relative Length")]
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Rel<T: Numeric = Length> {
/// The relative part.

View File

@ -421,10 +421,6 @@ impl Repr for Bibliography {
}
}
cast! {
type Bibliography,
}
/// Format a BibLaTeX loading error.
fn format_biblatex_error(path: &str, src: &str, errors: Vec<BibLaTeXError>) -> EcoString {
let Some(error) = errors.first() else {
@ -440,7 +436,7 @@ fn format_biblatex_error(path: &str, src: &str, errors: Vec<BibLaTeXError>) -> E
}
/// A loaded CSL style.
#[ty]
#[ty(cast)]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct CslStyle {
name: Option<EcoString>,

View File

@ -42,7 +42,7 @@ pub use typst_macros::symbols;
/// $arrow.r$ \
/// $arrow.t.quad$
/// ```
#[ty(scope)]
#[ty(scope, cast)]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Symbol(Repr);

View File

@ -5,7 +5,7 @@ use ecow::{eco_format, EcoString};
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{cast, elem, ty, Content, Fold, Repr, Show, Smart, StyleChain};
use crate::foundations::{elem, ty, Content, Fold, Repr, Show, Smart, StyleChain};
use crate::layout::{Abs, Em, Frame, FrameItem, Length, Point, Size};
use crate::syntax::Span;
use crate::text::{
@ -363,10 +363,6 @@ impl Repr for Decoration {
}
}
cast! {
type Decoration,
}
/// A kind of decorative line.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
enum DecoLine {

View File

@ -163,7 +163,7 @@ const ANGLE_EPSILON: f32 = 1e-5;
/// )
/// }))
/// ```
#[ty(scope)]
#[ty(scope, cast)]
#[derive(Copy, Clone)]
pub enum Color {
/// A 32-bit luma color.

View File

@ -175,7 +175,7 @@ use crate::visualize::{Color, ColorSpace, WeightedColor};
/// [`color.oklab`]($color.oklab) colors with extra stops in between. This
/// avoids needing to encode these color spaces in your PDF file, but it does
/// add extra stops to your gradient, which can increase the file size.
#[ty(scope)]
#[ty(scope, cast)]
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Gradient {
Linear(Arc<LinearGradient>),

View File

@ -95,7 +95,7 @@ use crate::World;
/// that are implicitly created by show rules and elements. For example, a
/// [`rotate`]($rotate) will not affect the parent of a gradient, but a
/// [`grid`]($grid) will.
#[ty(scope)]
#[ty(scope, cast)]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Pattern(Arc<Repr>);

View File

@ -49,7 +49,7 @@ use crate::visualize::{Color, Gradient, Paint, Pattern};
/// constructor function. For example, `{(2pt + blue).thickness}` is `{2pt}`.
/// Meanwhile, `{stroke(red).cap}` is `{auto}` because it's unspecified. Fields
/// set to `{auto}` are inherited.
#[ty(scope)]
#[ty(scope, cast)]
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
pub struct Stroke<T: Numeric = Length> {
/// The stroke's paint.