Skip to content

Commit

Permalink
Parse .crates2.json for packages with no given config
Browse files Browse the repository at this point in the history
Closes: #251
  • Loading branch information
nabijaczleweli committed May 25, 2024
1 parent eeb026f commit e82363c
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 14 deletions.
9 changes: 9 additions & 0 deletions man/cargo-install-update-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ Settable options:
* version range locks.
* environment variable value or removal,

If there is no configuration for a package,
the `$CARGO_DIR/.crates2.json` file is parsed instead,
which may yield, depending on the Cargo version, the following subset of the data:

* whether to use default features,
* additional feature list,
* build profile (equivalent to --debug if "debug", ignored otherwise),
* version range locks.

See cargo-install-update(1) for general information.

## OPTIONS
Expand Down
3 changes: 2 additions & 1 deletion man/cargo-install-update.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ updates for my cargo-installed executables, which was long and boring.

Updates packages from the main repository and git repositories.

See cargo-install-update-config(1) for further configuring updates.
See cargo-install-update-config(1) for further configuring updates,
and the metadata from `cargo install` that may be preserved by default.

The `CARGO_INSTALL_OPTS` environment variable can be set,
containing options to forward to the end of `cargo install` invocations'
Expand Down
2 changes: 1 addition & 1 deletion src/main-config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ fn actual_main() -> Result<(), i32> {
let opts = cargo_update::ConfigOptions::parse();
let config_file = cargo_update::ops::crates_file_in(&opts.cargo_dir).with_file_name(".install_config.toml");

let mut configuration = cargo_update::ops::PackageConfig::read(&config_file).map_err(|(e, r)| {
let mut configuration = cargo_update::ops::PackageConfig::read(&config_file, &config_file.with_file_name(".crates2.json")).map_err(|(e, r)| {
eprintln!("Reading config: {}", e);
r
})?;
Expand Down
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ fn actual_main() -> Result<(), i32> {

let crates_file = cargo_update::ops::crates_file_in(&opts.cargo_dir.1);
let http_proxy = cargo_update::ops::find_proxy(&crates_file);
let configuration = cargo_update::ops::PackageConfig::read(&crates_file.with_file_name(".install_config.toml")).map_err(|(e, r)| {
let configuration = cargo_update::ops::PackageConfig::read(&crates_file.with_file_name(".install_config.toml"),
&crates_file.with_file_name(".crates2.json")).map_err(|(e, r)| {
eprintln!("Reading config: {}", e);
r
})?;
Expand Down
87 changes: 79 additions & 8 deletions src/ops/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::fmt::{Formatter as FFormatter, Result as FResult, Write as FWrite};
use serde::{Deserializer, Deserialize, Serializer, Serialize};
use std::collections::{BTreeMap, BTreeSet};
use std::io::ErrorKind as IoErrorKind;
use json_deserializer as json;
use std::process::Command;
use std::default::Default;
use semver::VersionReq;
Expand Down Expand Up @@ -56,12 +58,13 @@ pub enum ConfigOperation {
/// ```
/// # use cargo_update::ops::PackageConfig;
/// # use std::fs::{File, create_dir_all};
/// # use std::path::Path;
/// # use std::env::temp_dir;
/// # let td = temp_dir().join("cargo_update-doctest").join("PackageConfig-0");
/// # create_dir_all(&td).unwrap();
/// # let config_file = td.join(".install_config.toml");
/// # let operations = [];
/// let mut configuration = PackageConfig::read(&config_file).unwrap();
/// let mut configuration = PackageConfig::read(&config_file, Path::new("/ENOENT")).unwrap();
/// configuration.insert("cargo_update".to_string(), PackageConfig::from(&operations));
/// PackageConfig::write(&configuration, &config_file).unwrap();
/// ```
Expand Down Expand Up @@ -301,7 +304,10 @@ impl PackageConfig {
}
}

/// Read a configset from the specified file.
/// Read a configset from the specified file, or from the given `.cargo2.json`.
///
/// The first file (usually `.install_config.toml`) is used by default for each package;
/// `.cargo2.json`, if any, is used to backfill existing data from cargo.
///
/// If the specified file doesn't exist an empty configset is returned.
///
Expand All @@ -312,6 +318,7 @@ impl PackageConfig {
/// # use cargo_update::ops::PackageConfig;
/// # use std::fs::{self, create_dir_all};
/// # use std::env::temp_dir;
/// # use std::path::Path;
/// # use std::io::Write;
/// # let td = temp_dir().join("cargo_update-doctest").join("PackageConfig-read-0");
/// # create_dir_all(&td).unwrap();
Expand All @@ -320,7 +327,7 @@ impl PackageConfig {
/// [cargo-update]\n\
/// default_features = true\n\
/// features = [\"serde\"]\n"[..]).unwrap();
/// assert_eq!(PackageConfig::read(&config_file), Ok({
/// assert_eq!(PackageConfig::read(&config_file, Path::new("/ENOENT")), Ok({
/// let mut pkgs = BTreeMap::new();
/// pkgs.insert("cargo-update".to_string(), PackageConfig {
/// toolchain: None,
Expand All @@ -340,12 +347,76 @@ impl PackageConfig {
/// pkgs
/// }));
/// ```
pub fn read(p: &Path) -> Result<BTreeMap<String, PackageConfig>, (String, i32)> {
if p.exists() {
toml::from_str(&fs::read_to_string(p).map_err(|e| (e.to_string(), 1))?).map_err(|e| (e.to_string(), 2))
} else {
Ok(BTreeMap::new())
pub fn read(p: &Path, cargo2_json: &Path) -> Result<BTreeMap<String, PackageConfig>, (String, i32)> {
let mut base = match fs::read_to_string(p) {
Ok(s) => toml::from_str(&s).map_err(|e| (e.to_string(), 2))?,
Err(e) if e.kind() == IoErrorKind::NotFound => BTreeMap::new(),
Err(e) => Err((e.to_string(), 1))?,
};
// {
// "installs": {
// "pixelmatch 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)": {
// "version_req": null,
// "bins": [
// "pixelmatch"
// ],
// "features": [
// "build-binary"
// ],
// "all_features": false,
// "no_default_features": false,
// "profile": "release",
// "target": "x86_64-unknown-linux-gnu",
// "rustc": "rustc 1.54.0 (a178d0322 2021-07-26)\nbinary: ..."
// },
if let Ok(cargo2_data) = fs::read(cargo2_json) {
if let Ok(json::Value::Object(mut cargo2)) = json::parse(&cargo2_data[..]) {
if let Some(json::Value::Object(installs)) = cargo2.remove("installs") {
for (k, v) in installs {
if let json::Value::Object(v) = v {
if let Some((name, _, _)) = super::parse_registry_package_ident(&k).or_else(|| super::parse_git_package_ident(&k)) {
if !base.contains_key(name) {
base.insert(name.to_string(), PackageConfig::cargo2_package_config(v));
}
}
}
}
}
}
}
Ok(base)
}

fn cargo2_package_config(mut blob: json::Object) -> PackageConfig {
let mut ret = PackageConfig::default();
// Nothing to parse PackageConfig::toolchain from
if let Some(json::Value::Bool(ndf)) = blob.get("no_default_features") {
ret.default_features = !ndf;
}
if let Some(json::Value::Array(fs)) = blob.remove("features") {
ret.features = fs.into_iter()
.filter_map(|f| match f {
json::Value::String(s) => Some(s.into_owned()),
_ => None,
})
.collect();
}
// Nothing to parse "all_features" into
if let Some(json::Value::String(prof)) = blob.get("profile") {
if prof == "debug" {
ret.debug = Some(true);
}
}
// Nothing to parse PackageConfig::install_prereleases from
// Nothing to parse PackageConfig::enforce_lock from
// "bins" is kinda like PackageConfig::respect_binaries but no really
if let Some(json::Value::String(prof)) = blob.get("version_req") {
if let Ok(req) = VersionReq::parse(prof) {
ret.target_version = Some(req);
}
}
// Nothing to parse PackageConfig::environment from
ret
}

/// Save a configset to the specified file.
Expand Down
9 changes: 6 additions & 3 deletions src/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,12 @@ fn parse_registry_package_ident(ident: &str) -> Option<(&str, &str, &str)> {
Some((name, version, reg.strip_prefix("registry+").or_else(|| reg.strip_prefix("sparse+"))?))
}
// alacritty 0.1.0 (git+https://github.com/jwilm/alacritty#eb231b3e70b87875df4bdd1974d5e94704024d70)
// chattium-oxide-client 0.1.0 (git+https://github.com/nabijaczleweli/chattium-oxide-client?branch=master#108a7b94f0e0dcb2a875f70fc0459d5a682df14c)
// chattium-oxide-client 0.1.0
// (git+https://github.com/nabijaczleweli/chattium-oxide-client?branch=master#108a7b94f0e0dcb2a875f70fc0459d5a682df14c)
// -> (name, url, sha)
// ("alacritty", "https://github.com/jwilm/alacritty", "eb231b3e70b87875df4bdd1974d5e94704024d70")
// ("chattium-oxide-client", "https://github.com/nabijaczleweli/chattium-oxide-client?branch=master", "108a7b94f0e0dcb2a875f70fc0459d5a682df14c")
// ("chattium-oxide-client", "https://github.com/nabijaczleweli/chattium-oxide-client?branch=master",
// "108a7b94f0e0dcb2a875f70fc0459d5a682df14c")
fn parse_git_package_ident(ident: &str) -> Option<(&str, &str, &str)> {
let mut idx = ident.splitn(3, ' ');
let (name, _, blob) = (idx.next()?, idx.next()?, idx.next()?);
Expand Down Expand Up @@ -1054,7 +1056,8 @@ pub fn crate_versions(buf: &[u8]) -> Result<Vec<Semver>, Cow<'static, str>> {
/// # Examples
///
/// ```
/// # #[cfg(all(target_pointer_width="64", target_endian="little"))] // https://github.com/nabijaczleweli/cargo-update/issues/235
/// # #[cfg(all(target_pointer_width="64", target_endian="little"))] //
/// https://github.com/nabijaczleweli/cargo-update/issues/235
/// # {
/// # use cargo_update::ops::assert_index_path;
/// # use std::env::temp_dir;
Expand Down

0 comments on commit e82363c

Please sign in to comment.