add --overwrite and --overwrite-str

This commit is contained in:
figsoda 2023-01-01 19:08:25 -05:00
parent 834bb2cedc
commit 6dc540c192
6 changed files with 114 additions and 28 deletions

14
Cargo.lock generated
View File

@ -172,12 +172,6 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "indoc"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da2d6f23ffea9d7e76c53eee25dfb67bcd8fde7f1198b0855350698c9f07c780"
[[package]]
name = "io-lifetimes"
version = "1.0.3"
@ -242,9 +236,9 @@ dependencies = [
"clap_complete",
"clap_mangen",
"enum_dispatch",
"indoc",
"itertools",
"owo-colors",
"rustc-hash",
"serde",
"serde_json",
"url",
@ -325,6 +319,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustix"
version = "0.36.6"

View File

@ -14,9 +14,9 @@ categories = ["command-line-utilities"]
[dependencies]
anyhow = "1.0.68"
enum_dispatch = "0.3.9"
indoc = "1.0.8"
itertools = "0.10.5"
owo-colors = { version = "3.5.0", features = ["supports-colors"] }
rustc-hash = "1.1.0"
serde = { version = "1.0.152", features = ["derive"] }
serde_json = "1.0.91"
url = "2.3.1"

View File

@ -38,6 +38,27 @@ pub struct Opts {
#[arg(short, long = "arg", num_args = 2, value_names = ["KEY", "VALUE"])]
pub args: Vec<String>,
/// overwrite arguments in the final output,
/// not taken into consideration when fetching the hash
///
/// Note that nurl does not verify any of the overwrites,
/// for the final output to be valid,
/// the user should not overwrite anything that would change the hash
///
/// examples:
/// {n} --overwrite repo pname
/// {n} --overwrite rev version
#[arg(short, long = "overwrite", num_args = 2, value_names = ["NAME", "EXPR"])]
pub overwrites: Vec<String>,
/// same as --overwrite, but accepts strings instead Nix expressions
///
/// examples:
/// {n} --overwrite-str rev 'v${version}'
/// {n} --overwrite-str meta.homepage https://example.org
#[arg(short = 'O', long = "overwrite-str", num_args = 2, value_names = ["NAME", "STRING"])]
pub overwrites_str: Vec<String>,
/// List all available fetchers
#[arg(short, long, group = "command")]
pub list_fetchers: bool,

View File

@ -20,6 +20,7 @@ pub use sourcehut::FetchFromSourcehut;
use anyhow::Result;
use enum_dispatch::enum_dispatch;
use rustc_hash::FxHashMap;
use url::Url;
use std::io::Write;
@ -32,6 +33,7 @@ pub trait Fetcher {
url: &Url,
rev: String,
args: Vec<(String, String)>,
overwrites: FxHashMap<String, String>,
indent: String,
) -> Result<()>;
fn fetch_json(
@ -40,6 +42,8 @@ pub trait Fetcher {
url: &Url,
rev: String,
args: Vec<(String, String)>,
overwrites: Vec<(String, String)>,
overwrites_str: Vec<(String, String)>,
) -> Result<()>;
}
@ -66,9 +70,10 @@ macro_rules! impl_fetcher {
url: &::url::Url,
rev: String,
args: Vec<(String, String)>,
overwrites: ::rustc_hash::FxHashMap<String, String>,
indent: String,
) -> ::anyhow::Result<()> {
self.fetch_nix_impl(out, url, rev, args, indent)
self.fetch_nix_impl(out, url, rev, args, overwrites, indent)
}
fn fetch_json(
@ -77,8 +82,10 @@ macro_rules! impl_fetcher {
url: &::url::Url,
rev: String,
args: Vec<(String, String)>,
overwrites: Vec<(String, String)>,
overwrites_str: Vec<(String, String)>,
) -> ::anyhow::Result<()> {
self.fetch_json_impl(out, url, rev, args)
self.fetch_json_impl(out, url, rev, args, overwrites, overwrites_str)
}
}
};

View File

@ -8,6 +8,7 @@ mod simple;
use anyhow::{bail, Result};
use clap::{Parser, ValueEnum};
use itertools::Itertools;
use rustc_hash::FxHashMap;
use crate::{
cli::{FetcherFunction, Opts},
@ -100,9 +101,29 @@ fn main() -> Result<()> {
let out = &mut stdout().lock();
let args = opts.args.into_iter().tuples().collect();
if opts.json {
fetcher.fetch_json(out, &opts.url, opts.rev, args)
fetcher.fetch_json(
out,
&opts.url,
opts.rev,
args,
opts.overwrites.into_iter().tuples().collect(),
opts.overwrites_str.into_iter().tuples().collect(),
)
} else {
fetcher.fetch_nix(out, &opts.url, opts.rev, args, " ".repeat(opts.indent))
let mut overwrites: FxHashMap<_, _> = opts.overwrites.into_iter().tuples().collect();
for (key, value) in opts.overwrites_str.into_iter().tuples() {
overwrites.insert(key, format!(r#""{value}""#));
}
fetcher.fetch_nix(
out,
&opts.url,
opts.rev,
args,
overwrites,
" ".repeat(opts.indent),
)
}?;
Ok(())

View File

@ -1,6 +1,6 @@
use anyhow::{Context, Result};
use indoc::writedoc;
use itertools::Itertools;
use rustc_hash::FxHashMap;
use serde_json::json;
use url::Url;
@ -73,27 +73,43 @@ pub trait SimpleFetcher<'a, const N: usize = 2> {
rev: String,
hash: String,
args: Vec<(String, String)>,
overwrites: FxHashMap<String, String>,
indent: String,
) -> Result<()> {
let mut overwrites = overwrites;
writeln!(out, "{} {{", Self::NAME)?;
if let Some(host) = self.host() {
if let Some(host) = overwrites.remove(Self::HOST_KEY) {
writeln!(out, r#"{indent} {} = {host};"#, Self::HOST_KEY)?;
} else if let Some(host) = self.host() {
writeln!(out, r#"{indent} {} = "{host}";"#, Self::HOST_KEY)?;
}
for (key, value) in Self::KEYS.iter().zip(values) {
writeln!(out, r#"{indent} {key} = "{value}";"#)?;
if let Some(value) = overwrites.remove(*key) {
writeln!(out, r#"{indent} {key} = {value};"#)?;
} else {
writeln!(out, r#"{indent} {key} = "{value}";"#)?;
}
}
writedoc!(
out,
r#"
{indent} rev = "{rev}";
{indent} hash = "{hash}";
"#
)?;
if let Some(rev) = overwrites.remove("rev") {
writeln!(out, "{indent} rev = {rev};")?;
} else {
writeln!(out, r#"{indent} rev = "{rev}";"#)?;
}
if let Some(hash) = overwrites.remove("hash") {
writeln!(out, "{indent} hash = {hash};")?;
} else {
writeln!(out, r#"{indent} hash = "{hash}";"#)?;
}
for (key, value) in args {
let value = overwrites.remove(&key).unwrap_or(value);
writeln!(out, "{indent} {key} = {value};")?;
}
for (key, value) in overwrites {
writeln!(out, "{indent} {key} = {value};")?;
}
@ -109,6 +125,8 @@ pub trait SimpleFetcher<'a, const N: usize = 2> {
rev: String,
hash: String,
args: Vec<(String, String)>,
overwrites: Vec<(String, String)>,
overwrites_str: Vec<(String, String)>,
) -> Result<()> {
let mut fetcher_args = json! ({
"rev": rev,
@ -130,6 +148,16 @@ pub trait SimpleFetcher<'a, const N: usize = 2> {
});
}
for (key, value) in overwrites {
fetcher_args[key] = json!({
"type": "nix",
"value": value,
})
}
for (key, value) in overwrites_str {
fetcher_args[key] = json!(value);
}
serde_json::to_writer(
out,
&json!({
@ -149,10 +177,11 @@ pub trait SimpleFodFetcher<'a, const N: usize = 2>: SimpleFetcher<'a, N> {
url: &'a Url,
rev: String,
args: Vec<(String, String)>,
overwrites: FxHashMap<String, String>,
indent: String,
) -> Result<()> {
let (values, hash) = self.fetch_fod(url, &rev, &args)?;
self.write_nix(out, values, rev, hash, args, indent)
self.write_nix(out, values, rev, hash, args, overwrites, indent)
}
fn fetch_json_impl(
@ -161,9 +190,11 @@ pub trait SimpleFodFetcher<'a, const N: usize = 2>: SimpleFetcher<'a, N> {
url: &'a Url,
rev: String,
args: Vec<(String, String)>,
overwrites: Vec<(String, String)>,
overwrites_str: Vec<(String, String)>,
) -> Result<()> {
let (values, hash) = self.fetch_fod(url, &rev, &args)?;
self.write_json(out, values, rev, hash, args)
self.write_json(out, values, rev, hash, args, overwrites, overwrites_str)
}
}
@ -198,6 +229,7 @@ pub trait SimpleFlakeFetcher<'a, const N: usize = 2>: SimpleFetcher<'a, N> {
url: &'a Url,
rev: String,
args: Vec<(String, String)>,
overwrites: FxHashMap<String, String>,
indent: String,
) -> Result<()> {
let (values, hash) = if args.is_empty() {
@ -206,7 +238,7 @@ pub trait SimpleFlakeFetcher<'a, const N: usize = 2>: SimpleFetcher<'a, N> {
self.fetch_fod(url, &rev, &args)?
};
self.write_nix(out, values, rev, hash, args, indent)
self.write_nix(out, values, rev, hash, args, overwrites, indent)
}
fn fetch_json_impl(
@ -215,13 +247,15 @@ pub trait SimpleFlakeFetcher<'a, const N: usize = 2>: SimpleFetcher<'a, N> {
url: &'a Url,
rev: String,
args: Vec<(String, String)>,
overwrites: Vec<(String, String)>,
overwrites_str: Vec<(String, String)>,
) -> Result<()> {
let (values, hash) = if args.is_empty() {
self.fetch(url, &rev)?
} else {
self.fetch_fod(url, &rev, &args)?
};
self.write_json(out, values, rev, hash, args)
self.write_json(out, values, rev, hash, args, overwrites, overwrites_str)
}
}
@ -242,6 +276,7 @@ pub trait SimpleUrlFetcher<'a, const N: usize = 2>: SimpleFetcher<'a, N> {
url: &'a Url,
rev: String,
args: Vec<(String, String)>,
overwrites: FxHashMap<String, String>,
indent: String,
) -> Result<()> {
let (values, hash) = if args.is_empty() {
@ -250,7 +285,7 @@ pub trait SimpleUrlFetcher<'a, const N: usize = 2>: SimpleFetcher<'a, N> {
self.fetch_fod(url, &rev, &args)?
};
self.write_nix(out, values, rev, hash, args, indent)
self.write_nix(out, values, rev, hash, args, overwrites, indent)
}
fn fetch_json_impl(
@ -259,12 +294,14 @@ pub trait SimpleUrlFetcher<'a, const N: usize = 2>: SimpleFetcher<'a, N> {
url: &'a Url,
rev: String,
args: Vec<(String, String)>,
overwrites: Vec<(String, String)>,
overwrites_str: Vec<(String, String)>,
) -> Result<()> {
let (values, hash) = if args.is_empty() {
self.fetch(url, &rev)?
} else {
self.fetch_fod(url, &rev, &args)?
};
self.write_json(out, values, rev, hash, args)
self.write_json(out, values, rev, hash, args, overwrites, overwrites_str)
}
}