tinymist_package/
registry.rs

1//! Package Registry.
2
3use std::num::NonZeroUsize;
4use std::path::PathBuf;
5use std::{path::Path, sync::Arc};
6
7use ecow::EcoString;
8use serde::Deserialize;
9use tinymist_std::time::UtcDateTime;
10pub use typst::diag::PackageError;
11pub use typst::syntax::package::PackageSpec;
12use typst::syntax::package::{PackageInfo, TemplateInfo, VersionlessPackageSpec};
13
14mod dummy;
15pub use dummy::*;
16
17mod memory;
18pub use memory::*;
19
20#[cfg(feature = "browser")]
21mod browser;
22#[cfg(feature = "browser")]
23pub use browser::*;
24
25#[cfg(feature = "http-registry")]
26mod http;
27#[cfg(feature = "http-registry")]
28pub use http::*;
29
30/// The default Typst registry.
31pub const DEFAULT_REGISTRY: &str = "https://packages.typst.org";
32
33/// The namespace for Typst registry.
34pub const PREVIEW_NS: &str = "preview";
35
36/// An extension trait for package specifications.
37pub trait PackageSpecExt {
38    /// Returns true if the package spec is in the preview namespace.
39    fn is_preview(&self) -> bool;
40}
41
42impl PackageSpecExt for PackageSpec {
43    fn is_preview(&self) -> bool {
44        self.namespace == PREVIEW_NS
45    }
46}
47
48impl PackageSpecExt for VersionlessPackageSpec {
49    fn is_preview(&self) -> bool {
50        self.namespace == PREVIEW_NS
51    }
52}
53
54/// A trait for package registries.
55pub trait PackageRegistry {
56    /// A function to be called when the registry is reset.
57    fn reset(&mut self) {}
58
59    /// If the state of package registry can be well-defined by a revision, it
60    /// should return it. This is used to determine if the compiler should clean
61    /// and pull the registry again.
62    fn revision(&self) -> Option<NonZeroUsize> {
63        None
64    }
65
66    /// Resolves a package specification to a local path.
67    fn resolve(&self, spec: &PackageSpec) -> Result<Arc<Path>, PackageError>;
68
69    /// A list of all available packages and optionally descriptions for them.
70    ///
71    /// This function is optional to implement. It enhances the user experience
72    /// by enabling autocompletion for packages. Details about packages from the
73    /// `@preview` namespace are available from
74    /// `https://packages.typst.org/preview/index.json`.
75    fn packages(&self) -> &[PackageIndexEntry] {
76        &[]
77    }
78}
79
80/// An entry in the package index.
81#[derive(Debug, Clone, Deserialize)]
82pub struct PackageIndexEntry {
83    /// The namespace the package lives in.
84    #[serde(default)]
85    pub namespace: EcoString,
86    /// Details about the package itself.
87    #[serde(flatten)]
88    pub package: PackageInfo,
89    /// Details about the template, if the package is one.
90    #[serde(default)]
91    pub template: Option<TemplateInfo>,
92    /// The timestamp when the package was last updated.
93    #[serde(rename = "updatedAt", deserialize_with = "deserialize_timestamp")]
94    pub updated_at: Option<UtcDateTime>,
95    /// The local path of the package, if available.
96    #[serde(default)]
97    pub path: Option<PathBuf>,
98}
99
100impl PackageIndexEntry {
101    /// Get the package specification for this entry.
102    pub fn spec(&self) -> PackageSpec {
103        PackageSpec {
104            namespace: self.namespace.clone(),
105            name: self.package.name.clone(),
106            version: self.package.version,
107        }
108    }
109
110    /// Check if this entry matches a versionless package specification.
111    pub fn matches_versionless(&self, spec: &VersionlessPackageSpec) -> bool {
112        self.namespace == spec.namespace && self.package.name == spec.name
113    }
114}
115
116fn deserialize_timestamp<'de, D>(deserializer: D) -> Result<Option<UtcDateTime>, D::Error>
117where
118    D: serde::Deserializer<'de>,
119{
120    let timestamp = i64::deserialize(deserializer)?;
121    Ok(UtcDateTime::from_unix_timestamp(timestamp).ok())
122}
123
124/// A trait for package registries that can be notified.
125pub trait Notifier {
126    /// Called when a package is being downloaded.
127    fn downloading(&self, _spec: &PackageSpec) {}
128}
129
130/// A dummy notifier that does nothing.
131#[derive(Debug, Default, Clone, Copy, Hash)]
132pub struct DummyNotifier;
133
134impl Notifier for DummyNotifier {}