typst_embedded_package/lib.rs
1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2
3//! Embed typst packages directly in the binary.
4//!
5//! # How to use
6//!
7//! 1. Download package archives from <https://typst.app/universe/search/?kind=packages>
8//! 1. Move those archives somewhere in the `src` directory
9//! 1. Include the archives with [`include_package!`]
10//! 1. Read the content of the archive with [`Package::read_archive`] (requires the `read-archive` feature)
11//!
12//! ```ignore
13//! # use typst_embedded_package::{include_package, Package};
14//! // Embed the package located at "/src/typst-packages/preview/cetz-0.3.1.tar.gz"
15//! const CETZ: Package = include_package!("typst-packages" "preview" "cetz" (0, 3, 1));
16//!
17//! // Embed multiple packages.
18//! const PACKAGES: [Package; 2] = include_package!(
19//! "typst-packages"
20//! [
21//! "preview" "cetz" (0, 3, 1),
22//! "preview" "oxifmt" (0, 2, 0),
23//! ]
24//! );
25//! ```
26
27#[cfg(feature = "read-archive")]
28mod archive;
29#[cfg(feature = "read-archive")]
30pub use archive::*;
31
32use typst::diag::EcoString;
33use typst::syntax::package::{PackageSpec, PackageVersion};
34
35/// Information about an embedded package.
36///
37/// Created by the [`include_package!`] macro.
38#[derive(Debug, Clone)]
39pub struct Package {
40 /// The namespace the package lives in.
41 pub namespace: &'static str,
42 /// The name of the package within its namespace.
43 pub name: &'static str,
44 /// The package's version.
45 pub version: PackageVersion,
46 /// The content of the package's tgz archive.
47 pub archive: &'static [u8],
48}
49
50impl Package {
51 /// Returns the [`PackageSpec`] of the package.
52 pub fn spec(&self) -> PackageSpec {
53 PackageSpec {
54 namespace: EcoString::from(self.namespace),
55 name: EcoString::from(self.name),
56 version: self.version,
57 }
58 }
59}
60
61#[doc(hidden)]
62pub mod __private {
63 pub use typst::syntax::package::PackageVersion;
64}
65
66/// Embed one or more typst package as tgz archive.
67///
68/// This macro returns an [`Package`] or an array of [`Package`].
69///
70/// # Usage
71///
72/// Embed package archive located at `src/{root}/{namespace}/{name}-{X}.{Y}.{Z}.tar.gz`.
73/// ```ignore
74/// include_package!( {root} {namespace} {name} ({X}, {Y}, {Z}) );
75/// include_package!(
76/// {root}
77/// [
78/// {namespace} {name} ({X}, {Y}, {Z}),
79/// {namespace} {name} ({X}, {Y}, {Z}),
80/// ...
81/// ]
82/// );
83/// ```
84#[macro_export]
85macro_rules! include_package {
86 (
87 $root:literal $namespace:literal $name:literal ($major:literal, $minor:literal, $patch:literal)
88 ) => {
89 $crate::Package {
90 namespace: $namespace,
91 name: $name,
92 version: $crate::__private::PackageVersion {
93 major: $major,
94 minor: $minor,
95 patch: $patch,
96 },
97 archive: include_bytes!(concat!(
98 $root,
99 "/",
100 $namespace,
101 "/",
102 $name,
103 "-",
104 stringify!($major),
105 ".",
106 stringify!($minor),
107 ".",
108 stringify!($patch),
109 ".tar.gz"
110 )),
111 }
112 };
113
114 (
115 $root:literal
116 [
117 $( $namespace:literal $name:literal ($major:literal, $minor:literal, $patch:literal) ),* $(,)?
118 ]
119 ) => {
120 [
121 $(
122 $crate::include_package!($root $namespace $name ($major, $minor, $patch) )
123 ),*
124 ]
125 };
126
127}