Skip to content

Commit

Permalink
cmm: Use toml-normalize CLI from Rust
Browse files Browse the repository at this point in the history
Signed-off-by: Nick Spinale <[email protected]>
  • Loading branch information
nspin committed Nov 7, 2023
1 parent 6fc1dfd commit 42802ee
Show file tree
Hide file tree
Showing 9 changed files with 241 additions and 136 deletions.
42 changes: 25 additions & 17 deletions hacking/cargo-manifest-management/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions hacking/cargo-manifest-management/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ resolver = "2"
members = [
"crates/toml-path-regex",
"crates/toml-normalize",
"crates/manage-cargo-manifests",
]
22 changes: 17 additions & 5 deletions hacking/cargo-manifest-management/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,29 @@
# SPDX-License-Identifier: BSD-2-Clause
#

blueprint := $$(nix-build -A blueprintJSON --no-out-link)

target_dir := target

bin_dir := $(target_dir)/debug

run := PATH=$(bin_dir):$$PATH $(bin_dir)/manage-cargo-manifests --blueprint $(blueprint)

.PHONY: none
none:

.PHONY: clean
clean:
rm -rf target
rm -rf $(target_dir)

.INTERMEDIATE: bins
bins:
cargo build

.PHONY: update
update:
script=$$(nix-build -A updateScript --no-out-link) && $$script
update: bins
$(run)

.PHONY: check
check:
nix-build -A witness --no-out-link
check: bins
$(run) --just-check
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#
# Copyright 2023, Colias Group, LLC
#
# SPDX-License-Identifier: BSD-2-Clause
#

[package]
name = "manage-cargo-manifests"
version = "0.1.0"
authors = ["Nick Spinale <[email protected]>"]
edition = "2021"
license = "BSD-2-Clause"

[dependencies]
clap = { version = "4.4.6", features = [ "derive" ] }
serde = { version = "1.0", features = [ "derive" ] }
serde_json = "1.0"
toml = "0.8.6"
similar = "2.3"
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//
// Copyright 2023, Colias Group, LLC
//
// SPDX-License-Identifier: BSD-2-Clause
//

use std::fs::{self, File};
use std::io::Write;
use std::path::PathBuf;
use std::process::{self, Command, Stdio};
use std::str;

use clap::Parser;
use serde::Deserialize;
use serde_json::Value as JsonValue;
use similar::{ChangeTag, TextDiff};
use toml::Table as TomlTable;

#[derive(Debug, Parser)]
struct Args {
#[arg(long)]
blueprint: PathBuf,

#[arg(long)]
just_check: bool,
}

fn main() {
let args = Args::parse();
let blueprint_file = File::open(&args.blueprint).unwrap();
let blueprint: Blueprint = serde_json::from_reader(blueprint_file).unwrap();
blueprint.execute(args.just_check);
}

#[derive(Debug, Deserialize)]
#[serde(transparent)]
pub struct Blueprint {
pub entries: Vec<Entry>,
}

#[derive(Debug, Deserialize)]
pub struct Entry {
#[serde(rename = "absolutePath")]
pub absolute_path: PathBuf,
#[serde(rename = "manifestValue")]
pub manifest_value: TomlTable,
#[serde(rename = "frontmatter")]
pub frontmatter: Option<String>,
#[serde(rename = "formatPolicyOverrides")]
pub format_policy_overrides: Vec<JsonValue>,
#[serde(rename = "justEnsureEquivalence")]
pub just_ensure_equivalence: bool,
}

impl Blueprint {
pub fn execute(&self, just_check: bool) {
for entry in self.entries.iter() {
entry.execute(just_check);
}
}
}

