mirror of
https://github.com/typst/typst
synced 2025-08-24 19:54:14 +08:00
refactor: apply some suggestions
- rename Loaded::bytes to data - change `let data = source.load()` to `let loaded` - only expose a single `within` method for the LoadedWithin trait - invalid utf-8 data is a rare edge case - only store one EcoString inside LoadError - mutate it when using `LoadedWithin::within` method
This commit is contained in:
parent
1c08683248
commit
2d3e883d2b
@ -190,7 +190,7 @@ impl SystemWorld {
|
|||||||
source.lines()
|
source.lines()
|
||||||
} else if let Some(bytes) = slot.file.get() {
|
} else if let Some(bytes) = slot.file.get() {
|
||||||
let bytes = bytes.as_ref().expect("file is not valid");
|
let bytes = bytes.as_ref().expect("file is not valid");
|
||||||
Lines::from_bytes(bytes.as_slice()).expect("file is not valid utf-8")
|
Lines::from_bytes(bytes).expect("file is not valid utf-8")
|
||||||
} else {
|
} else {
|
||||||
panic!("file id does not point to any source file");
|
panic!("file id does not point to any source file");
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
|
|
||||||
use typst_library::diag::{warning, At, LoadedAt, SourceResult, StrResult};
|
use typst_library::diag::{warning, At, LoadedWithin, SourceResult, StrResult};
|
||||||
use typst_library::engine::Engine;
|
use typst_library::engine::Engine;
|
||||||
use typst_library::foundations::{Bytes, Derived, Packed, Smart, StyleChain};
|
use typst_library::foundations::{Bytes, Derived, Packed, Smart, StyleChain};
|
||||||
use typst_library::introspection::Locator;
|
use typst_library::introspection::Locator;
|
||||||
@ -27,17 +27,17 @@ pub fn layout_image(
|
|||||||
|
|
||||||
// Take the format that was explicitly defined, or parse the extension,
|
// Take the format that was explicitly defined, or parse the extension,
|
||||||
// or try to detect the format.
|
// or try to detect the format.
|
||||||
let Derived { source, derived: data } = &elem.source;
|
let Derived { source, derived: loaded } = &elem.source;
|
||||||
let format = match elem.format(styles) {
|
let format = match elem.format(styles) {
|
||||||
Smart::Custom(v) => v,
|
Smart::Custom(v) => v,
|
||||||
Smart::Auto => determine_format(source, &data.bytes).at(span)?,
|
Smart::Auto => determine_format(source, &loaded.data).at(span)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Warn the user if the image contains a foreign object. Not perfect
|
// Warn the user if the image contains a foreign object. Not perfect
|
||||||
// because the svg could also be encoded, but that's an edge case.
|
// because the svg could also be encoded, but that's an edge case.
|
||||||
if format == ImageFormat::Vector(VectorFormat::Svg) {
|
if format == ImageFormat::Vector(VectorFormat::Svg) {
|
||||||
let has_foreign_object =
|
let has_foreign_object =
|
||||||
memchr::memmem::find(&data.bytes, b"<foreignObject").is_some();
|
memchr::memmem::find(&loaded.data, b"<foreignObject").is_some();
|
||||||
|
|
||||||
if has_foreign_object {
|
if has_foreign_object {
|
||||||
engine.sink.warn(warning!(
|
engine.sink.warn(warning!(
|
||||||
@ -53,7 +53,7 @@ pub fn layout_image(
|
|||||||
let kind = match format {
|
let kind = match format {
|
||||||
ImageFormat::Raster(format) => ImageKind::Raster(
|
ImageFormat::Raster(format) => ImageKind::Raster(
|
||||||
RasterImage::new(
|
RasterImage::new(
|
||||||
data.bytes.clone(),
|
loaded.data.clone(),
|
||||||
format,
|
format,
|
||||||
elem.icc(styles).as_ref().map(|icc| icc.derived.clone()),
|
elem.icc(styles).as_ref().map(|icc| icc.derived.clone()),
|
||||||
)
|
)
|
||||||
@ -61,11 +61,11 @@ pub fn layout_image(
|
|||||||
),
|
),
|
||||||
ImageFormat::Vector(VectorFormat::Svg) => ImageKind::Svg(
|
ImageFormat::Vector(VectorFormat::Svg) => ImageKind::Svg(
|
||||||
SvgImage::with_fonts(
|
SvgImage::with_fonts(
|
||||||
data.bytes.clone(),
|
loaded.data.clone(),
|
||||||
engine.world,
|
engine.world,
|
||||||
&families(styles).map(|f| f.as_str()).collect::<Vec<_>>(),
|
&families(styles).map(|f| f.as_str()).collect::<Vec<_>>(),
|
||||||
)
|
)
|
||||||
.in_text(data)?,
|
.within(loaded)?,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Diagnostics.
|
//! Diagnostics.
|
||||||
|
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter, Write as _};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::Utf8Error;
|
use std::str::Utf8Error;
|
||||||
@ -581,9 +581,9 @@ pub type LoadResult<T> = Result<T, LoadError>;
|
|||||||
/// [`FileId`]: typst_syntax::FileId
|
/// [`FileId`]: typst_syntax::FileId
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct LoadError {
|
pub struct LoadError {
|
||||||
pub pos: ReportPos,
|
pos: ReportPos,
|
||||||
pub message: EcoString,
|
/// Must contain a message formatted like this: `"failed to do thing (cause)"`.
|
||||||
pub error: EcoString,
|
message: EcoString,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LoadError {
|
impl LoadError {
|
||||||
@ -594,109 +594,98 @@ impl LoadError {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
pos: pos.into(),
|
pos: pos.into(),
|
||||||
message: eco_format!("{message}"),
|
message: eco_format!("{message} ({error})"),
|
||||||
error: eco_format!("{error}"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a [`LoadResult`] to a [`SourceResult`] by adding the [`Loaded`] context.
|
/// Convert a [`LoadResult`] to a [`SourceResult`] by adding the [`Loaded`] context.
|
||||||
pub trait LoadedAt<T> {
|
pub trait LoadedWithin<T> {
|
||||||
/// Add the span information.
|
/// Report an error, possibly in an external file.
|
||||||
fn in_text(self, data: &Loaded) -> SourceResult<T>;
|
fn within(self, loaded: &Loaded) -> SourceResult<T>;
|
||||||
|
|
||||||
/// Add the span information.
|
|
||||||
fn in_invalid_text(self, data: &Loaded) -> SourceResult<T>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> LoadedAt<T> for Result<T, LoadError> {
|
impl<T> LoadedWithin<T> for Result<T, LoadError> {
|
||||||
/// Report an error, possibly in an external file.
|
fn within(self, loaded: &Loaded) -> SourceResult<T> {
|
||||||
fn in_text(self, data: &Loaded) -> SourceResult<T> {
|
self.map_err(|err| load_err_in_text(loaded, err.pos, err.message))
|
||||||
self.map_err(|err| data.err_in_text(err.pos, err.message, err.error))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Report an error in invalid text.
|
|
||||||
fn in_invalid_text(self, data: &Loaded) -> SourceResult<T> {
|
|
||||||
self.map_err(|err| data.err_in_invalid_text(err.pos, err.message, err.error))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Loaded {
|
/// Report an error, possibly in an external file. This will delegate to
|
||||||
/// Report an error, possibly in an external file.
|
/// [`load_err_in_invalid_text`] if the data isn't valid utf-8.
|
||||||
pub fn err_in_text(
|
fn load_err_in_text(
|
||||||
&self,
|
loaded: &Loaded,
|
||||||
pos: impl Into<ReportPos>,
|
pos: impl Into<ReportPos>,
|
||||||
msg: impl std::fmt::Display,
|
mut message: EcoString,
|
||||||
error: impl std::fmt::Display,
|
) -> EcoVec<SourceDiagnostic> {
|
||||||
) -> EcoVec<SourceDiagnostic> {
|
let pos = pos.into();
|
||||||
let pos = pos.into();
|
// This also does utf-8 validation. Only report an error in an external
|
||||||
// This also does utf-8 validation. Only report an error in an external
|
// file if it is human readable (valid utf-8), otherwise fall back to
|
||||||
// file if it is human readable (valid utf-8), otherwise fall back to
|
// `load_err_in_invalid_text`.
|
||||||
// `err_in_invalid_text`.
|
let lines = Lines::from_bytes(&loaded.data);
|
||||||
let lines = Lines::from_bytes(&self.bytes);
|
match (loaded.source.v, lines) {
|
||||||
match (self.source.v, lines) {
|
(LoadSource::Path(file_id), Ok(lines)) => {
|
||||||
(LoadSource::Path(file_id), Ok(lines)) => {
|
if let Some(range) = pos.range(&lines) {
|
||||||
if let Some(range) = pos.range(&lines) {
|
let span = Span::from_range(file_id, range);
|
||||||
let span = Span::from_range(file_id, range);
|
return eco_vec![SourceDiagnostic::error(span, message)];
|
||||||
return eco_vec!(error!(span, "{msg} ({error})"));
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Either `ReportPos::None` was provided, or resolving the range
|
// Either `ReportPos::None` was provided, or resolving the range
|
||||||
// from the line/column failed. If present report the possibly
|
// from the line/column failed. If present report the possibly
|
||||||
// wrong line/column in the error message anyway.
|
// wrong line/column in the error message anyway.
|
||||||
let span = Span::from_range(file_id, 0..self.bytes.len());
|
let span = Span::from_range(file_id, 0..loaded.data.len());
|
||||||
let error = if let Some(pair) = pos.line_col(&lines) {
|
if let Some(pair) = pos.line_col(&lines) {
|
||||||
let (line, col) = pair.numbers();
|
message.pop();
|
||||||
error!(span, "{msg} ({error} at {line}:{col})")
|
let (line, col) = pair.numbers();
|
||||||
} else {
|
write!(&mut message, " at {line}:{col})").ok();
|
||||||
error!(span, "{msg} ({error})")
|
|
||||||
};
|
|
||||||
eco_vec![error]
|
|
||||||
}
|
}
|
||||||
(LoadSource::Bytes, Ok(lines)) => {
|
eco_vec![SourceDiagnostic::error(span, message)]
|
||||||
let error = if let Some(pair) = pos.line_col(&lines) {
|
|
||||||
let (line, col) = pair.numbers();
|
|
||||||
error!(self.source.span, "{msg} ({error} at {line}:{col})")
|
|
||||||
} else {
|
|
||||||
error!(self.source.span, "{msg} ({error})")
|
|
||||||
};
|
|
||||||
eco_vec![error]
|
|
||||||
}
|
|
||||||
_ => self.err_in_invalid_text(pos, msg, error),
|
|
||||||
}
|
}
|
||||||
|
(LoadSource::Bytes, Ok(lines)) => {
|
||||||
|
if let Some(pair) = pos.line_col(&lines) {
|
||||||
|
message.pop();
|
||||||
|
let (line, col) = pair.numbers();
|
||||||
|
write!(&mut message, " at {line}:{col})").ok();
|
||||||
|
}
|
||||||
|
eco_vec![SourceDiagnostic::error(loaded.source.span, message)]
|
||||||
|
}
|
||||||
|
_ => load_err_in_invalid_text(loaded, pos, message),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Report an error (possibly from an external file) that isn't valid utf-8.
|
/// Report an error (possibly from an external file) that isn't valid utf-8.
|
||||||
pub fn err_in_invalid_text(
|
fn load_err_in_invalid_text(
|
||||||
&self,
|
loaded: &Loaded,
|
||||||
pos: impl Into<ReportPos>,
|
pos: impl Into<ReportPos>,
|
||||||
msg: impl std::fmt::Display,
|
mut message: EcoString,
|
||||||
error: impl std::fmt::Display,
|
) -> EcoVec<SourceDiagnostic> {
|
||||||
) -> EcoVec<SourceDiagnostic> {
|
let line_col = pos.into().try_line_col(&loaded.data).map(|p| p.numbers());
|
||||||
let line_col = pos.into().try_line_col(&self.bytes).map(|p| p.numbers());
|
match (loaded.source.v, line_col) {
|
||||||
let error = match (self.source.v, line_col) {
|
(LoadSource::Path(file), _) => {
|
||||||
(LoadSource::Path(file), _) => {
|
message.pop();
|
||||||
let path = if let Some(package) = file.package() {
|
if let Some(package) = file.package() {
|
||||||
format!("{package}{}", file.vpath().as_rooted_path().display())
|
write!(
|
||||||
} else {
|
&mut message,
|
||||||
format!("{}", file.vpath().as_rootless_path().display())
|
" in {package}{}",
|
||||||
};
|
file.vpath().as_rooted_path().display()
|
||||||
|
)
|
||||||
if let Some((line, col)) = line_col {
|
.ok();
|
||||||
error!(self.source.span, "{msg} ({error} in {path}:{line}:{col})")
|
} else {
|
||||||
} else {
|
write!(&mut message, " in {}", file.vpath().as_rootless_path().display())
|
||||||
error!(self.source.span, "{msg} ({error} in {path})")
|
.ok();
|
||||||
}
|
};
|
||||||
|
if let Some((line, col)) = line_col {
|
||||||
|
write!(&mut message, ":{line}:{col}").ok();
|
||||||
}
|
}
|
||||||
(LoadSource::Bytes, Some((line, col))) => {
|
message.push(')');
|
||||||
error!(self.source.span, "{msg} ({error} at {line}:{col})")
|
}
|
||||||
}
|
(LoadSource::Bytes, Some((line, col))) => {
|
||||||
(LoadSource::Bytes, None) => {
|
message.pop();
|
||||||
error!(self.source.span, "{msg} ({error})")
|
write!(&mut message, " at {line}:{col})").ok();
|
||||||
}
|
}
|
||||||
};
|
(LoadSource::Bytes, None) => (),
|
||||||
eco_vec![error]
|
|
||||||
}
|
}
|
||||||
|
eco_vec![SourceDiagnostic::error(loaded.source.span, message)]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
|
||||||
@ -819,22 +808,21 @@ impl LineCol {
|
|||||||
/// Format a user-facing error message for an XML-like file format.
|
/// Format a user-facing error message for an XML-like file format.
|
||||||
pub fn format_xml_like_error(format: &str, error: roxmltree::Error) -> LoadError {
|
pub fn format_xml_like_error(format: &str, error: roxmltree::Error) -> LoadError {
|
||||||
let pos = LineCol::one_based(error.pos().row as usize, error.pos().col as usize);
|
let pos = LineCol::one_based(error.pos().row as usize, error.pos().col as usize);
|
||||||
let message = eco_format!("failed to parse {format}");
|
let message = match error {
|
||||||
let error = match error {
|
|
||||||
roxmltree::Error::UnexpectedCloseTag(expected, actual, _) => {
|
roxmltree::Error::UnexpectedCloseTag(expected, actual, _) => {
|
||||||
eco_format!("found closing tag '{actual}' instead of '{expected}'")
|
eco_format!("failed to parse {format} (found closing tag '{actual}' instead of '{expected}')")
|
||||||
}
|
}
|
||||||
roxmltree::Error::UnknownEntityReference(entity, _) => {
|
roxmltree::Error::UnknownEntityReference(entity, _) => {
|
||||||
eco_format!("unknown entity '{entity}'")
|
eco_format!("failed to parse {format} (unknown entity '{entity}')")
|
||||||
}
|
}
|
||||||
roxmltree::Error::DuplicatedAttribute(attr, _) => {
|
roxmltree::Error::DuplicatedAttribute(attr, _) => {
|
||||||
eco_format!("duplicate attribute '{attr}'")
|
eco_format!("failed to parse {format} (duplicate attribute '{attr}')")
|
||||||
}
|
}
|
||||||
roxmltree::Error::NoRootNode => {
|
roxmltree::Error::NoRootNode => {
|
||||||
eco_format!("missing root node")
|
eco_format!("failed to parse {format} (missing root node)")
|
||||||
}
|
}
|
||||||
err => eco_format!("{err}"),
|
err => eco_format!("failed to parse {format} ({err})"),
|
||||||
};
|
};
|
||||||
|
|
||||||
LoadError { pos: pos.into(), message, error }
|
LoadError { pos: pos.into(), message }
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ use ecow::{eco_format, EcoString};
|
|||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
use typst_utils::LazyHash;
|
use typst_utils::LazyHash;
|
||||||
|
|
||||||
use crate::diag::{bail, StrResult};
|
use crate::diag::{bail, LoadError, LoadResult, StrResult};
|
||||||
use crate::foundations::{cast, func, scope, ty, Array, Reflect, Repr, Str, Value};
|
use crate::foundations::{cast, func, scope, ty, Array, Reflect, Repr, Str, Value};
|
||||||
|
|
||||||
/// A sequence of bytes.
|
/// A sequence of bytes.
|
||||||
@ -112,6 +112,21 @@ impl Bytes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_str(&self) -> LoadResult<&str> {
|
||||||
|
match self.inner().as_any().downcast_ref::<Str>() {
|
||||||
|
Some(string) => Ok(string.as_str()),
|
||||||
|
None => self.as_str().map_err(|err| {
|
||||||
|
let start = err.valid_up_to();
|
||||||
|
let end = start + err.error_len().unwrap_or(0);
|
||||||
|
LoadError::new(
|
||||||
|
start..end,
|
||||||
|
"failed to convert to string",
|
||||||
|
"file is not valid utf-8",
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Resolve an index or throw an out of bounds error.
|
/// Resolve an index or throw an out of bounds error.
|
||||||
fn locate(&self, index: i64) -> StrResult<usize> {
|
fn locate(&self, index: i64) -> StrResult<usize> {
|
||||||
self.locate_opt(index).ok_or_else(|| out_of_bounds(index, self.len()))
|
self.locate_opt(index).ok_or_else(|| out_of_bounds(index, self.len()))
|
||||||
|
@ -151,8 +151,8 @@ pub fn plugin(
|
|||||||
/// A [path]($syntax/#paths) to a WebAssembly file or raw WebAssembly bytes.
|
/// A [path]($syntax/#paths) to a WebAssembly file or raw WebAssembly bytes.
|
||||||
source: Spanned<DataSource>,
|
source: Spanned<DataSource>,
|
||||||
) -> SourceResult<Module> {
|
) -> SourceResult<Module> {
|
||||||
let data = source.load(engine.world)?;
|
let loaded = source.load(engine.world)?;
|
||||||
Plugin::module(data.bytes).at(source.span)
|
Plugin::module(loaded.data).at(source.span)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[scope]
|
#[scope]
|
||||||
|
@ -23,8 +23,8 @@ pub fn cbor(
|
|||||||
/// A [path]($syntax/#paths) to a CBOR file or raw CBOR bytes.
|
/// A [path]($syntax/#paths) to a CBOR file or raw CBOR bytes.
|
||||||
source: Spanned<DataSource>,
|
source: Spanned<DataSource>,
|
||||||
) -> SourceResult<Value> {
|
) -> SourceResult<Value> {
|
||||||
let data = source.load(engine.world)?;
|
let loaded = source.load(engine.world)?;
|
||||||
ciborium::from_reader(data.bytes.as_slice())
|
ciborium::from_reader(loaded.data.as_slice())
|
||||||
.map_err(|err| eco_format!("failed to parse CBOR ({err})"))
|
.map_err(|err| eco_format!("failed to parse CBOR ({err})"))
|
||||||
.at(source.span)
|
.at(source.span)
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
use az::SaturatingAs;
|
use az::SaturatingAs;
|
||||||
use ecow::EcoVec;
|
|
||||||
use typst_syntax::Spanned;
|
use typst_syntax::Spanned;
|
||||||
|
|
||||||
use crate::diag::{bail, LineCol, ReportPos, SourceDiagnostic, SourceResult};
|
use crate::diag::{bail, LineCol, LoadError, LoadedWithin, ReportPos, SourceResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{cast, func, scope, Array, Dict, IntoValue, Type, Value};
|
use crate::foundations::{cast, func, scope, Array, Dict, IntoValue, Type, Value};
|
||||||
use crate::loading::{DataSource, Load, Loaded, Readable};
|
use crate::loading::{DataSource, Load, Readable};
|
||||||
|
|
||||||
/// Reads structured data from a CSV file.
|
/// Reads structured data from a CSV file.
|
||||||
///
|
///
|
||||||
@ -45,7 +44,7 @@ pub fn csv(
|
|||||||
#[default(RowType::Array)]
|
#[default(RowType::Array)]
|
||||||
row_type: RowType,
|
row_type: RowType,
|
||||||
) -> SourceResult<Array> {
|
) -> SourceResult<Array> {
|
||||||
let data = source.load(engine.world)?;
|
let loaded = source.load(engine.world)?;
|
||||||
|
|
||||||
let mut builder = ::csv::ReaderBuilder::new();
|
let mut builder = ::csv::ReaderBuilder::new();
|
||||||
let has_headers = row_type == RowType::Dict;
|
let has_headers = row_type == RowType::Dict;
|
||||||
@ -54,7 +53,7 @@ pub fn csv(
|
|||||||
|
|
||||||
// Counting lines from 1 by default.
|
// Counting lines from 1 by default.
|
||||||
let mut line_offset: usize = 1;
|
let mut line_offset: usize = 1;
|
||||||
let mut reader = builder.from_reader(data.bytes.as_slice());
|
let mut reader = builder.from_reader(loaded.data.as_slice());
|
||||||
let mut headers: Option<::csv::StringRecord> = None;
|
let mut headers: Option<::csv::StringRecord> = None;
|
||||||
|
|
||||||
if has_headers {
|
if has_headers {
|
||||||
@ -64,7 +63,8 @@ pub fn csv(
|
|||||||
reader
|
reader
|
||||||
.headers()
|
.headers()
|
||||||
.cloned()
|
.cloned()
|
||||||
.map_err(|err| format_csv_error(&data, err, 1))?,
|
.map_err(|err| format_csv_error(err, 1))
|
||||||
|
.within(&loaded)?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ pub fn csv(
|
|||||||
// incorrect with `has_headers` set to `false`. See issue:
|
// incorrect with `has_headers` set to `false`. See issue:
|
||||||
// https://github.com/BurntSushi/rust-csv/issues/184
|
// https://github.com/BurntSushi/rust-csv/issues/184
|
||||||
let line = line + line_offset;
|
let line = line + line_offset;
|
||||||
let row = result.map_err(|err| format_csv_error(&data, err, line))?;
|
let row = result.map_err(|err| format_csv_error(err, line)).within(&loaded)?;
|
||||||
let item = if let Some(headers) = &headers {
|
let item = if let Some(headers) = &headers {
|
||||||
let mut dict = Dict::new();
|
let mut dict = Dict::new();
|
||||||
for (field, value) in headers.iter().zip(&row) {
|
for (field, value) in headers.iter().zip(&row) {
|
||||||
@ -164,11 +164,7 @@ cast! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Format the user-facing CSV error message.
|
/// Format the user-facing CSV error message.
|
||||||
fn format_csv_error(
|
fn format_csv_error(err: ::csv::Error, line: usize) -> LoadError {
|
||||||
data: &Loaded,
|
|
||||||
err: ::csv::Error,
|
|
||||||
line: usize,
|
|
||||||
) -> EcoVec<SourceDiagnostic> {
|
|
||||||
let msg = "failed to parse CSV";
|
let msg = "failed to parse CSV";
|
||||||
let pos = (err.kind().position())
|
let pos = (err.kind().position())
|
||||||
.map(|pos| {
|
.map(|pos| {
|
||||||
@ -178,13 +174,13 @@ fn format_csv_error(
|
|||||||
.unwrap_or(LineCol::one_based(line, 1).into());
|
.unwrap_or(LineCol::one_based(line, 1).into());
|
||||||
match err.kind() {
|
match err.kind() {
|
||||||
::csv::ErrorKind::Utf8 { .. } => {
|
::csv::ErrorKind::Utf8 { .. } => {
|
||||||
data.err_in_text(pos, msg, "file is not valid utf-8")
|
LoadError::new(pos, msg, "file is not valid utf-8")
|
||||||
}
|
}
|
||||||
::csv::ErrorKind::UnequalLengths { expected_len, len, .. } => {
|
::csv::ErrorKind::UnequalLengths { expected_len, len, .. } => {
|
||||||
let err =
|
let err =
|
||||||
format!("found {len} instead of {expected_len} fields in line {line}");
|
format!("found {len} instead of {expected_len} fields in line {line}");
|
||||||
data.err_in_text(pos, msg, err)
|
LoadError::new(pos, msg, err)
|
||||||
}
|
}
|
||||||
_ => data.err_in_text(pos, "failed to parse CSV", err),
|
_ => LoadError::new(pos, "failed to parse CSV", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use ecow::eco_format;
|
use ecow::eco_format;
|
||||||
use typst_syntax::Spanned;
|
use typst_syntax::Spanned;
|
||||||
|
|
||||||
use crate::diag::{At, LineCol, SourceResult};
|
use crate::diag::{At, LineCol, LoadError, LoadedWithin, SourceResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{func, scope, Str, Value};
|
use crate::foundations::{func, scope, Str, Value};
|
||||||
use crate::loading::{DataSource, Load, Readable};
|
use crate::loading::{DataSource, Load, Readable};
|
||||||
@ -54,11 +54,13 @@ pub fn json(
|
|||||||
/// A [path]($syntax/#paths) to a JSON file or raw JSON bytes.
|
/// A [path]($syntax/#paths) to a JSON file or raw JSON bytes.
|
||||||
source: Spanned<DataSource>,
|
source: Spanned<DataSource>,
|
||||||
) -> SourceResult<Value> {
|
) -> SourceResult<Value> {
|
||||||
let data = source.load(engine.world)?;
|
let loaded = source.load(engine.world)?;
|
||||||
serde_json::from_slice(data.bytes.as_slice()).map_err(|err| {
|
serde_json::from_slice(loaded.data.as_slice())
|
||||||
let pos = LineCol::one_based(err.line(), err.column());
|
.map_err(|err| {
|
||||||
data.err_in_text(pos, "failed to parse JSON", err)
|
let pos = LineCol::one_based(err.line(), err.column());
|
||||||
})
|
LoadError::new(pos, "failed to parse JSON", err)
|
||||||
|
})
|
||||||
|
.within(&loaded)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[scope]
|
#[scope]
|
||||||
|
@ -27,7 +27,7 @@ pub use self::toml_::*;
|
|||||||
pub use self::xml_::*;
|
pub use self::xml_::*;
|
||||||
pub use self::yaml_::*;
|
pub use self::yaml_::*;
|
||||||
|
|
||||||
use crate::diag::{At, LoadError, LoadResult, LoadedAt, SourceResult};
|
use crate::diag::{At, LoadedWithin, SourceResult};
|
||||||
use crate::foundations::OneOrMultiple;
|
use crate::foundations::OneOrMultiple;
|
||||||
use crate::foundations::{cast, Bytes, Scope, Str};
|
use crate::foundations::{cast, Bytes, Scope, Str};
|
||||||
use crate::World;
|
use crate::World;
|
||||||
@ -124,16 +124,16 @@ impl Load for Spanned<&OneOrMultiple<DataSource>> {
|
|||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Loaded {
|
pub struct Loaded {
|
||||||
pub source: Spanned<LoadSource>,
|
pub source: Spanned<LoadSource>,
|
||||||
pub bytes: Bytes,
|
pub data: Bytes,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Loaded {
|
impl Loaded {
|
||||||
pub fn new(source: Spanned<LoadSource>, bytes: Bytes) -> Self {
|
pub fn new(source: Spanned<LoadSource>, bytes: Bytes) -> Self {
|
||||||
Self { source, bytes }
|
Self { source, data: bytes }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_str(&self) -> SourceResult<&str> {
|
pub fn load_str(&self) -> SourceResult<&str> {
|
||||||
self.bytes.load_str().in_invalid_text(self)
|
self.data.load_str().within(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,24 +144,6 @@ pub enum LoadSource {
|
|||||||
Bytes,
|
Bytes,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait LoadStr {
|
|
||||||
fn load_str(&self) -> LoadResult<&str>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsRef<[u8]>> LoadStr for T {
|
|
||||||
fn load_str(&self) -> LoadResult<&str> {
|
|
||||||
std::str::from_utf8(self.as_ref()).map_err(|err| {
|
|
||||||
let start = err.valid_up_to();
|
|
||||||
let end = start + err.error_len().unwrap_or(0);
|
|
||||||
LoadError::new(
|
|
||||||
start..end,
|
|
||||||
"failed to convert to string",
|
|
||||||
"file is not valid utf-8",
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A value that can be read from a file.
|
/// A value that can be read from a file.
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||||
pub enum Readable {
|
pub enum Readable {
|
||||||
|
@ -35,10 +35,10 @@ pub fn read(
|
|||||||
#[default(Some(Encoding::Utf8))]
|
#[default(Some(Encoding::Utf8))]
|
||||||
encoding: Option<Encoding>,
|
encoding: Option<Encoding>,
|
||||||
) -> SourceResult<Readable> {
|
) -> SourceResult<Readable> {
|
||||||
let data = path.map(DataSource::Path).load(engine.world)?;
|
let loaded = path.map(DataSource::Path).load(engine.world)?;
|
||||||
Ok(match encoding {
|
Ok(match encoding {
|
||||||
None => Readable::Bytes(data.bytes),
|
None => Readable::Bytes(loaded.data),
|
||||||
Some(Encoding::Utf8) => Readable::Str(data.load_str()?.into()),
|
Some(Encoding::Utf8) => Readable::Str(loaded.load_str()?.into()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use ecow::eco_format;
|
use ecow::eco_format;
|
||||||
use typst_syntax::Spanned;
|
use typst_syntax::Spanned;
|
||||||
|
|
||||||
use crate::diag::{At, LoadError, LoadedAt, ReportPos, SourceResult};
|
use crate::diag::{At, LoadError, LoadedWithin, ReportPos, SourceResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{func, scope, Str, Value};
|
use crate::foundations::{func, scope, Str, Value};
|
||||||
use crate::loading::{DataSource, Load, Readable};
|
use crate::loading::{DataSource, Load, Readable};
|
||||||
@ -32,9 +32,9 @@ pub fn toml(
|
|||||||
/// A [path]($syntax/#paths) to a TOML file or raw TOML bytes.
|
/// A [path]($syntax/#paths) to a TOML file or raw TOML bytes.
|
||||||
source: Spanned<DataSource>,
|
source: Spanned<DataSource>,
|
||||||
) -> SourceResult<Value> {
|
) -> SourceResult<Value> {
|
||||||
let data = source.load(engine.world)?;
|
let loaded = source.load(engine.world)?;
|
||||||
let raw = data.load_str()?;
|
let raw = loaded.load_str()?;
|
||||||
::toml::from_str(raw).map_err(format_toml_error).in_text(&data)
|
::toml::from_str(raw).map_err(format_toml_error).within(&loaded)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[scope]
|
#[scope]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use roxmltree::ParsingOptions;
|
use roxmltree::ParsingOptions;
|
||||||
use typst_syntax::Spanned;
|
use typst_syntax::Spanned;
|
||||||
|
|
||||||
use crate::diag::{format_xml_like_error, LoadError, LoadedAt, SourceResult};
|
use crate::diag::{format_xml_like_error, LoadError, LoadedWithin, SourceResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{dict, func, scope, Array, Dict, IntoValue, Str, Value};
|
use crate::foundations::{dict, func, scope, Array, Dict, IntoValue, Str, Value};
|
||||||
use crate::loading::{DataSource, Load, Readable};
|
use crate::loading::{DataSource, Load, Readable};
|
||||||
@ -60,14 +60,14 @@ pub fn xml(
|
|||||||
/// A [path]($syntax/#paths) to an XML file or raw XML bytes.
|
/// A [path]($syntax/#paths) to an XML file or raw XML bytes.
|
||||||
source: Spanned<DataSource>,
|
source: Spanned<DataSource>,
|
||||||
) -> SourceResult<Value> {
|
) -> SourceResult<Value> {
|
||||||
let data = source.load(engine.world)?;
|
let loaded = source.load(engine.world)?;
|
||||||
let text = data.load_str()?;
|
let text = loaded.load_str()?;
|
||||||
let document = roxmltree::Document::parse_with_options(
|
let document = roxmltree::Document::parse_with_options(
|
||||||
text,
|
text,
|
||||||
ParsingOptions { allow_dtd: true, ..Default::default() },
|
ParsingOptions { allow_dtd: true, ..Default::default() },
|
||||||
)
|
)
|
||||||
.map_err(format_xml_error)
|
.map_err(format_xml_error)
|
||||||
.in_text(&data)?;
|
.within(&loaded)?;
|
||||||
Ok(convert_xml(document.root()))
|
Ok(convert_xml(document.root()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use ecow::{eco_format, EcoVec};
|
use ecow::eco_format;
|
||||||
use typst_syntax::Spanned;
|
use typst_syntax::Spanned;
|
||||||
|
|
||||||
use crate::diag::{At, LineCol, ReportPos, SourceDiagnostic, SourceResult};
|
use crate::diag::{At, LineCol, LoadError, LoadedWithin, ReportPos, SourceResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{func, scope, Str, Value};
|
use crate::foundations::{func, scope, Str, Value};
|
||||||
use crate::loading::{DataSource, Load, Loaded, Readable};
|
use crate::loading::{DataSource, Load, Readable};
|
||||||
|
|
||||||
/// Reads structured data from a YAML file.
|
/// Reads structured data from a YAML file.
|
||||||
///
|
///
|
||||||
@ -44,9 +44,10 @@ pub fn yaml(
|
|||||||
/// A [path]($syntax/#paths) to a YAML file or raw YAML bytes.
|
/// A [path]($syntax/#paths) to a YAML file or raw YAML bytes.
|
||||||
source: Spanned<DataSource>,
|
source: Spanned<DataSource>,
|
||||||
) -> SourceResult<Value> {
|
) -> SourceResult<Value> {
|
||||||
let data = source.load(engine.world)?;
|
let loaded = source.load(engine.world)?;
|
||||||
serde_yaml::from_slice(data.bytes.as_slice())
|
serde_yaml::from_slice(loaded.data.as_slice())
|
||||||
.map_err(|err| format_yaml_error(&data, err))
|
.map_err(format_yaml_error)
|
||||||
|
.within(&loaded)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[scope]
|
#[scope]
|
||||||
@ -76,10 +77,7 @@ impl yaml {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_yaml_error(
|
pub fn format_yaml_error(error: serde_yaml::Error) -> LoadError {
|
||||||
data: &Loaded,
|
|
||||||
error: serde_yaml::Error,
|
|
||||||
) -> EcoVec<SourceDiagnostic> {
|
|
||||||
let pos = error
|
let pos = error
|
||||||
.location()
|
.location()
|
||||||
.map(|loc| {
|
.map(|loc| {
|
||||||
@ -88,5 +86,5 @@ pub fn format_yaml_error(
|
|||||||
ReportPos::full(range, line_col)
|
ReportPos::full(range, line_col)
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
data.err_in_text(pos, "failed to parse YAML", error)
|
LoadError::new(pos, "failed to parse YAML", error)
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,8 @@ use typst_syntax::{Span, Spanned};
|
|||||||
use typst_utils::{Get, ManuallyHash, NonZeroExt, PicoStr};
|
use typst_utils::{Get, ManuallyHash, NonZeroExt, PicoStr};
|
||||||
|
|
||||||
use crate::diag::{
|
use crate::diag::{
|
||||||
bail, error, At, HintedStrResult, LoadError, LoadResult, LoadedAt, ReportPos,
|
bail, error, At, HintedStrResult, LoadError, LoadResult, LoadedWithin, ReportPos,
|
||||||
SourceDiagnostic, SourceResult, StrResult,
|
SourceResult, StrResult,
|
||||||
};
|
};
|
||||||
use crate::engine::{Engine, Sink};
|
use crate::engine::{Engine, Sink};
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
@ -34,7 +34,7 @@ use crate::layout::{
|
|||||||
BlockBody, BlockElem, Em, GridCell, GridChild, GridElem, GridItem, HElem, PadElem,
|
BlockBody, BlockElem, Em, GridCell, GridChild, GridElem, GridItem, HElem, PadElem,
|
||||||
Sides, Sizing, TrackSizings,
|
Sides, Sizing, TrackSizings,
|
||||||
};
|
};
|
||||||
use crate::loading::{format_yaml_error, DataSource, Load, LoadSource, LoadStr, Loaded};
|
use crate::loading::{format_yaml_error, DataSource, Load, LoadSource, Loaded};
|
||||||
use crate::model::{
|
use crate::model::{
|
||||||
CitationForm, CiteGroup, Destination, FootnoteElem, HeadingElem, LinkElem, ParElem,
|
CitationForm, CiteGroup, Destination, FootnoteElem, HeadingElem, LinkElem, ParElem,
|
||||||
Url,
|
Url,
|
||||||
@ -297,8 +297,8 @@ impl Bibliography {
|
|||||||
world: Tracked<dyn World + '_>,
|
world: Tracked<dyn World + '_>,
|
||||||
sources: Spanned<OneOrMultiple<DataSource>>,
|
sources: Spanned<OneOrMultiple<DataSource>>,
|
||||||
) -> SourceResult<Derived<OneOrMultiple<DataSource>, Self>> {
|
) -> SourceResult<Derived<OneOrMultiple<DataSource>, Self>> {
|
||||||
let data = sources.load(world)?;
|
let loaded = sources.load(world)?;
|
||||||
let bibliography = Self::decode(&data)?;
|
let bibliography = Self::decode(&loaded)?;
|
||||||
Ok(Derived::new(sources.v, bibliography))
|
Ok(Derived::new(sources.v, bibliography))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,10 +355,10 @@ impl Debug for Bibliography {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Decode on library from one data source.
|
/// Decode on library from one data source.
|
||||||
fn decode_library(data: &Loaded) -> SourceResult<Library> {
|
fn decode_library(loaded: &Loaded) -> SourceResult<Library> {
|
||||||
let str = data.load_str()?;
|
let data = loaded.load_str()?;
|
||||||
|
|
||||||
if let LoadSource::Path(file_id) = data.source.v {
|
if let LoadSource::Path(file_id) = loaded.source.v {
|
||||||
// If we got a path, use the extension to determine whether it is
|
// If we got a path, use the extension to determine whether it is
|
||||||
// YAML or BibLaTeX.
|
// YAML or BibLaTeX.
|
||||||
let ext = file_id
|
let ext = file_id
|
||||||
@ -369,25 +369,27 @@ fn decode_library(data: &Loaded) -> SourceResult<Library> {
|
|||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
match ext.to_lowercase().as_str() {
|
match ext.to_lowercase().as_str() {
|
||||||
"yml" | "yaml" => hayagriva::io::from_yaml_str(str)
|
"yml" | "yaml" => hayagriva::io::from_yaml_str(data)
|
||||||
.map_err(|err| format_yaml_error(data, err)),
|
.map_err(format_yaml_error)
|
||||||
"bib" => hayagriva::io::from_biblatex_str(str)
|
.within(loaded),
|
||||||
.map_err(|errors| format_biblatex_error(data, errors)),
|
"bib" => hayagriva::io::from_biblatex_str(data)
|
||||||
|
.map_err(format_biblatex_error)
|
||||||
|
.within(loaded),
|
||||||
_ => bail!(
|
_ => bail!(
|
||||||
data.source.span,
|
loaded.source.span,
|
||||||
"unknown bibliography format (must be .yml/.yaml or .bib)"
|
"unknown bibliography format (must be .yml/.yaml or .bib)"
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If we just got bytes, we need to guess. If it can be decoded as
|
// If we just got bytes, we need to guess. If it can be decoded as
|
||||||
// hayagriva YAML, we'll use that.
|
// hayagriva YAML, we'll use that.
|
||||||
let haya_err = match hayagriva::io::from_yaml_str(str) {
|
let haya_err = match hayagriva::io::from_yaml_str(data) {
|
||||||
Ok(library) => return Ok(library),
|
Ok(library) => return Ok(library),
|
||||||
Err(err) => err,
|
Err(err) => err,
|
||||||
};
|
};
|
||||||
|
|
||||||
// If it can be decoded as BibLaTeX, we use that isntead.
|
// If it can be decoded as BibLaTeX, we use that isntead.
|
||||||
let bib_errs = match hayagriva::io::from_biblatex_str(str) {
|
let bib_errs = match hayagriva::io::from_biblatex_str(data) {
|
||||||
// If the file is almost valid yaml, but contains no `@` character
|
// If the file is almost valid yaml, but contains no `@` character
|
||||||
// it will be successfully parsed as an empty BibLaTeX library,
|
// it will be successfully parsed as an empty BibLaTeX library,
|
||||||
// since BibLaTeX does support arbitrary text outside of entries.
|
// since BibLaTeX does support arbitrary text outside of entries.
|
||||||
@ -401,7 +403,7 @@ fn decode_library(data: &Loaded) -> SourceResult<Library> {
|
|||||||
// and emit the more appropriate error.
|
// and emit the more appropriate error.
|
||||||
let mut yaml = 0;
|
let mut yaml = 0;
|
||||||
let mut biblatex = 0;
|
let mut biblatex = 0;
|
||||||
for c in str.chars() {
|
for c in data.chars() {
|
||||||
match c {
|
match c {
|
||||||
':' => yaml += 1,
|
':' => yaml += 1,
|
||||||
'{' => biblatex += 1,
|
'{' => biblatex += 1,
|
||||||
@ -411,22 +413,19 @@ fn decode_library(data: &Loaded) -> SourceResult<Library> {
|
|||||||
|
|
||||||
match bib_errs {
|
match bib_errs {
|
||||||
Some(bib_errs) if biblatex >= yaml => {
|
Some(bib_errs) if biblatex >= yaml => {
|
||||||
Err(format_biblatex_error(data, bib_errs))
|
Err(format_biblatex_error(bib_errs)).within(&loaded)
|
||||||
}
|
}
|
||||||
_ => Err(format_yaml_error(data, haya_err)),
|
_ => Err(format_yaml_error(haya_err)).within(&loaded),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Format a BibLaTeX loading error.
|
/// Format a BibLaTeX loading error.
|
||||||
fn format_biblatex_error(
|
fn format_biblatex_error(errors: Vec<BibLaTeXError>) -> LoadError {
|
||||||
data: &Loaded,
|
|
||||||
errors: Vec<BibLaTeXError>,
|
|
||||||
) -> EcoVec<SourceDiagnostic> {
|
|
||||||
// TODO: return multiple errors?
|
// TODO: return multiple errors?
|
||||||
let Some(error) = errors.into_iter().next() else {
|
let Some(error) = errors.into_iter().next() else {
|
||||||
// TODO: can this even happen, should we just unwrap?
|
// TODO: can this even happen, should we just unwrap?
|
||||||
return data.err_in_text(ReportPos::None, "failed to parse BibLaTeX", "???");
|
return LoadError::new(ReportPos::None, "failed to parse BibLaTeX", "???");
|
||||||
};
|
};
|
||||||
|
|
||||||
let (range, msg) = match error {
|
let (range, msg) = match error {
|
||||||
@ -434,7 +433,7 @@ fn format_biblatex_error(
|
|||||||
BibLaTeXError::Type(error) => (error.span, error.kind.to_string()),
|
BibLaTeXError::Type(error) => (error.span, error.kind.to_string()),
|
||||||
};
|
};
|
||||||
|
|
||||||
data.err_in_text(range, "failed to parse BibLaTeX", msg)
|
LoadError::new(range, "failed to parse BibLaTeX", msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A loaded CSL style.
|
/// A loaded CSL style.
|
||||||
@ -450,8 +449,8 @@ impl CslStyle {
|
|||||||
let style = match &source {
|
let style = match &source {
|
||||||
CslSource::Named(style) => Self::from_archived(*style),
|
CslSource::Named(style) => Self::from_archived(*style),
|
||||||
CslSource::Normal(source) => {
|
CslSource::Normal(source) => {
|
||||||
let data = Spanned::new(source, span).load(world)?;
|
let loaded = Spanned::new(source, span).load(world)?;
|
||||||
Self::from_data(&data.bytes).in_text(&data)?
|
Self::from_data(&loaded.data).within(&loaded)?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(Derived::new(source, style))
|
Ok(Derived::new(source, style))
|
||||||
|
@ -11,7 +11,7 @@ use typst_utils::ManuallyHash;
|
|||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
use super::Lang;
|
use super::Lang;
|
||||||
use crate::diag::{LineCol, LoadError, LoadResult, LoadedAt, ReportPos, SourceResult};
|
use crate::diag::{LineCol, LoadError, LoadResult, LoadedWithin, ReportPos, SourceResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
cast, elem, scope, Bytes, Content, Derived, NativeElement, OneOrMultiple, Packed,
|
cast, elem, scope, Bytes, Content, Derived, NativeElement, OneOrMultiple, Packed,
|
||||||
@ -19,7 +19,7 @@ use crate::foundations::{
|
|||||||
};
|
};
|
||||||
use crate::html::{tag, HtmlElem};
|
use crate::html::{tag, HtmlElem};
|
||||||
use crate::layout::{BlockBody, BlockElem, Em, HAlignment};
|
use crate::layout::{BlockBody, BlockElem, Em, HAlignment};
|
||||||
use crate::loading::{DataSource, Load, LoadStr};
|
use crate::loading::{DataSource, Load};
|
||||||
use crate::model::{Figurable, ParElem};
|
use crate::model::{Figurable, ParElem};
|
||||||
use crate::text::{FontFamily, FontList, LinebreakElem, LocalName, TextElem, TextSize};
|
use crate::text::{FontFamily, FontList, LinebreakElem, LocalName, TextElem, TextSize};
|
||||||
use crate::visualize::Color;
|
use crate::visualize::Color;
|
||||||
@ -539,10 +539,10 @@ impl RawSyntax {
|
|||||||
world: Tracked<dyn World + '_>,
|
world: Tracked<dyn World + '_>,
|
||||||
sources: Spanned<OneOrMultiple<DataSource>>,
|
sources: Spanned<OneOrMultiple<DataSource>>,
|
||||||
) -> SourceResult<Derived<OneOrMultiple<DataSource>, Vec<RawSyntax>>> {
|
) -> SourceResult<Derived<OneOrMultiple<DataSource>, Vec<RawSyntax>>> {
|
||||||
let data = sources.load(world)?;
|
let loaded = sources.load(world)?;
|
||||||
let list = data
|
let list = loaded
|
||||||
.iter()
|
.iter()
|
||||||
.map(|data| Self::decode(&data.bytes).in_text(data))
|
.map(|data| Self::decode(&data.data).within(data))
|
||||||
.collect::<SourceResult<_>>()?;
|
.collect::<SourceResult<_>>()?;
|
||||||
Ok(Derived::new(sources.v, list))
|
Ok(Derived::new(sources.v, list))
|
||||||
}
|
}
|
||||||
@ -599,8 +599,8 @@ impl RawTheme {
|
|||||||
world: Tracked<dyn World + '_>,
|
world: Tracked<dyn World + '_>,
|
||||||
source: Spanned<DataSource>,
|
source: Spanned<DataSource>,
|
||||||
) -> SourceResult<Derived<DataSource, Self>> {
|
) -> SourceResult<Derived<DataSource, Self>> {
|
||||||
let data = source.load(world)?;
|
let loaded = source.load(world)?;
|
||||||
let theme = Self::decode(&data.bytes).in_text(&data)?;
|
let theme = Self::decode(&loaded.data).within(&loaded)?;
|
||||||
Ok(Derived::new(source.v, theme))
|
Ok(Derived::new(source.v, theme))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,8 +65,8 @@ pub struct ImageElem {
|
|||||||
#[required]
|
#[required]
|
||||||
#[parse(
|
#[parse(
|
||||||
let source = args.expect::<Spanned<DataSource>>("source")?;
|
let source = args.expect::<Spanned<DataSource>>("source")?;
|
||||||
let data = source.load(engine.world)?;
|
let loaded = source.load(engine.world)?;
|
||||||
Derived::new(source.v, data)
|
Derived::new(source.v, loaded)
|
||||||
)]
|
)]
|
||||||
pub source: Derived<DataSource, Loaded>,
|
pub source: Derived<DataSource, Loaded>,
|
||||||
|
|
||||||
@ -154,8 +154,8 @@ pub struct ImageElem {
|
|||||||
/// to `{auto}`, Typst will try to extract an ICC profile from the image.
|
/// to `{auto}`, Typst will try to extract an ICC profile from the image.
|
||||||
#[parse(match args.named::<Spanned<Smart<DataSource>>>("icc")? {
|
#[parse(match args.named::<Spanned<Smart<DataSource>>>("icc")? {
|
||||||
Some(Spanned { v: Smart::Custom(source), span }) => Some(Smart::Custom({
|
Some(Spanned { v: Smart::Custom(source), span }) => Some(Smart::Custom({
|
||||||
let data = Spanned::new(&source, span).load(engine.world)?;
|
let loaded = Spanned::new(&source, span).load(engine.world)?;
|
||||||
Derived::new(source, data.bytes)
|
Derived::new(source, loaded.data)
|
||||||
})),
|
})),
|
||||||
Some(Spanned { v: Smart::Auto, .. }) => Some(Smart::Auto),
|
Some(Spanned { v: Smart::Auto, .. }) => Some(Smart::Auto),
|
||||||
None => None,
|
None => None,
|
||||||
@ -194,8 +194,8 @@ impl ImageElem {
|
|||||||
scaling: Option<Smart<ImageScaling>>,
|
scaling: Option<Smart<ImageScaling>>,
|
||||||
) -> StrResult<Content> {
|
) -> StrResult<Content> {
|
||||||
let bytes = data.v.into_bytes();
|
let bytes = data.v.into_bytes();
|
||||||
let data = Loaded::new(Spanned::new(LoadSource::Bytes, data.span), bytes.clone());
|
let loaded = Loaded::new(Spanned::new(LoadSource::Bytes, data.span), bytes.clone());
|
||||||
let source = Derived::new(DataSource::Bytes(bytes), data);
|
let source = Derived::new(DataSource::Bytes(bytes), loaded);
|
||||||
let mut elem = ImageElem::new(source);
|
let mut elem = ImageElem::new(source);
|
||||||
if let Some(format) = format {
|
if let Some(format) = format {
|
||||||
elem.push_format(format);
|
elem.push_format(format);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user