Skip to main content

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
18pub use self::cbor_::*;
19pub use self::csv_::*;
20pub use self::json_::*;
21pub use self::read_::*;
22pub use self::toml_::*;
23pub use self::xml_::*;
24pub use self::yaml_::*;
25
26use comemo::Tracked;
27use typst_syntax::{FileId, Spanned};
28
29use crate::World;
30use crate::diag::{At, HintedString, SourceResult};
31use crate::foundations::{Bytes, OneOrMultiple, PathOrStr, Scope, Str, cast};
32
33/// Hook up all `data-loading` definitions.
34pub(super) fn define(global: &mut Scope) {
35    global.start_category(crate::Category::DataLoading);
36    global.define_func::<read>();
37    global.define_func::<csv>();
38    global.define_func::<json>();
39    global.define_func::<toml>();
40    global.define_func::<yaml>();
41    global.define_func::<cbor>();
42    global.define_func::<xml>();
43    global.reset_category();
44}
45
46/// Something we can retrieve byte data from.
47#[derive(Debug, Clone, PartialEq, Hash)]
48pub enum DataSource {
49    /// A path to a file.
50    Path(PathOrStr),
51    /// Raw bytes.
52    Bytes(Bytes),
53}
54
55cast! {
56    DataSource,
57    self => match self {
58        Self::Path(v) => v.into_value(),
59        Self::Bytes(v) => v.into_value(),
60    },
61    v: PathOrStr => Self::Path(v),
62    v: Bytes => Self::Bytes(v),
63}
64
65/// Loads data from a path or provided bytes.
66pub trait Load {
67    /// Bytes or a list of bytes (if there are multiple sources).
68    type Output;
69
70    /// Load the bytes.
71    fn load(&self, world: Tracked<dyn World + '_>) -> SourceResult<Self::Output>;
72}
73
74impl Load for Spanned<DataSource> {
75    type Output = Loaded;
76
77    fn load(&self, world: Tracked<dyn World + '_>) -> SourceResult<Self::Output> {
78        self.as_ref().load(world)
79    }
80}
81
82impl Load for Spanned<&DataSource> {
83    type Output = Loaded;
84
85    fn load(&self, world: Tracked<dyn World + '_>) -> SourceResult<Self::Output> {
86        match self.v {
87            DataSource::Path(path) => {
88                let resolved =
89                    path.resolve_if_some(self.span.id()).at(self.span)?.intern();
90                let data = world
91                    .file(resolved)
92                    .map_err(|error| {
93                        let mut hinted = HintedString::from(error);
94                        if let PathOrStr::Str(string) = &path
95                            && (string.as_str().starts_with("http://")
96                                || string.as_str().starts_with("https://"))
97                        {
98                            hinted.hint("network access is not supported");
99                        }
100                        hinted
101                    })
102                    .at(self.span)?;
103                let source = Spanned::new(LoadSource::Path(resolved), self.span);
104                Ok(Loaded::new(source, data))
105            }
106            DataSource::Bytes(data) => {
107                let source = Spanned::new(LoadSource::Bytes, self.span);
108                Ok(Loaded::new(source, data.clone()))
109            }
110        }
111    }
112}
113
114impl Load for Spanned<OneOrMultiple<DataSource>> {
115    type Output = Vec<Loaded>;
116
117    fn load(&self, world: Tracked<dyn World + '_>) -> SourceResult<Self::Output> {
118        self.as_ref().load(world)
119    }
120}
121
122impl Load for Spanned<&OneOrMultiple<DataSource>> {
123    type Output = Vec<Loaded>;
124
125    fn load(&self, world: Tracked<dyn World + '_>) -> SourceResult<Self::Output> {
126        self.v
127            .0
128            .iter()
129            .map(|source| Spanned::new(source, self.span).load(world))
130            .collect()
131    }
132}
133
134/// Data loaded from a [`DataSource`].
135#[derive(Debug, Clone, Eq, PartialEq, Hash)]
136pub struct Loaded {
137    /// Details about where `data` was loaded from.
138    pub source: Spanned<LoadSource>,
139    /// The loaded data.
140    pub data: Bytes,
141}
142
143impl Loaded {
144    pub fn new(source: Spanned<LoadSource>, bytes: Bytes) -> Self {
145        Self { source, data: bytes }
146    }
147}
148
149/// A loaded [`DataSource`].
150#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
151pub enum LoadSource {
152    Path(FileId),
153    Bytes,
154}
155
156/// A value that can be read from a file.
157#[derive(Debug, Clone, PartialEq, Hash)]
158pub enum Readable {
159    /// A decoded string.
160    Str(Str),
161    /// Raw bytes.
162    Bytes(Bytes),
163}
164
165impl Readable {
166    pub fn into_bytes(self) -> Bytes {
167        match self {
168            Self::Bytes(v) => v,
169            Self::Str(v) => Bytes::from_string(v),
170        }
171    }
172
173    pub fn into_source(self) -> DataSource {
174        DataSource::Bytes(self.into_bytes())
175    }
176}
177
178cast! {
179    Readable,
180    self => match self {
181        Self::Str(v) => v.into_value(),
182        Self::Bytes(v) => v.into_value(),
183    },
184    v: Str => Self::Str(v),
185    v: Bytes => Self::Bytes(v),
186}