use std::collections::HashMap;
use std::path::{Path, PathBuf};
use async_std::sync::Arc;
use oro_client::OroClient;
use oro_common::{CorgiManifest, CorgiPackument, CorgiVersionMetadata, Packument, VersionMetadata};
use url::Url;
pub use oro_package_spec::{PackageSpec, VersionSpec};
use crate::entries::Entries;
use crate::error::Result;
#[cfg(not(target_arch = "wasm32"))]
use crate::fetch::DirFetcher;
#[cfg(not(target_arch = "wasm32"))]
use crate::fetch::GitFetcher;
use crate::fetch::{DummyFetcher, NpmFetcher, PackageFetcher};
use crate::package::Package;
use crate::resolver::{PackageResolution, PackageResolver};
use crate::tarball::Tarball;
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct NassunOpts {
#[cfg(not(target_arch = "wasm32"))]
cache: Option<PathBuf>,
base_dir: Option<PathBuf>,
default_tag: Option<String>,
registries: HashMap<Option<String>, Url>,
memoize_metadata: bool,
#[cfg(not(target_arch = "wasm32"))]
proxy: bool,
#[cfg(not(target_arch = "wasm32"))]
proxy_url: Option<String>,
#[cfg(not(target_arch = "wasm32"))]
no_proxy_domain: Option<String>,
#[cfg(not(target_arch = "wasm32"))]
fetch_retries: u32,
}
impl NassunOpts {
pub fn new() -> Self {
Default::default()
}
#[cfg(not(target_arch = "wasm32"))]
pub fn cache(mut self, cache: impl AsRef<Path>) -> Self {
self.cache = Some(PathBuf::from(cache.as_ref()));
self
}
pub fn registry(mut self, registry: Url) -> Self {
self.registries.insert(None, registry);
self
}
pub fn scope_registry(mut self, scope: impl AsRef<str>, registry: Url) -> Self {
let scope = scope.as_ref();
self.registries.insert(
Some(scope.strip_prefix('@').unwrap_or(scope).to_string()),
registry,
);
self
}
pub fn base_dir(mut self, base_dir: impl AsRef<Path>) -> Self {
self.base_dir = Some(PathBuf::from(base_dir.as_ref()));
self
}
pub fn default_tag(mut self, default_tag: impl AsRef<str>) -> Self {
self.default_tag = Some(default_tag.as_ref().into());
self
}
pub fn memoize_metadata(mut self, memoize: bool) -> Self {
self.memoize_metadata = memoize;
self
}
#[cfg(not(target_arch = "wasm32"))]
pub fn proxy(mut self, proxy: bool) -> Self {
self.proxy = proxy;
self
}
#[cfg(not(target_arch = "wasm32"))]
pub fn proxy_url(mut self, proxy_url: impl AsRef<str>) -> Self {
self.proxy_url = Some(proxy_url.as_ref().into());
self
}
#[cfg(not(target_arch = "wasm32"))]
pub fn no_proxy_domain(mut self, no_proxy_domain: impl AsRef<str>) -> Self {
self.no_proxy_domain = Some(no_proxy_domain.as_ref().into());
self
}
#[cfg(not(target_arch = "wasm32"))]
pub fn fetch_retries(mut self, fetch_retries: u32) -> Self {
self.fetch_retries = fetch_retries;
self
}
pub fn build(self) -> Nassun {
let registry = self
.registries
.get(&None)
.cloned()
.unwrap_or_else(|| "https://registry.npmjs.org/".parse().unwrap());
#[cfg(target_arch = "wasm32")]
let client_builder = OroClient::builder().registry(registry);
#[cfg(not(target_arch = "wasm32"))]
let mut client_builder = OroClient::builder()
.registry(registry)
.fetch_retries(self.fetch_retries)
.proxy(self.proxy);
#[cfg(not(target_arch = "wasm32"))]
if let Some(domain) = self.no_proxy_domain {
client_builder = client_builder.no_proxy_domain(domain);
}
#[cfg(not(target_arch = "wasm32"))]
if let Some(proxy) = self.proxy_url {
if let Ok(builder) = client_builder.clone().proxy_url(&proxy) {
client_builder = builder;
} else {
tracing::warn!("Failed to parse proxy URL: {}", proxy)
}
}
#[cfg(not(target_arch = "wasm32"))]
let cache = if let Some(cache) = self.cache {
client_builder = client_builder.cache(cache.clone());
Arc::new(Some(cache))
} else {
Arc::new(None)
};
let client: OroClient = client_builder.build();
Nassun {
#[cfg(not(target_arch = "wasm32"))]
cache,
#[cfg(target_arch = "wasm32")]
cache: Arc::new(None),
resolver: PackageResolver {
#[cfg(target_arch = "wasm32")]
base_dir: PathBuf::from("."),
#[cfg(not(target_arch = "wasm32"))]
base_dir: self
.base_dir
.unwrap_or_else(|| std::env::current_dir().expect("failed to get cwd.")),
default_tag: self.default_tag.unwrap_or_else(|| "latest".into()),
},
npm_fetcher: Arc::new(NpmFetcher::new(
#[allow(clippy::redundant_clone)]
client.clone(),
self.registries,
self.memoize_metadata,
)),
#[cfg(not(target_arch = "wasm32"))]
dir_fetcher: Arc::new(DirFetcher::new()),
#[cfg(not(target_arch = "wasm32"))]
git_fetcher: Arc::new(GitFetcher::new(client)),
}
}
}
#[derive(Clone)]
pub struct Nassun {
cache: Arc<Option<PathBuf>>,
resolver: PackageResolver,
npm_fetcher: Arc<dyn PackageFetcher>,
#[cfg(not(target_arch = "wasm32"))]
dir_fetcher: Arc<dyn PackageFetcher>,
#[cfg(not(target_arch = "wasm32"))]
git_fetcher: Arc<dyn PackageFetcher>,
}
impl Default for Nassun {
fn default() -> Self {
NassunOpts::new().build()
}
}
impl Nassun {
pub fn new() -> Self {
Default::default()
}
pub async fn packument(spec: impl AsRef<str>) -> Result<Arc<Packument>> {
Self::new().resolve(spec.as_ref()).await?.packument().await
}
pub async fn corgi_packument(spec: impl AsRef<str>) -> Result<Arc<CorgiPackument>> {
Self::new()
.resolve(spec.as_ref())
.await?
.corgi_packument()
.await
}
pub async fn metadata(spec: impl AsRef<str>) -> Result<VersionMetadata> {
Self::new().resolve(spec.as_ref()).await?.metadata().await
}
pub async fn corgi_metadata(spec: impl AsRef<str>) -> Result<CorgiVersionMetadata> {
Self::new()
.resolve(spec.as_ref())
.await?
.corgi_metadata()
.await
}
pub async fn tarball(spec: impl AsRef<str>) -> Result<Tarball> {
Self::new().resolve(spec.as_ref()).await?.tarball().await
}
pub async fn entries(spec: impl AsRef<str>) -> Result<Entries> {
Self::new().resolve(spec.as_ref()).await?.entries().await
}
pub async fn resolve(&self, spec: impl AsRef<str>) -> Result<Package> {
let spec = spec.as_ref().parse()?;
self.resolve_spec(spec).await
}
pub async fn resolve_spec(&self, spec: PackageSpec) -> Result<Package> {
let fetcher = self.pick_fetcher(&spec);
let name = fetcher.name(&spec, &self.resolver.base_dir).await?;
self.resolver
.resolve(name, spec, fetcher, self.cache.clone())
.await
}
pub fn resolve_from(
&self,
name: String,
from: PackageSpec,
resolved: PackageResolution,
) -> Package {
let fetcher = self.pick_fetcher(&from);
self.resolver
.resolve_from(name, from, resolved, fetcher, self.cache.clone())
}
pub fn dummy_from_manifest(manifest: CorgiManifest) -> Package {
Package {
cache: Arc::new(None),
from: PackageSpec::Dir {
path: PathBuf::from("."),
},
name: manifest.name.clone().unwrap_or_else(|| "dummy".to_string()),
resolved: PackageResolution::Dir {
name: manifest.name.clone().unwrap_or_else(|| "dummy".to_string()),
path: PathBuf::from("."),
},
base_dir: PathBuf::from("."),
fetcher: Arc::new(DummyFetcher(manifest)),
}
}
fn pick_fetcher(&self, arg: &PackageSpec) -> Arc<dyn PackageFetcher> {
use PackageSpec::*;
match *arg {
Alias { ref spec, .. } => self.pick_fetcher(spec),
Npm { .. } => self.npm_fetcher.clone(),
#[cfg(not(target_arch = "wasm32"))]
Dir { .. } => self.dir_fetcher.clone(),
#[cfg(target_arch = "wasm32")]
Dir { .. } => panic!(
"Directory dependencies are not enabled. (While trying to process {})",
arg
),
#[cfg(not(target_arch = "wasm32"))]
Git(..) => self.git_fetcher.clone(),
#[cfg(target_arch = "wasm32")]
Git(..) => panic!(
"Git dependencies are not enabled. (While trying to process {})",
arg
),
}
}
}