summaryrefslogtreecommitdiff
path: root/crates/typst-library/src/compute
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-08-04 15:09:01 +0200
committerLaurenz <laurmaedje@gmail.com>2023-08-04 15:46:46 +0200
commitb8b0137504d388efbe2d1ba5082c0dcabcd8bc8a (patch)
tree825abab07230c702bca8efee026d9b98ecfa2aae /crates/typst-library/src/compute
parent028d2f53085022c39945b1a99c9dc78eb8069d4a (diff)
Bytes type
- Moves `Bytes` from `util` to `eval` module - Accepts bytes in `str` function for bytes -> str conversion - Adds `bytes` function for str | array -> bytes conversion - Adds `array` function for bytes -> array conversion - Adds `len`, `at`, and `slice` methods for bytes - Adds `encoding` parameter to `read` function
Diffstat (limited to 'crates/typst-library/src/compute')
-rw-r--r--crates/typst-library/src/compute/construct.rs141
-rw-r--r--crates/typst-library/src/compute/data.rs62
-rw-r--r--crates/typst-library/src/compute/mod.rs2
3 files changed, 163 insertions, 42 deletions
diff --git a/crates/typst-library/src/compute/construct.rs b/crates/typst-library/src/compute/construct.rs
index 1ce676bb..4329f259 100644
--- a/crates/typst-library/src/compute/construct.rs
+++ b/crates/typst-library/src/compute/construct.rs
@@ -3,7 +3,7 @@ use std::str::FromStr;
use time::{Month, PrimitiveDateTime};
-use typst::eval::{Datetime, Module, Regex};
+use typst::eval::{Bytes, Datetime, Module, Reflect, Regex};
use crate::prelude::*;
@@ -37,9 +37,9 @@ pub struct ToInt(i64);
cast! {
ToInt,
v: bool => Self(v as i64),
- v: i64 => Self(v),
v: f64 => Self(v as i64),
v: EcoString => Self(v.parse().map_err(|_| eco_format!("invalid integer: {}", v))?),
+ v: i64 => Self(v),
}
/// Converts a value to a float.
@@ -77,9 +77,9 @@ cast! {
ToFloat,
v: bool => Self(v as i64 as f64),
v: i64 => Self(v as f64),
- v: f64 => Self(v),
v: Ratio => Self(v.get()),
v: EcoString => Self(v.parse().map_err(|_| eco_format!("invalid float: {}", v))?),
+ v: f64 => Self(v),
}
/// Creates a grayscale color.
@@ -486,6 +486,7 @@ cast! {
/// optional `base` parameter.
/// - Floats are formatted in base 10 and never in exponential notation.
/// - From labels the name is extracted.
+/// - Bytes are decoded as UTF-8.
///
/// If you wish to convert from and to Unicode code points, see
/// [`str.to-unicode`]($func/str.to-unicode) and
@@ -545,6 +546,11 @@ cast! {
v: i64 => Self::Int(v),
v: f64 => Self::Str(format_str!("{}", v)),
v: Label => Self::Str(v.0.into()),
+ v: Bytes => Self::Str(
+ std::str::from_utf8(&v)
+ .map_err(|_| "bytes are not valid utf-8")?
+ .into()
+ ),
v: Str => Self::Str(v),
}
@@ -633,35 +639,6 @@ cast! {
},
}
-/// Creates a label from a string.
-///
-/// Inserting a label into content attaches it to the closest previous element
-/// that is not a space. Then, the element can be [referenced]($func/ref) and
-/// styled through the label.
-///
-/// ## Example { #example }
-/// ```example
-/// #show <a>: set text(blue)
-/// #show label("b"): set text(red)
-///
-/// = Heading <a>
-/// *Strong* #label("b")
-/// ```
-///
-/// ## Syntax { #syntax }
-/// This function also has dedicated syntax: You can create a label by enclosing
-/// its name in angle brackets. This works both in markup and code.
-///
-/// Display: Label
-/// Category: construct
-#[func]
-pub fn label(
- /// The name of the label.
- name: EcoString,
-) -> Label {
- Label(name)
-}
-
/// Creates a regular expression from a string.
///
/// The result can be used as a
@@ -701,6 +678,106 @@ pub fn regex(
Regex::new(&regex.v).at(regex.span)
}
+/// Converts a value to bytes.
+///
+/// - Strings are encoded in UTF-8.
+/// - Arrays of integers between `{0}` and `{255}` are converted directly. The
+/// dedicated byte representation is much more efficient than the array
+/// representation and thus typically used for large byte buffers (e.g. image
+/// data).
+///
+/// ```example
+/// #bytes("Hello 😃") \
+/// #bytes((123, 160, 22, 0))
+/// ```
+///
+/// Display: Bytes
+/// Category: construct
+#[func]
+pub fn bytes(
+ /// The value that should be converted to a string.
+ value: ToBytes,
+) -> Bytes {
+ value.0
+}
+
+/// A value that can be cast to bytes.
+pub struct ToBytes(Bytes);
+
+cast! {
+ ToBytes,
+ v: Str => Self(v.as_bytes().into()),
+ v: Array => Self(v.iter()
+ .map(|v| match v {
+ Value::Int(byte @ 0..=255) => Ok(*byte as u8),
+ Value::Int(_) => bail!("number must be between 0 and 255"),
+ value => Err(<u8 as Reflect>::error(value)),
+ })
+ .collect::<Result<Vec<u8>, _>>()?
+ .into()
+ ),
+ v: Bytes => Self(v),
+}
+
+/// Creates a label from a string.
+///
+/// Inserting a label into content attaches it to the closest previous element
+/// that is not a space. Then, the element can be [referenced]($func/ref) and
+/// styled through the label.
+///
+/// ## Example { #example }
+/// ```example
+/// #show <a>: set text(blue)
+/// #show label("b"): set text(red)
+///
+/// = Heading <a>
+/// *Strong* #label("b")
+/// ```
+///
+/// ## Syntax { #syntax }
+/// This function also has dedicated syntax: You can create a label by enclosing
+/// its name in angle brackets. This works both in markup and code.
+///
+/// Display: Label
+/// Category: construct
+#[func]
+pub fn label(
+ /// The name of the label.
+ name: EcoString,
+) -> Label {
+ Label(name)
+}
+
+/// Converts a value to an array.
+///
+/// Note that this function is only intended for conversion of a collection-like
+/// value to an array, not for creation of an array from individual items. Use
+/// the array syntax `(1, 2, 3)` (or `(1,)` for a single-element array) instead.
+///
+/// ```example
+/// #let hi = "Hello 😃"
+/// #array(bytes(hi))
+/// ```
+///
+/// Display: Array
+/// Category: construct
+#[func]
+pub fn array(
+ /// The value that should be converted to an array.
+ value: ToArray,
+) -> Array {
+ value.0
+}
+
+/// A value that can be cast to bytes.
+pub struct ToArray(Array);
+
+cast! {
+ ToArray,
+ v: Bytes => Self(v.iter().map(|&b| Value::Int(b as i64)).collect()),
+ v: Array => Self(v),
+}
+
/// Creates an array consisting of consecutive integers.
///
/// If you pass just one positional parameter, it is interpreted as the `end` of
diff --git a/crates/typst-library/src/compute/data.rs b/crates/typst-library/src/compute/data.rs
index 6e3a298e..4a7c53cc 100644
--- a/crates/typst-library/src/compute/data.rs
+++ b/crates/typst-library/src/compute/data.rs
@@ -1,18 +1,24 @@
use typst::diag::{format_xml_like_error, FileError};
-use typst::eval::Datetime;
+use typst::eval::{Bytes, Datetime};
use crate::prelude::*;
-/// Reads plain text from a file.
+/// Reads plain text or data from a file.
///
-/// The file will be read and returned as a string.
+/// By default, the file will be read as UTF-8 and returned as a
+/// [string]($type/string).
+///
+/// If you specify `{encoding: none}`, this returns raw [bytes]($type/bytes)
+/// instead.
///
/// ## Example { #example }
/// ```example
+/// An example for a HTML file: \
/// #let text = read("data.html")
-///
-/// An example for a HTML file:\
/// #raw(text, lang: "html")
+///
+/// Raw bytes:
+/// #read("tiger.jpg", encoding: none)
/// ```
///
/// Display: Read
@@ -21,16 +27,52 @@ use crate::prelude::*;
pub fn read(
/// Path to a file.
path: Spanned<EcoString>,
+ /// The encoding to read the file with.
+ ///
+ /// If set to `{none}`, this function returns raw bytes.
+ #[named]
+ #[default(Some(Encoding::Utf8))]
+ encoding: Option<Encoding>,
/// The virtual machine.
vm: &mut Vm,
-) -> SourceResult<Str> {
+) -> SourceResult<Readable> {
let Spanned { v: path, span } = path;
let id = vm.location().join(&path).at(span)?;
let data = vm.world().file(id).at(span)?;
- let text = std::str::from_utf8(&data)
- .map_err(|_| "file is not valid utf-8")
- .at(span)?;
- Ok(text.into())
+ Ok(match encoding {
+ None => Readable::Bytes(data),
+ Some(Encoding::Utf8) => Readable::Str(
+ std::str::from_utf8(&data)
+ .map_err(|_| "file is not valid utf-8")
+ .at(span)?
+ .into(),
+ ),
+ })
+}
+
+/// An encoding of a file.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
+pub enum Encoding {
+ /// The Unicode UTF-8 encoding.
+ Utf8,
+}
+
+/// A value that can be read from a value.
+pub enum Readable {
+ /// A decoded string.
+ Str(Str),
+ /// Raw bytes.
+ Bytes(Bytes),
+}
+
+cast! {
+ Readable,
+ self => match self {
+ Self::Str(v) => v.into_value(),
+ Self::Bytes(v) => v.into_value(),
+ },
+ v: Str => Self::Str(v),
+ v: Bytes => Self::Bytes(v),
}
/// Reads structured data from a CSV file.
diff --git a/crates/typst-library/src/compute/mod.rs b/crates/typst-library/src/compute/mod.rs
index 15309f12..757377f0 100644
--- a/crates/typst-library/src/compute/mod.rs
+++ b/crates/typst-library/src/compute/mod.rs
@@ -27,8 +27,10 @@ pub(super) fn define(global: &mut Scope) {
global.define("datetime", datetime_func());
global.define("symbol", symbol_func());
global.define("str", str_func());
+ global.define("bytes", bytes_func());
global.define("label", label_func());
global.define("regex", regex_func());
+ global.define("array", array_func());
global.define("range", range_func());
global.define("read", read_func());
global.define("csv", csv_func());