1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
#![warn(missing_docs)] #![allow(clippy::redundant_closure)] /*! This library is for accessing and manipulating a Cargo registry index. A very basic example: ```rust # fn main() -> Result<(), failure::Error> { # std::env::set_var("GIT_AUTHOR_NAME", "Index Admin"); # std::env::set_var("GIT_AUTHOR_EMAIL", "admin@example.com"); # let tmp_dir = tempfile::tempdir().unwrap(); # let index_path = tmp_dir.path().join("index"); # let index_url = "https://example.com/"; # let project = tmp_dir.path().join("foo"); # let status = std::process::Command::new("cargo") # .args(&["new", "--vcs=none", project.to_str().unwrap()]) # .status()?; # assert!(status.success()); # let manifest_path = project.join("Cargo.toml"); // Initialize a new index. reg_index::init(&index_path, "https://example.com", None)?; // Add a package to the index. reg_index::add(&index_path, index_url, Some(&manifest_path), None, None)?; // Packages can be yanked. reg_index::yank(&index_path, "foo", "0.1.0")?; // Get the metadata for the new entry. let pkgs = reg_index::list(&index_path, "foo", None)?; // Displays something like: // {"name":"foo","vers":"0.1.0","deps":[],"features":{},"cksum":"d87f097fcc13ae97736a7d8086fb70a0499f3512f0fe1fe82e6422f25f567c83","yanked":true,"links":null} println!("{}", serde_json::to_string(&pkgs[0])?); # Ok(()) # } ``` See https://doc.rust-lang.org/cargo/reference/registries.html for documentation about Cargo registries. ## Locking The functions here perform simple filesystem locking to ensure multiple commands running at the same time do not interfere with one another. This requires that the filesystem supports locking. */ use failure::{Error, ResultExt}; use semver::{Version, VersionReq}; use serde_derive::{Deserialize, Serialize}; use std::{collections::BTreeMap, fs, path::Path}; use url::Url; mod add; mod init; mod list; mod lock; mod metadata; mod util; mod validate; mod yank; pub use add::{add, add_from_crate}; pub use init::init; pub use list::{list, list_all}; pub use metadata::{metadata, metadata_from_crate}; pub use validate::validate; pub use yank::{set_yank, unyank, yank}; /// An entry for a single version of a package in the index. #[derive(Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct IndexPackage { /// The name of the package. pub name: String, /// The version of the package. pub vers: Version, /// List of direct dependencies of the package. pub deps: Vec<IndexDependency>, /// Cargo features defined in the package. pub features: BTreeMap<String, Vec<String>>, /// Checksum of the `.crate` file. pub cksum: String, /// Whether or not this package is yanked. pub yanked: bool, /// Optional string that is the name of a native library the package is /// linking to. pub links: Option<String>, #[doc(hidden)] #[serde(skip)] __nonexhaustive: (), } /// A dependency of a package. #[derive(Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct IndexDependency { /// Name of the dependency. /// /// If the dependency is renamed from the original package name, /// this is the new name. The original package name is stored in /// the `package` field. pub name: String, /// The semver requirement for this dependency. pub req: VersionReq, /// List of features enabled for this dependency. pub features: Vec<String>, /// Whether or not this is an optional dependency. pub optional: bool, /// Whether or not default features are enabled. pub default_features: bool, /// The target platform for the dependency. pub target: Option<String>, /// The dependency kind. // Required, but crates.io has some broken missing entries. #[serde(default, deserialize_with = "parse_dependency_kind")] pub kind: cargo_metadata::DependencyKind, /// The URL of the index of the registry where this dependency is from. /// /// If not specified or null, it is assumed the dependency is in the /// current registry. #[serde(default, with = "url_serde")] pub registry: Option<Url>, /// If the dependency is renamed, this is a string of the actual package /// name. If None, this dependency is not renamed. pub package: Option<String>, #[doc(hidden)] #[serde(skip)] __nonexhaustive: (), } fn parse_dependency_kind<'de, D>(d: D) -> Result<cargo_metadata::DependencyKind, D::Error> where D: serde::Deserializer<'de>, { serde::Deserialize::deserialize(d).map(|x: Option<_>| x.unwrap_or_default()) } /// The configuration file of the index. /// /// This is stored in the root of the index repo as `config.json`. #[derive(Serialize, Deserialize)] pub struct IndexConfig { /// URL that Cargo uses to download crates. /// /// This can have the markers `{crate}` and `{version}`. If the markers /// are not present, Cargo automatically appends /// `/{crate}/{version}/download` to the end. #[serde(with = "url_serde")] pub dl: Url, /// URL that Cargo uses for the web API (publish/yank/search/etc.). /// /// This is optional. If not specified, Cargo will refuse to publish to /// this registry. #[serde(default, with = "url_serde")] #[serde(skip_serializing_if = "Option::is_none")] pub api: Option<Url>, #[doc(hidden)] #[serde(skip)] __nonexhaustive: (), } /// Return the configuration file in an index. pub fn load_config(index: impl AsRef<Path>) -> Result<IndexConfig, Error> { let path = index.as_ref().join("config.json"); let f = fs::File::open(&path).with_context(|_| format!("Failed to open `{}`.", path.display()))?; let index_cfg: IndexConfig = serde_json::from_reader(f) .with_context(|_| format!("Failed to deserialize `{}`.", path.display()))?; Ok(index_cfg) }