diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-08-04 15:09:01 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-08-04 15:46:46 +0200 |
| commit | b8b0137504d388efbe2d1ba5082c0dcabcd8bc8a (patch) | |
| tree | 825abab07230c702bca8efee026d9b98ecfa2aae /crates/typst-library | |
| parent | 028d2f53085022c39945b1a99c9dc78eb8069d4a (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')
| -rw-r--r-- | crates/typst-library/src/compute/construct.rs | 141 | ||||
| -rw-r--r-- | crates/typst-library/src/compute/data.rs | 62 | ||||
| -rw-r--r-- | crates/typst-library/src/compute/mod.rs | 2 | ||||
| -rw-r--r-- | crates/typst-library/src/meta/bibliography.rs | 3 | ||||
| -rw-r--r-- | crates/typst-library/src/text/raw.rs | 2 | ||||
| -rw-r--r-- | crates/typst-library/src/visualize/image.rs | 2 |
6 files changed, 167 insertions, 45 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(®ex.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()); diff --git a/crates/typst-library/src/meta/bibliography.rs b/crates/typst-library/src/meta/bibliography.rs index a2e7099d..2b00ff44 100644 --- a/crates/typst-library/src/meta/bibliography.rs +++ b/crates/typst-library/src/meta/bibliography.rs @@ -8,7 +8,8 @@ use hayagriva::io::{BibLaTeXError, YamlBibliographyError}; use hayagriva::style::{self, Brackets, Citation, Database, DisplayString, Formatting}; use hayagriva::Entry; use typst::diag::FileError; -use typst::util::{option_eq, Bytes}; +use typst::eval::Bytes; +use typst::util::option_eq; use super::{LinkElem, LocalName, RefElem}; use crate::layout::{BlockElem, GridElem, ParElem, Sizing, TrackSizings, VElem}; diff --git a/crates/typst-library/src/text/raw.rs b/crates/typst-library/src/text/raw.rs index 6713f5d0..3d3fff40 100644 --- a/crates/typst-library/src/text/raw.rs +++ b/crates/typst-library/src/text/raw.rs @@ -6,8 +6,8 @@ use once_cell::unsync::Lazy as UnsyncLazy; use syntect::highlighting as synt; use syntect::parsing::{SyntaxDefinition, SyntaxSet, SyntaxSetBuilder}; use typst::diag::FileError; +use typst::eval::Bytes; use typst::syntax::{self, LinkedNode}; -use typst::util::Bytes; use super::{ FontFamily, FontList, Hyphenate, LinebreakElem, SmartQuoteElem, TextElem, TextSize, diff --git a/crates/typst-library/src/visualize/image.rs b/crates/typst-library/src/visualize/image.rs index e572f2ad..514861e5 100644 --- a/crates/typst-library/src/visualize/image.rs +++ b/crates/typst-library/src/visualize/image.rs @@ -1,8 +1,8 @@ use std::ffi::OsStr; use std::path::Path; +use typst::eval::Bytes; use typst::image::{Image, ImageFormat, RasterFormat, VectorFormat}; -use typst::util::Bytes; use crate::meta::{Figurable, LocalName}; use crate::prelude::*; |
