diff options
| author | Laurenz <laurmaedje@gmail.com> | 2024-03-06 12:33:35 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-03-06 11:33:35 +0000 |
| commit | a558fd232b379e527eac2d5196eb9367b7d28032 (patch) | |
| tree | 1e91330df3b21bb86e067067cc9ff07ac8d513c8 /crates/typst-cli/src/package.rs | |
| parent | 898367f096fef507488438e00caae8c4ea1d0ff4 (diff) | |
Add `typst init` command (#3544)
Diffstat (limited to 'crates/typst-cli/src/package.rs')
| -rw-r--r-- | crates/typst-cli/src/package.rs | 81 |
1 files changed, 64 insertions, 17 deletions
diff --git a/crates/typst-cli/src/package.rs b/crates/typst-cli/src/package.rs index 8141ad19..7d3f2264 100644 --- a/crates/typst-cli/src/package.rs +++ b/crates/typst-cli/src/package.rs @@ -5,12 +5,16 @@ use std::path::{Path, PathBuf}; use codespan_reporting::term::{self, termcolor}; use ecow::eco_format; use termcolor::WriteColor; -use typst::diag::{PackageError, PackageResult}; -use typst::syntax::PackageSpec; +use typst::diag::{bail, PackageError, PackageResult, StrResult}; +use typst::syntax::package::{ + PackageInfo, PackageSpec, PackageVersion, VersionlessPackageSpec, +}; -use crate::download::download_with_progress; +use crate::download::{download, download_with_progress}; use crate::terminal; +const HOST: &str = "https://packages.typst.org"; + /// Make a package available in the on-disk cache. pub fn prepare_package(spec: &PackageSpec) -> PackageResult<PathBuf> { let subdir = @@ -25,30 +29,59 @@ pub fn prepare_package(spec: &PackageSpec) -> PackageResult<PathBuf> { if let Some(cache_dir) = dirs::cache_dir() { let dir = cache_dir.join(&subdir); + if dir.exists() { + return Ok(dir); + } // Download from network if it doesn't exist yet. - if spec.namespace == "preview" && !dir.exists() { + if spec.namespace == "preview" { download_package(spec, &dir)?; - } - - if dir.exists() { - return Ok(dir); + if dir.exists() { + return Ok(dir); + } } } Err(PackageError::NotFound(spec.clone())) } +/// Try to determine the latest version of a package. +pub fn determine_latest_version( + spec: &VersionlessPackageSpec, +) -> StrResult<PackageVersion> { + if spec.namespace == "preview" { + // For `@preview`, download the package index and find the latest + // version. + download_index()? + .iter() + .filter(|package| package.name == spec.name) + .map(|package| package.version) + .max() + .ok_or_else(|| eco_format!("failed to find package {spec}")) + } else { + // For other namespaces, search locally. We only search in the data + // directory and not the cache directory, because the latter is not + // intended for storage of local packages. + let subdir = format!("typst/packages/{}/{}", spec.namespace, spec.name); + dirs::data_dir() + .into_iter() + .flat_map(|dir| std::fs::read_dir(dir.join(&subdir)).ok()) + .flatten() + .filter_map(|entry| entry.ok()) + .map(|entry| entry.path()) + .filter_map(|path| path.file_name()?.to_string_lossy().parse().ok()) + .max() + .ok_or_else(|| eco_format!("please specify the desired version")) + } +} + /// Download a package over the network. fn download_package(spec: &PackageSpec, package_dir: &Path) -> PackageResult<()> { // The `@preview` namespace is the only namespace that supports on-demand // fetching. assert_eq!(spec.namespace, "preview"); - let url = format!( - "https://packages.typst.org/preview/{}-{}.tar.gz", - spec.name, spec.version - ); + let url = format!("{HOST}/preview/{}-{}.tar.gz", spec.name, spec.version); print_downloading(spec).unwrap(); @@ -67,14 +100,28 @@ fn download_package(spec: &PackageSpec, package_dir: &Path) -> PackageResult<()> }) } +/// Download the `@preview` package index. +fn download_index() -> StrResult<Vec<PackageInfo>> { + let url = format!("{HOST}/preview/index.json"); + match download(&url) { + Ok(response) => response + .into_json() + .map_err(|err| eco_format!("failed to parse package index: {err}")), + Err(ureq::Error::Status(404, _)) => { + bail!("failed to fetch package index (not found)") + } + Err(err) => bail!("failed to fetch package index ({err})"), + } +} + /// Print that a package downloading is happening. fn print_downloading(spec: &PackageSpec) -> io::Result<()> { let styles = term::Styles::default(); - let mut term_out = terminal::out(); - term_out.set_color(&styles.header_help)?; - write!(term_out, "downloading")?; + let mut out = terminal::out(); + out.set_color(&styles.header_help)?; + write!(out, "downloading")?; - term_out.reset()?; - writeln!(term_out, " {spec}") + out.reset()?; + writeln!(out, " {spec}") } |
