Compare commits

..

3 Commits

Author SHA1 Message Date
Ana Gelez
c247dbc42d Lazy parsing of the package index (#5851) 2025-02-12 17:12:01 +01:00
+merlan #flirora
c259545c6e Gradient::repeat: Fix floating-point error in stop calculation (#5837) 2025-02-12 13:53:05 +01:00
+merlan #flirora
e470ccff19 Update documentation for float.{to-bits, from-bits} (#5836) 2025-02-12 13:53:05 +01:00
6 changed files with 96 additions and 13 deletions

2
Cargo.lock generated
View File

@ -2901,6 +2901,8 @@ dependencies = [
"native-tls",
"once_cell",
"openssl",
"serde",
"serde_json",
"tar",
"typst-assets",
"typst-library",

View File

@ -23,6 +23,8 @@ flate2 = { workspace = true, optional = true }
fontdb = { workspace = true, optional = true }
native-tls = { workspace = true, optional = true }
once_cell = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tar = { workspace = true, optional = true }
ureq = { workspace = true, optional = true }

View File

@ -5,10 +5,9 @@ use std::path::{Path, PathBuf};
use ecow::eco_format;
use once_cell::sync::OnceCell;
use serde::Deserialize;
use typst_library::diag::{bail, PackageError, PackageResult, StrResult};
use typst_syntax::package::{
PackageInfo, PackageSpec, PackageVersion, VersionlessPackageSpec,
};
use typst_syntax::package::{PackageSpec, PackageVersion, VersionlessPackageSpec};
use crate::download::{Downloader, Progress};
@ -32,7 +31,7 @@ pub struct PackageStorage {
/// The downloader used for fetching the index and packages.
downloader: Downloader,
/// The cached index of the default namespace.
index: OnceCell<Vec<PackageInfo>>,
index: OnceCell<Vec<serde_json::Value>>,
}
impl PackageStorage {
@ -42,6 +41,18 @@ impl PackageStorage {
package_cache_path: Option<PathBuf>,
package_path: Option<PathBuf>,
downloader: Downloader,
) -> Self {
Self::with_index(package_cache_path, package_path, downloader, OnceCell::new())
}
/// Creates a new package storage with a pre-defined index.
///
/// Useful for testing.
fn with_index(
package_cache_path: Option<PathBuf>,
package_path: Option<PathBuf>,
downloader: Downloader,
index: OnceCell<Vec<serde_json::Value>>,
) -> Self {
Self {
package_cache_path: package_cache_path.or_else(|| {
@ -51,7 +62,7 @@ impl PackageStorage {
dirs::data_dir().map(|data_dir| data_dir.join(DEFAULT_PACKAGES_SUBDIR))
}),
downloader,
index: OnceCell::new(),
index,
}
}
@ -109,6 +120,7 @@ impl PackageStorage {
// version.
self.download_index()?
.iter()
.filter_map(|value| MinimalPackageInfo::deserialize(value).ok())
.filter(|package| package.name == spec.name)
.map(|package| package.version)
.max()
@ -131,7 +143,7 @@ impl PackageStorage {
}
/// Download the package index. The result of this is cached for efficiency.
pub fn download_index(&self) -> StrResult<&[PackageInfo]> {
pub fn download_index(&self) -> StrResult<&[serde_json::Value]> {
self.index
.get_or_try_init(|| {
let url = format!("{DEFAULT_REGISTRY}/{DEFAULT_NAMESPACE}/index.json");
@ -186,3 +198,54 @@ impl PackageStorage {
})
}
}
/// Minimal information required about a package to determine its latest
/// version.
#[derive(Deserialize)]
struct MinimalPackageInfo {
name: String,
version: PackageVersion,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lazy_deser_index() {
let storage = PackageStorage::with_index(
None,
None,
Downloader::new("typst/test"),
OnceCell::with_value(vec![
serde_json::json!({
"name": "charged-ieee",
"version": "0.1.0",
"entrypoint": "lib.typ",
}),
serde_json::json!({
"name": "unequivocal-ams",
// This version number is currently not valid, so this package
// can't be parsed.
"version": "0.2.0-dev",
"entrypoint": "lib.typ",
}),
]),
);
let ieee_version = storage.determine_latest_version(&VersionlessPackageSpec {
namespace: "preview".into(),
name: "charged-ieee".into(),
});
assert_eq!(ieee_version, Ok(PackageVersion { major: 0, minor: 1, patch: 0 }));
let ams_version = storage.determine_latest_version(&VersionlessPackageSpec {
namespace: "preview".into(),
name: "unequivocal-ams".into(),
});
assert_eq!(
ams_version,
Err("failed to find package @preview/unequivocal-ams".into())
)
}
}

View File

@ -110,7 +110,7 @@ impl f64 {
f64::signum(self)
}
/// Converts bytes to a float.
/// Interprets bytes as a float.
///
/// ```example
/// #float.from-bytes(bytes((0, 0, 0, 0, 0, 0, 240, 63))) \
@ -120,8 +120,10 @@ impl f64 {
pub fn from_bytes(
/// The bytes that should be converted to a float.
///
/// Must be of length exactly 8 so that the result fits into a 64-bit
/// float.
/// Must have a length of either 4 or 8. The bytes are then
/// interpreted in [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754)'s
/// binary32 (single-precision) or binary64 (double-precision) format
/// depending on the length of the bytes.
bytes: Bytes,
/// The endianness of the conversion.
#[named]
@ -158,6 +160,13 @@ impl f64 {
#[named]
#[default(Endianness::Little)]
endian: Endianness,
/// The size of the resulting bytes.
///
/// This must be either 4 or 8. The call will return the
/// representation of this float in either
/// [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754)'s binary32
/// (single-precision) or binary64 (double-precision) format
/// depending on the provided size.
#[named]
#[default(8)]
size: u32,

View File

@ -582,12 +582,11 @@ impl Gradient {
let mut stops = stops
.iter()
.map(move |&(color, offset)| {
let t = i as f64 / n as f64;
let r = offset.get();
if i % 2 == 1 && mirror {
(color, Ratio::new(t + (1.0 - r) / n as f64))
(color, Ratio::new((i as f64 + 1.0 - r) / n as f64))
} else {
(color, Ratio::new(t + r / n as f64))
(color, Ratio::new((i as f64 + r) / n as f64))
}
})
.collect::<Vec<_>>();
@ -1230,7 +1229,7 @@ fn process_stops(stops: &[Spanned<GradientStop>]) -> SourceResult<Vec<(Color, Ra
};
if stop.get() < last_stop {
bail!(*span, "offsets must be in strictly monotonic order");
bail!(*span, "offsets must be in monotonic order");
}
last_stop = stop.get();

View File

@ -658,3 +658,11 @@ $ A = mat(
height: 10pt,
fill: gradient.linear(violet, blue, space: cmyk)
)
--- issue-5819-gradient-repeat ---
// Ensure the gradient constructor generates monotonic stops which can be fed
// back into the gradient constructor itself.
#let my-gradient = gradient.linear(red, blue).repeat(5)
#let _ = gradient.linear(..my-gradient.stops())
#let my-gradient2 = gradient.linear(red, blue).repeat(5, mirror: true)
#let _ = gradient.linear(..my-gradient2.stops())