mirror of https://github.com/stelzo/typst.git
git download method
This commit is contained in:
parent
ec3bc7dd7e
commit
3436f825f2
|
|
@ -129,6 +129,17 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
|
||||
|
||||
[[package]]
|
||||
name = "auth-git2"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3810b5af212b013fe7302b12d86616c6c39a48e18f2e4b812a5a9e5710213791"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"git2",
|
||||
"terminal-prompt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
|
|
@ -863,6 +874,21 @@ dependencies = [
|
|||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"libc",
|
||||
"libgit2-sys",
|
||||
"log",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.4.1"
|
||||
|
|
@ -1322,6 +1348,20 @@ dependencies = [
|
|||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
version = "0.17.0+1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10472326a8a6477c3c20a64547b0059e4b0d086869eee31e6d7da728a8eb7224"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"libssh2-sys",
|
||||
"libz-sys",
|
||||
"openssl-sys",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.8"
|
||||
|
|
@ -1339,6 +1379,32 @@ dependencies = [
|
|||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libssh2-sys"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"libz-sys",
|
||||
"openssl-sys",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.6"
|
||||
|
|
@ -2505,6 +2571,16 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal-prompt"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "572818b3472910acbd5dff46a3413715c18e934b071ab2ba464a7b2c2af16376"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.4.0"
|
||||
|
|
@ -2854,11 +2930,13 @@ dependencies = [
|
|||
name = "typst-kit"
|
||||
version = "0.12.0"
|
||||
dependencies = [
|
||||
"auth-git2",
|
||||
"dirs",
|
||||
"ecow",
|
||||
"env_proxy",
|
||||
"flate2",
|
||||
"fontdb",
|
||||
"git2",
|
||||
"native-tls",
|
||||
"once_cell",
|
||||
"openssl",
|
||||
|
|
@ -3422,6 +3500,22 @@ version = "0.1.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.9"
|
||||
|
|
@ -3431,6 +3525,12 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ typst-timing = { path = "crates/typst-timing", version = "0.12.0" }
|
|||
typst-utils = { path = "crates/typst-utils", version = "0.12.0" }
|
||||
typst-assets = { git = "https://github.com/typst/typst-assets", rev = "8cccef9" }
|
||||
typst-dev-assets = { git = "https://github.com/typst/typst-dev-assets", rev = "b07d156" }
|
||||
auth-git2 = "0.5.5"
|
||||
arrayvec = "0.7.4"
|
||||
az = "1.2"
|
||||
base64 = "0.22"
|
||||
|
|
@ -58,6 +59,7 @@ env_proxy = "0.4"
|
|||
flate2 = "1"
|
||||
fontdb = { version = "0.21", default-features = false }
|
||||
fs_extra = "1.3"
|
||||
git2 = "0.19.0"
|
||||
hayagriva = "0.8"
|
||||
heck = "0.5"
|
||||
hypher = "0.1.4"
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ native-tls = { workspace = true, optional = true }
|
|||
once_cell = { workspace = true }
|
||||
tar = { workspace = true, optional = true }
|
||||
ureq = { workspace = true, optional = true }
|
||||
git2 = { workspace = true, optional = true }
|
||||
auth-git2 = { workspace = true, optional = true }
|
||||
|
||||
# Explicitly depend on OpenSSL if applicable, so that we can add the
|
||||
# `openssl/vendored` feature to it if `vendor-openssl` is enabled.
|
||||
|
|
@ -40,7 +42,7 @@ fonts = ["dep:fontdb", "fontdb/memmap", "fontdb/fontconfig"]
|
|||
# Add generic downloading utilities
|
||||
downloads = ["downloads_http", "downloads_git"]
|
||||
downloads_http = ["dep:env_proxy", "dep:native-tls", "dep:ureq", "dep:openssl"]
|
||||
downloads_git = []
|
||||
downloads_git = ["git2", "auth-git2"]
|
||||
|
||||
# Add package downloading utilities, implies `downloads`
|
||||
packages = ["downloads", "dep:dirs", "dep:flate2", "dep:tar"]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
use std::cell::{RefCell, RefMut};
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt::Debug;
|
||||
use std::fs;
|
||||
use std::io::{self, ErrorKind, Read};
|
||||
use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
use auth_git2::GitAuthenticator;
|
||||
use crate::package_downloads::{
|
||||
DownloadState, PackageDownloader, Progress, DEFAULT_NAMESPACE,
|
||||
};
|
||||
use ecow::{eco_format, EcoString};
|
||||
use native_tls::{Certificate, TlsConnector};
|
||||
use once_cell::sync::OnceCell;
|
||||
use typst_library::diag::{bail, PackageError, PackageResult};
|
||||
use typst_syntax::package::{PackageInfo, PackageSpec, VersionlessPackageSpec};
|
||||
use git2::{AutotagOption, FetchOptions, Progress as GitProgress, RemoteCallbacks};
|
||||
use git2::build::{CheckoutBuilder, RepoBuilder};
|
||||
use typst_library::html::tag::form;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GitDownloader;
|
||||
|
||||
impl GitDownloader {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn download_with_progress(
|
||||
&self,
|
||||
repo: &str,
|
||||
tag: &str,
|
||||
dest: &Path,
|
||||
progress: &mut dyn Progress,
|
||||
) -> Result<(), EcoString> {
|
||||
progress.print_start();
|
||||
|
||||
eprintln!("{} {} {}", repo, tag, dest.display());
|
||||
|
||||
let state = DownloadState{
|
||||
content_len: None,
|
||||
total_downloaded: 0,
|
||||
bytes_per_second: VecDeque::from(vec![0; 5]),
|
||||
start_time: Instant::now(),
|
||||
};
|
||||
|
||||
|
||||
let auth = GitAuthenticator::default();
|
||||
let git_config = git2::Config::open_default().map_err(|err| {EcoString::from(format!("{:?}", err))})?;
|
||||
|
||||
let mut fetch_options = FetchOptions::new();
|
||||
let mut remote_callbacks = RemoteCallbacks::new();
|
||||
|
||||
remote_callbacks.credentials(auth.credentials(&git_config));
|
||||
fetch_options
|
||||
.remote_callbacks(remote_callbacks);
|
||||
|
||||
let repo = RepoBuilder::new()
|
||||
.fetch_options(fetch_options)
|
||||
.clone(repo, dest).map_err(|err| {EcoString::from(format!("{:?}", err))})?;
|
||||
|
||||
let (object, reference) = repo
|
||||
.revparse_ext(tag).map_err(|err| {EcoString::from(format!("{:?}", err))})?;
|
||||
repo.checkout_tree(&object, None).map_err(|err| {EcoString::from(format!("{:?}", err))})?;
|
||||
|
||||
match reference {
|
||||
// gref is an actual reference like branches or tags
|
||||
Some(gref) => repo.set_head(gref.name().unwrap()),
|
||||
// this is a commit, not a reference
|
||||
None => repo.set_head_detached(object.id()),
|
||||
}.map_err(|err| {EcoString::from(format!("{:?}", err))})?;
|
||||
|
||||
progress.print_finish(&state);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_namespace(ns: &str, name: &str) -> Result<String, EcoString> {
|
||||
|
||||
let mut parts = ns.splitn(2, ":");
|
||||
let schema = parts.next().ok_or_else(|| {
|
||||
eco_format!("expected schema in {}", ns)
|
||||
})?;
|
||||
let repo = parts.next().ok_or_else(|| {
|
||||
eco_format!("invalid package repo {}", ns)
|
||||
})?;
|
||||
|
||||
if !schema.eq("git") {
|
||||
Err(eco_format!("invalid schema in {}", ns))?
|
||||
}
|
||||
|
||||
Ok(format!("{}/{}.git", repo, name))
|
||||
}
|
||||
}
|
||||
|
||||
impl PackageDownloader for GitDownloader {
|
||||
fn download_index(
|
||||
&self,
|
||||
spec: &VersionlessPackageSpec,
|
||||
) -> Result<Vec<PackageInfo>, EcoString> {
|
||||
|
||||
//todo ls-remote
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn download(
|
||||
&self,
|
||||
spec: &PackageSpec,
|
||||
package_dir: &Path,
|
||||
progress: &mut dyn Progress,
|
||||
) -> PackageResult<()> {
|
||||
let repo = Self::parse_namespace(spec.namespace.as_str(), spec.name.as_str()).map_err(|x| PackageError::Other(Some(x)))?;
|
||||
let tag = format!("v{}", spec.version);
|
||||
self.download_with_progress(repo.as_str(), tag.as_str(), package_dir, progress).map_err(|x| PackageError::Other(Some(x)))
|
||||
}
|
||||
}
|
||||
|
|
@ -89,7 +89,7 @@ impl HttpDownloader {
|
|||
|
||||
/// Download binary data from the given url.
|
||||
#[allow(clippy::result_large_err)]
|
||||
pub fn download(&self, url: &str) -> Result<ureq::Response, ureq::Error> {
|
||||
pub fn perform_download(&self, url: &str) -> Result<ureq::Response, ureq::Error> {
|
||||
let mut builder = ureq::AgentBuilder::new();
|
||||
let mut tls = TlsConnector::builder();
|
||||
|
||||
|
|
@ -125,7 +125,7 @@ impl HttpDownloader {
|
|||
progress: &mut dyn Progress,
|
||||
) -> Result<Vec<u8>, ureq::Error> {
|
||||
progress.print_start();
|
||||
let response = self.download(url)?;
|
||||
let response = self.perform_download(url)?;
|
||||
Ok(RemoteReader::from_response(response, progress).download()?)
|
||||
}
|
||||
|
||||
|
|
@ -145,6 +145,10 @@ impl HttpDownloader {
|
|||
eco_format!("invalid package namespace in {}", ns)
|
||||
})?;
|
||||
|
||||
if !schema.eq("http") && !schema.eq("https") {
|
||||
Err(eco_format!("invalid schema in {}", ns))?
|
||||
}
|
||||
|
||||
Ok((format!("{}://{}", schema, registry), ns.to_string()))
|
||||
}
|
||||
}
|
||||
|
|
@ -264,7 +268,7 @@ impl PackageDownloader for HttpDownloader {
|
|||
fn download_index(&self, spec: &VersionlessPackageSpec) -> Result<Vec<PackageInfo>, EcoString> {
|
||||
let (registry, namespace) = Self::parse_namespace(spec.namespace.as_str())?;
|
||||
let url = format!("{registry}/{namespace}/index.json");
|
||||
match self.download(&url) {
|
||||
match self.perform_download(&url) {
|
||||
Ok(response) => response.into_json().map_err(|err| {
|
||||
eco_format!("failed to parse package index: {err}")
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use std::time::Instant;
|
|||
use ecow::{eco_format, EcoString};
|
||||
use typst_library::diag::{PackageError, PackageResult};
|
||||
use typst_syntax::package::{PackageInfo, PackageSpec, VersionlessPackageSpec};
|
||||
use crate::package_downloads::git::GitDownloader;
|
||||
|
||||
/// The public namespace in the default Typst registry.
|
||||
pub const DEFAULT_NAMESPACE: &str = "preview";
|
||||
|
|
@ -76,12 +77,12 @@ impl Downloader {
|
|||
}
|
||||
|
||||
fn make_git_downloader(_cert: Option<PathBuf>) -> Option<Box<dyn PackageDownloader>>{
|
||||
#[cfg(not(feature = "downloads_http"))]
|
||||
#[cfg(not(feature = "downloads_git"))]
|
||||
{ None }
|
||||
|
||||
#[cfg(feature = "downloads_http")]
|
||||
#[cfg(feature = "downloads_git")]
|
||||
{
|
||||
None
|
||||
Some(Box::new(GitDownloader::new()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue