typst_library/foundations/module.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
use std::fmt::{self, Debug, Formatter};
use std::sync::Arc;
use ecow::{eco_format, EcoString};
use typst_syntax::FileId;
use crate::diag::{bail, DeprecationSink, StrResult};
use crate::foundations::{repr, ty, Content, Scope, Value};
/// An module of definitions.
///
/// A module
/// - be built-in
/// - stem from a [file import]($scripting/#modules)
/// - stem from a [package import]($scripting/#packages) (and thus indirectly
/// its entrypoint file)
/// - result from a call to the [plugin]($plugin) function
///
/// You can access definitions from the module using [field access
/// notation]($scripting/#fields) and interact with it using the [import and
/// include syntaxes]($scripting/#modules). Alternatively, it is possible to
/// convert a module to a dictionary, and therefore access its contents
/// dynamically, using the [dictionary constructor]($dictionary/#constructor).
///
/// # Example
/// ```example
/// <<< #import "utils.typ"
/// <<< #utils.add(2, 5)
///
/// <<< #import utils: sub
/// <<< #sub(1, 4)
/// >>> #7
/// >>>
/// >>> #(-3)
/// ```
#[ty(cast)]
#[derive(Clone, Hash)]
#[allow(clippy::derived_hash_with_manual_eq)]
pub struct Module {
/// The module's name.
name: Option<EcoString>,
/// The reference-counted inner fields.
inner: Arc<Repr>,
}
/// The internal representation.
#[derive(Debug, Clone, Hash)]
struct Repr {
/// The top-level definitions that were bound in this module.
scope: Scope,
/// The module's layoutable contents.
content: Content,
/// The id of the file which defines the module, if any.
file_id: Option<FileId>,
}
impl Module {
/// Create a new module.
pub fn new(name: impl Into<EcoString>, scope: Scope) -> Self {
Self {
name: Some(name.into()),
inner: Arc::new(Repr { scope, content: Content::empty(), file_id: None }),
}
}
/// Create a new anonymous module without a name.
pub fn anonymous(scope: Scope) -> Self {
Self {
name: None,
inner: Arc::new(Repr { scope, content: Content::empty(), file_id: None }),
}
}
/// Update the module's name.
pub fn with_name(mut self, name: impl Into<EcoString>) -> Self {
self.name = Some(name.into());
self
}
/// Update the module's scope.
pub fn with_scope(mut self, scope: Scope) -> Self {
Arc::make_mut(&mut self.inner).scope = scope;
self
}
/// Update the module's content.
pub fn with_content(mut self, content: Content) -> Self {
Arc::make_mut(&mut self.inner).content = content;
self
}
/// Update the module's file id.
pub fn with_file_id(mut self, file_id: FileId) -> Self {
Arc::make_mut(&mut self.inner).file_id = Some(file_id);
self
}
/// Get the module's name.
pub fn name(&self) -> Option<&EcoString> {
self.name.as_ref()
}
/// Access the module's scope.
pub fn scope(&self) -> &Scope {
&self.inner.scope
}
/// Access the module's file id.
///
/// Some modules are not associated with a file, like the built-in modules.
pub fn file_id(&self) -> Option<FileId> {
self.inner.file_id
}
/// Access the module's scope, mutably.
pub fn scope_mut(&mut self) -> &mut Scope {
&mut Arc::make_mut(&mut self.inner).scope
}
/// Try to access a definition in the module.
pub fn field(&self, field: &str, sink: impl DeprecationSink) -> StrResult<&Value> {
match self.scope().get(field) {
Some(binding) => Ok(binding.read_checked(sink)),
None => match &self.name {
Some(name) => bail!("module `{name}` does not contain `{field}`"),
None => bail!("module does not contain `{field}`"),
},
}
}
/// Extract the module's content.
pub fn content(self) -> Content {
match Arc::try_unwrap(self.inner) {
Ok(repr) => repr.content,
Err(arc) => arc.content.clone(),
}
}
}
impl Debug for Module {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("Module")
.field("name", &self.name)
.field("scope", &self.inner.scope)
.field("content", &self.inner.content)
.finish()
}
}
impl repr::Repr for Module {
fn repr(&self) -> EcoString {
match &self.name {
Some(module) => eco_format!("<module {module}>"),
None => "<module>".into(),
}
}
}
impl PartialEq for Module {
fn eq(&self, other: &Self) -> bool {
self.name == other.name && Arc::ptr_eq(&self.inner, &other.inner)
}
}