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}