typst_library/loading/
mod.rs

1//! Data loading.
2
3#[path = "cbor.rs"]
4mod cbor_;
5#[path = "csv.rs"]
6mod csv_;
7#[path = "json.rs"]
8mod json_;
9#[path = "read.rs"]
10mod read_;
11#[path = "toml.rs"]
12mod toml_;
13#[path = "xml.rs"]
14mod xml_;
15#[path = "yaml.rs"]
16mod yaml_;
17
18use comemo::Tracked;
19use ecow::EcoString;
20use typst_syntax::Spanned;
21
22pub use self::cbor_::*;
23pub use self::csv_::*;
24pub use self::json_::*;
25pub use self::read_::*;
26pub use self::toml_::*;
27pub use self::xml_::*;
28pub use self::yaml_::*;
29
30use crate::diag::{At, SourceResult};
31use crate::foundations::OneOrMultiple;
32use crate::foundations::{cast, Bytes, Scope, Str};
33use crate::World;
34
35/// Hook up all `data-loading` definitions.
36pub(super) fn define(global: &mut Scope) {
37    global.start_category(crate::Category::DataLoading);
38    global.define_func::<read>();
39    global.define_func::<csv>();
40    global.define_func::<json>();
41    global.define_func::<toml>();
42    global.define_func::<yaml>();
43    global.define_func::<cbor>();
44    global.define_func::<xml>();
45    global.reset_category();
46}
47
48/// Something we can retrieve byte data from.
49#[derive(Debug, Clone, PartialEq, Hash)]
50pub enum DataSource {
51    /// A path to a file.
52    Path(EcoString),
53    /// Raw bytes.
54    Bytes(Bytes),
55}
56
57cast! {
58    DataSource,
59    self => match self {
60        Self::Path(v) => v.into_value(),
61        Self::Bytes(v) => v.into_value(),
62    },
63    v: EcoString => Self::Path(v),
64    v: Bytes => Self::Bytes(v),
65}
66
67/// Loads data from a path or provided bytes.
68pub trait Load {
69    /// Bytes or a list of bytes (if there are multiple sources).
70    type Output;
71
72    /// Load the bytes.
73    fn load(&self, world: Tracked<dyn World + '_>) -> SourceResult<Self::Output>;
74}
75
76impl Load for Spanned<DataSource> {
77    type Output = Bytes;
78
79    fn load(&self, world: Tracked<dyn World + '_>) -> SourceResult<Bytes> {
80        self.as_ref().load(world)
81    }
82}
83
84impl Load for Spanned<&DataSource> {
85    type Output = Bytes;
86
87    fn load(&self, world: Tracked<dyn World + '_>) -> SourceResult<Bytes> {
88        match &self.v {
89            DataSource::Path(path) => {
90                let file_id = self.span.resolve_path(path).at(self.span)?;
91                world.file(file_id).at(self.span)
92            }
93            DataSource::Bytes(bytes) => Ok(bytes.clone()),
94        }
95    }
96}
97
98impl Load for Spanned<OneOrMultiple<DataSource>> {
99    type Output = Vec<Bytes>;
100
101    fn load(&self, world: Tracked<dyn World + '_>) -> SourceResult<Vec<Bytes>> {
102        self.as_ref().load(world)
103    }
104}
105
106impl Load for Spanned<&OneOrMultiple<DataSource>> {
107    type Output = Vec<Bytes>;
108
109    fn load(&self, world: Tracked<dyn World + '_>) -> SourceResult<Vec<Bytes>> {
110        self.v
111            .0
112            .iter()
113            .map(|source| Spanned::new(source, self.span).load(world))
114            .collect()
115    }
116}
117
118/// A value that can be read from a file.
119#[derive(Debug, Clone, PartialEq, Hash)]
120pub enum Readable {
121    /// A decoded string.
122    Str(Str),
123    /// Raw bytes.
124    Bytes(Bytes),
125}
126
127impl Readable {
128    pub fn into_bytes(self) -> Bytes {
129        match self {
130            Self::Bytes(v) => v,
131            Self::Str(v) => Bytes::from_string(v),
132        }
133    }
134
135    pub fn into_source(self) -> DataSource {
136        DataSource::Bytes(self.into_bytes())
137    }
138}
139
140cast! {
141    Readable,
142    self => match self {
143        Self::Str(v) => v.into_value(),
144        Self::Bytes(v) => v.into_value(),
145    },
146    v: Str => Self::Str(v),
147    v: Bytes => Self::Bytes(v),
148}