typst_library/foundations/ty.rs
1#[doc(inline)]
2pub use typst_macros::{scope, ty};
3
4use std::cmp::Ordering;
5use std::fmt::{self, Debug, Display, Formatter};
6use std::sync::LazyLock;
7
8use ecow::{EcoString, eco_format};
9use typst_utils::Static;
10
11use crate::diag::{DeprecationSink, StrResult, bail};
12use crate::foundations::{
13 AutoValue, Func, NativeFuncData, NoneValue, Repr, Scope, Value, cast, func,
14};
15
16/// Describes a kind of value.
17///
18/// To style your document, you need to work with values of different kinds:
19/// Lengths specifying the size of your elements, colors for your text and
20/// shapes, and more. Typst categorizes these into clearly defined _types_ and
21/// tells you where it expects which type of value.
22///
23/// Apart from basic types for numeric values and [typical]($int)
24/// [types]($float) [known]($str) [from]($array) [programming]($dictionary)
25/// languages, Typst provides a special type for [_content._]($content) A value
26/// of this type can hold anything that you can enter into your document: Text,
27/// elements like headings and shapes, and style information.
28///
29/// # Example
30/// ```example
31/// #let x = 10
32/// #if type(x) == int [
33/// #x is an integer!
34/// ] else [
35/// #x is another value...
36/// ]
37///
38/// An image is of type
39/// #type(image("glacier.jpg")).
40/// ```
41///
42/// The type of `{10}` is `int`. Now, what is the type of `int` or even `type`?
43/// ```example
44/// #type(int) \
45/// #type(type)
46/// ```
47///
48/// Unlike other types like `int`, [none] and [auto] do not have a name
49/// representing them. To test if a value is one of these, compare your value to
50/// them directly, e.g:
51/// ```example
52/// #let val = none
53/// #if val == none [
54/// Yep, it's none.
55/// ]
56/// ```
57///
58/// Note that `type` will return [`content`] for all document elements. To
59/// programmatically determine which kind of content you are dealing with, see
60/// [`content.func`].
61#[ty(scope, cast)]
62#[derive(Copy, Clone, Eq, PartialEq, Hash)]
63pub struct Type(Static<NativeTypeData>);
64
65impl Type {
66 /// Get the type for `T`.
67 pub fn of<T: NativeType>() -> Self {
68 T::ty()
69 }
70
71 /// The type's short name, how it is used in code (e.g. `str`).
72 pub fn short_name(&self) -> &'static str {
73 self.0.name
74 }
75
76 /// The type's long name, for use in diagnostics (e.g. `string`).
77 pub fn long_name(&self) -> &'static str {
78 self.0.long_name
79 }
80
81 /// The type's title case name, for use in documentation (e.g. `String`).
82 pub fn title(&self) -> &'static str {
83 self.0.title
84 }
85
86 /// Documentation for the type (as Markdown).
87 pub fn docs(&self) -> &'static str {
88 self.0.docs
89 }
90
91 /// Search keywords for the type.
92 pub fn keywords(&self) -> &'static [&'static str] {
93 self.0.keywords
94 }
95
96 /// This type's constructor function.
97 pub fn constructor(&self) -> StrResult<Func> {
98 self.0
99 .constructor
100 .as_ref()
101 .map(|lazy| Func::from(*lazy))
102 .ok_or_else(|| eco_format!("type {self} does not have a constructor"))
103 }
104
105 /// The type's associated scope that holds sub-definitions.
106 pub fn scope(&self) -> &'static Scope {
107 &(self.0).0.scope
108 }
109
110 /// Get a field from this type's scope, if possible.
111 pub fn field(
112 &self,
113 field: &str,
114 sink: impl DeprecationSink,
115 ) -> StrResult<&'static Value> {
116 match self.scope().get(field) {
117 Some(binding) => Ok(binding.read_checked(sink)),
118 None => bail!("type {self} does not contain field `{field}`"),
119 }
120 }
121}
122
123#[scope]
124impl Type {
125 /// Determines a value's type.
126 ///
127 /// ```example
128 /// #type(12) \
129 /// #type(14.7) \
130 /// #type("hello") \
131 /// #type(<glacier>) \
132 /// #type([Hi]) \
133 /// #type(x => x + 1) \
134 /// #type(type)
135 /// ```
136 #[func(constructor)]
137 pub fn construct(
138 /// The value whose type's to determine.
139 value: Value,
140 ) -> Type {
141 value.ty()
142 }
143}
144
145impl Debug for Type {
146 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
147 write!(f, "Type({})", self.long_name())
148 }
149}
150
151impl Repr for Type {
152 fn repr(&self) -> EcoString {
153 if *self == Type::of::<AutoValue>() {
154 "type(auto)"
155 } else if *self == Type::of::<NoneValue>() {
156 "type(none)"
157 } else {
158 self.short_name()
159 }
160 .into()
161 }
162}
163
164impl Display for Type {
165 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
166 f.pad(self.long_name())
167 }
168}
169
170impl Ord for Type {
171 fn cmp(&self, other: &Self) -> Ordering {
172 self.long_name().cmp(other.long_name())
173 }
174}
175
176impl PartialOrd for Type {
177 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
178 Some(self.cmp(other))
179 }
180}
181
182/// A Typst type that is defined by a native Rust type.
183pub trait NativeType {
184 /// The type's name.
185 ///
186 /// In contrast to `data()`, this is usable in const contexts.
187 const NAME: &'static str;
188
189 /// Get the type for the native Rust type.
190 fn ty() -> Type {
191 Type::from(Self::data())
192 }
193
194 // Get the type data for the native Rust type.
195 fn data() -> &'static NativeTypeData;
196}
197
198/// Defines a native type.
199#[derive(Debug)]
200pub struct NativeTypeData {
201 /// The type's normal name (e.g. `str`), as exposed to Typst.
202 pub name: &'static str,
203 /// The type's long name (e.g. `string`), for error messages.
204 pub long_name: &'static str,
205 /// The function's title case name (e.g. `String`).
206 pub title: &'static str,
207 /// The documentation for this type as a string.
208 pub docs: &'static str,
209 /// A list of alternate search terms for this type.
210 pub keywords: &'static [&'static str],
211 /// The constructor for this type.
212 pub constructor: LazyLock<Option<&'static NativeFuncData>>,
213 /// Definitions in the scope of the type.
214 pub scope: LazyLock<Scope>,
215}
216
217impl From<&'static NativeTypeData> for Type {
218 fn from(data: &'static NativeTypeData) -> Self {
219 Self(Static(data))
220 }
221}
222
223cast! {
224 &'static NativeTypeData,
225 self => Type::from(self).into_value(),
226}