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};
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/// A trait for package registries.
34pub trait PackageRegistry {
35    /// A function to be called when the registry is reset.
36    fn reset(&mut self) {}
37
38    /// If the state of package registry can be well-defined by a revision, it
39    /// should return it. This is used to determine if the compiler should clean
40    /// and pull the registry again.
41    fn revision(&self) -> Option<NonZeroUsize> {
42        None
43    }
44
45    /// Resolves a package specification to a local path.
46    fn resolve(&self, spec: &PackageSpec) -> Result<Arc<Path>, PackageError>;
47
48    /// A list of all available packages and optionally descriptions for them.
49    ///
50    /// This function is optional to implement. It enhances the user experience
51    /// by enabling autocompletion for packages. Details about packages from the
52    /// `@preview` namespace are available from
53    /// `https://packages.typst.org/preview/index.json`.
54    fn packages(&self) -> &[PackageIndexEntry] {
55        &[]
56    }
57}
58
59/// An entry in the package index.
60#[derive(Debug, Clone, Deserialize)]
61pub struct PackageIndexEntry {
62    /// The namespace the package lives in.
63    #[serde(default)]
64    pub namespace: EcoString,
65    /// Details about the package itself.
66    #[serde(flatten)]
67    pub package: PackageInfo,
68    /// Details about the template, if the package is one.
69    #[serde(default)]
70    pub template: Option<TemplateInfo>,
71    /// The timestamp when the package was last updated.
72    #[serde(rename = "updatedAt", deserialize_with = "deserialize_timestamp")]
73    pub updated_at: Option<UtcDateTime>,
74    /// The local path of the package, if available.
75    #[serde(default)]
76    pub path: Option<PathBuf>,
77}
78
79impl PackageIndexEntry {
80    /// Get the package specification for this entry.
81    pub fn spec(&self) -> PackageSpec {
82        PackageSpec {
83            namespace: self.namespace.clone(),
84            name: self.package.name.clone(),
85            version: self.package.version,
86        }
87    }
88}
89
90fn deserialize_timestamp<'de, D>(deserializer: D) -> Result<Option<UtcDateTime>, D::Error>
91where
92    D: serde::Deserializer<'de>,
93{
94    let timestamp = i64::deserialize(deserializer)?;
95    Ok(UtcDateTime::from_unix_timestamp(timestamp).ok())
96}
97
98/// A trait for package registries that can be notified.
99pub trait Notifier {
100    /// Called when a package is being downloaded.
101    fn downloading(&self, _spec: &PackageSpec) {}
102}
103
104/// A dummy notifier that does nothing.
105#[derive(Debug, Default, Clone, Copy, Hash)]
106pub struct DummyNotifier;
107
108impl Notifier for DummyNotifier {}