impl Entry {
fn execute(&self, just_check: bool) {
let manifest_path = self.absolute_path.join("Cargo.toml");
let rendered = self.render(&format!("{}", self.manifest_value));
let mut write = true;
if manifest_path.is_file() {
let existing = fs::read_to_string(&manifest_path).unwrap();
if self.just_ensure_equivalence {
let existing_rendered = self.render(&existing);
if existing_rendered != rendered {
eprintln!(
"error: {} is out of date (note that this is a structural comparison):",
manifest_path.display()
);
eprintln!("{}", format_diff(&rendered, &existing_rendered));
die();
}
} else {
if existing == rendered {
write = false;
} else if just_check {
eprintln!("error: {} is out of date:", manifest_path.display());
eprintln!("{}", format_diff(&rendered, &existing));
die();
}
}
} else if just_check || self.just_ensure_equivalence {
eprintln!("error: {} does not exist", manifest_path.display());
die();
}
if write {
eprintln!("writing {}", manifest_path.display());
fs::write(manifest_path, rendered).unwrap();
}
}

fn render(&self, unformatted_toml: &str) -> String {
let mut cmd = Command::new("toml-normalize");
cmd.stdin(Stdio::piped())
.stdout(Stdio::piped())
.arg("--builtin-policy=cargo-manifest");
for policy in self.format_policy_overrides.iter() {
cmd.arg(format!("--inline-policy={}", policy));
}
let mut child = cmd.spawn().unwrap();
child
.stdin
.take()
.unwrap()
.write_all(unformatted_toml.as_bytes())
.unwrap();
let output = child.wait_with_output().unwrap();
assert!(output.status.success());
let mut s = String::new();
if let Some(frontmatter) = self.frontmatter.as_ref() {
s.push_str(frontmatter);
s.push('\n');
}
s.push_str(str::from_utf8(&output.stdout).unwrap());
s
}
}

fn format_diff(a: &str, b: &str) -> String {
let d = TextDiff::from_lines(a, b);
let mut s = String::new();
for change in d.iter_all_changes() {
let sign = match change.tag() {
ChangeTag::Delete => "-",
ChangeTag::Insert => "+",
ChangeTag::Equal => " ",
};
s.push_str(&format!("{}{}", sign, change))
}
s
}

fn die() -> ! {
process::exit(1)
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ struct Args {
#[arg(long, value_name = "POLICY_FILE")]
policy: Vec<PathBuf>,

#[arg(long, value_name = "POLICY")]
inline_policy: Vec<String>,

#[arg(long)]
builtin_policy: Vec<String>,
}
Expand Down Expand Up @@ -77,6 +80,17 @@ fn main() {
policies.push((policy, index));
}

for (s, index) in args.inline_policy.iter().zip(
matches
.indices_of("inline_policy")
.map(Iterator::collect)
.unwrap_or_else(Vec::new),
) {
let policy = serde_json::from_str(&s)
.unwrap_or_else(|err| panic!("error deserializing policy {}: {:?}", s, err));
policies.push((policy, index));
}

for (name, index) in args.builtin_policy.iter().zip(
matches
.indices_of("builtin_policy")
Expand Down
12 changes: 7 additions & 5 deletions hacking/cargo-manifest-management/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ rec {
workspaceDirFilter = relativePathSegments: lib.head relativePathSegments == "crates";
};

inherit (workspace) generatedManifestsList;
inherit (workspace) blueprint debug;

x = pkgs.callPackage ./x.nix {
inherit generatedManifestsList;
};
prettyJSON = name: value: with pkgs; runCommand name {
nativeBuildInputs = [ jq ];
} ''
jq < ${builtins.toFile name (builtins.toJSON value)} > $out
'';

inherit (x) witness updateScript debug;
blueprintJSON = prettyJSON "blueprint.json" blueprint;
}
18 changes: 16 additions & 2 deletions hacking/cargo-manifest-management/tool.nix
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,20 @@ let
(lib.flip lib.concatMap generatedManifestsList
(manifest: lib.optional (manifest.packageName != null) (lib.nameValuePair manifest.packageName manifest)));

in {
inherit generatedManifestsList;
in rec {
blueprint = generatedManifestsList;

debug = {
byPath = lib.listToAttrs (lib.forEach blueprint (attrs: {
name = attrs.absolutePath;
value = attrs;
}));
byCrateName = lib.listToAttrs
(lib.forEach
(lib.filter (attrs: attrs.manifestValue.package.name or null != null) blueprint)
(attrs: {
name = attrs.manifestValue.package.name;
value = attrs;
}));
};
}
Loading

0 comments on commit 42802ee

Please sign in to comment.