Jonathan Strong
1 year ago
3 changed files with 800 additions and 483 deletions
@ -0,0 +1,224 @@
|
||||
use std::path::{Path, PathBuf}; |
||||
use std::collections::BTreeMap; |
||||
use std::borrow::Cow; |
||||
|
||||
use serde::Deserialize; |
||||
use clap::Parser; |
||||
use tracing::{debug, error, info, warn}; |
||||
use tracing_subscriber::filter::EnvFilter; |
||||
use url::Url; |
||||
use anyhow::{anyhow, bail, Error}; |
||||
|
||||
#[derive(Debug, Clone, Deserialize)] |
||||
#[serde(rename_all = "kebab-case")] |
||||
pub struct DestinationRegistryConfig { |
||||
pub api_url: Url, |
||||
pub token: String, |
||||
} |
||||
|
||||
#[derive(Debug, Clone, Deserialize)] |
||||
#[serde(rename_all = "kebab-case")] |
||||
pub struct SourceRegistryConfig { |
||||
pub index_dir: PathBuf, |
||||
pub crate_files_dir: PathBuf, |
||||
} |
||||
|
||||
#[derive(Debug, Clone, Deserialize)] |
||||
#[serde(rename_all = "kebab-case")] |
||||
pub struct Config { |
||||
#[serde(alias = "source")] |
||||
pub src: SourceRegistryConfig, |
||||
#[serde(alias = "destination")] |
||||
pub dst: DestinationRegistryConfig, |
||||
} |
||||
|
||||
/// fields we need from Cargo.toml [package] section to combine with IndexMeta
|
||||
/// to form a PublishMeta.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] |
||||
pub struct PackageStub { |
||||
pub name: String, |
||||
pub version: Version, |
||||
#[serde(default)] |
||||
pub authors: Vec<String>, |
||||
pub description: Option<String>, |
||||
pub license: Option<String>, |
||||
pub license_file: Option<PathBuf>, |
||||
#[serde(default)] |
||||
pub categories: Vec<String>, |
||||
#[serde(default)] |
||||
pub keywords: Vec<String>, |
||||
pub readme: Option<PathBuf>, |
||||
pub repository: Option<String>, |
||||
pub homepage: Option<String>, |
||||
pub documentation: Option<String>, |
||||
} |
||||
|
||||
/// for parsing Cargo.toml to extract missing PublishMeta fields that do not appear
|
||||
/// in IndexMeta
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] |
||||
pub struct ManifestStub { |
||||
pub package: PackageStub, |
||||
} |
||||
|
||||
/// full definition of cargo publish json
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] |
||||
pub struct PublishMeta { |
||||
#[serde(borrow)] |
||||
pub name: String, |
||||
#[serde(alias = "version")] |
||||
pub vers: semver::Version, |
||||
#[serde(alias = "dependencies")] |
||||
#[serde(default)] |
||||
pub deps: Vec<PublishDependency>, |
||||
#[serde(default, borrow)] |
||||
pub features: BTreeMap<String, Vec<String>>, |
||||
#[serde(default, borrow)] |
||||
pub authors: Vec<String>, |
||||
#[serde(borrow)] |
||||
pub description: Option<String>, |
||||
#[serde(borrow)] |
||||
pub documentation: Option<String>, |
||||
#[serde(borrow)] |
||||
pub homepage: Option<String>, |
||||
#[serde(borrow)] |
||||
pub readme: Option<String>, |
||||
#[serde(borrow)] |
||||
pub readme_file: Option<String>, |
||||
#[serde(default, borrow)] |
||||
pub keywords: Vec<String>, |
||||
#[serde(default, borrow)] |
||||
pub categories: Vec<String>, |
||||
#[serde(borrow)] |
||||
pub license: Option<String>, |
||||
#[serde(borrow)] |
||||
pub license_file: Option<String>, |
||||
#[serde(borrow)] |
||||
pub repository: Option<String>, |
||||
#[serde(skip_serializing_if = "Option::is_none", borrow)] |
||||
pub links: Option<String>, |
||||
#[serde(skip_serializing_if = "Option::is_none", borrow)] |
||||
pub badges: Option<BTreeMap<String, String>>, |
||||
/// from ancient cargo versions
|
||||
#[serde(skip_serializing_if = "Option::is_none", borrow)] |
||||
pub features2: Option<BTreeMap<String, Vec<String>>>, |
||||
/// from ancient cargo versions
|
||||
#[serde(skip_serializing_if = "Option::is_none")] |
||||
pub v: Option<u8>, |
||||
} |
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] |
||||
pub struct PublishDependency { |
||||
pub optional: bool, |
||||
pub default_features: bool, |
||||
#[serde(borrow)] |
||||
pub name: String, |
||||
#[serde(borrow)] |
||||
pub features: Vec<String>, |
||||
// cargo and crates-io have this as string
|
||||
#[serde(alias = "req")] |
||||
pub version_req: semver::VersionReq, |
||||
#[serde(borrow)] |
||||
pub target: Option<String>, |
||||
// crates-io has this as option
|
||||
pub kind: PublishDependencyKind, |
||||
#[serde(skip_serializing_if = "Option::is_none", borrow)] |
||||
pub registry: Option<String>, |
||||
#[serde(skip_serializing_if = "Option::is_none", borrow)] |
||||
pub explicit_name_in_toml: Option<String>, |
||||
} |
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] |
||||
pub struct IndexMeta { |
||||
// same everything as publish metadata
|
||||
#[serde(borrow)] |
||||
pub name: String, |
||||
#[serde(alias = "version")] |
||||
pub vers: semver::Version, |
||||
#[serde(alias = "dependencies", borrow)] |
||||
pub features: BTreeMap<String, Vec<String>>, |
||||
#[serde(skip_serializing_if = "Option::is_none", borrow)] |
||||
pub links: Option<String>, |
||||
#[serde(skip_serializing_if = "Option::is_none", borrow)] |
||||
pub badges: Option<BTreeMap<String, String>>, |
||||
|
||||
// modified format/field names
|
||||
pub deps: Vec<IndexDependency>, |
||||
|
||||
// fields that don't appear in publish metadata
|
||||
pub cksum: String, |
||||
pub yanked: bool, |
||||
|
||||
// ancient fields, these were actually written
|
||||
// on sanskrit on stone tablets
|
||||
#[serde(skip_serializing_if = "Option::is_none", borrow)] |
||||
pub features2: Option<BTreeMap<String, Vec<String>>>, |
||||
#[serde(skip_serializing_if = "Option::is_none")] |
||||
pub v: Option<u8>, |
||||
} |
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] |
||||
pub struct IndexDependency { |
||||
/// corresponds to `explicit_name_in_toml` field in `publish::Dependency`
|
||||
/// when a dep is renamed in Cargo.toml, otherwise same as `package`.
|
||||
#[serde(borrow)] |
||||
pub name: String, |
||||
/// corresponds to `name` in `publish::Dependency`
|
||||
#[serde(skip_serializing_if = "Option::is_none", borrow)] |
||||
pub package: Option<String>, |
||||
/// in publish meta, this field is called `version_req`, and the index
|
||||
/// format requires it to be renamed to `req`
|
||||
#[serde(alias = "version_req")] |
||||
pub req: semver::VersionReq, |
||||
#[serde(borrow)] |
||||
pub features: Vec<String>, |
||||
pub optional: bool, |
||||
pub default_features: bool, |
||||
#[serde(borrow)] |
||||
pub target: Option<String>, |
||||
pub kind: DependencyKind, |
||||
#[serde(skip_serializing_if = "Option::is_none", borrow)] |
||||
pub registry: Option<String>, |
||||
} |
||||
|
||||
/// Section in which this dependency was defined
|
||||
#[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] |
||||
#[serde(rename_all = "lowercase")] |
||||
pub enum DependencyKind { |
||||
/// Used at run time
|
||||
Normal, |
||||
/// Used at build time, not available at run time
|
||||
Build, |
||||
/// Not fetched and not used, except for when used direclty in a workspace
|
||||
Dev, |
||||
} |
||||
|
||||
fn extract_manifest_from_tar<R: Read>(rdr: R) -> Result<Option<String>, Error> { |
||||
let mut archive = tar::Archive::new(rdr); |
||||
for entry in archive.entries()? { |
||||
let mut entry = entry?; |
||||
let path = entry.path()?; |
||||
|
||||
if path.ends_with("Cargo.toml.orig") { |
||||
let mut manifest_toml = String::new(); |
||||
entry.read_to_string(&mut manifest_toml)?; |
||||
return Ok(Some(manifest_toml)) |
||||
} |
||||
} |
||||
Ok(None) |
||||
} |
||||
|
||||
fn extract_readme_from_tar<R: Read>(rdr: R, readme_path: &Path) -> Result<Option<String>, Error> { |
||||
let mut archive = tar::Archive::new(rdr); |
||||
for entry in archive.entries()? { |
||||
let mut entry = entry?; |
||||
let path = entry.path()?; |
||||
if path == readme_path || path.ends_with(readme_path) { |
||||
let mut out = String::new(); |
||||
entry.read_to_string(&mut out)?; |
||||
return Ok(Some(out)) |
||||
} |
||||
} |
||||
Ok(None) |
||||
} |
||||
|
||||
|
Loading…
Reference in new issue