const generics

This commit is contained in:
figsoda 2022-12-31 17:29:20 -05:00
parent b495a23e6c
commit 08245d0837
9 changed files with 108 additions and 86 deletions

View File

@ -25,7 +25,7 @@ pub trait CvsFetcher {
fn write_nix(
&self,
out: &mut impl Write,
url: Url,
url: &Url,
rev: String,
hash: String,
args: Vec<(String, String)>,
@ -54,7 +54,7 @@ pub trait CvsFetcher {
fn write_json(
&self,
out: &mut impl Write,
url: Url,
url: &Url,
rev: String,
hash: String,
args: Vec<(String, String)>,
@ -89,23 +89,23 @@ pub trait CvsFodFetcher: CvsFetcher {
fn fetch_nix_impl(
&self,
out: &mut impl Write,
url: Url,
url: &Url,
rev: String,
args: Vec<(String, String)>,
indent: String,
) -> Result<()> {
let hash = self.fetch_fod(&url, &rev, &args)?;
let hash = self.fetch_fod(url, &rev, &args)?;
self.write_nix(out, url, rev, hash, args, indent)
}
fn fetch_json_impl(
&self,
out: &mut impl Write,
url: Url,
url: &Url,
rev: String,
args: Vec<(String, String)>,
) -> Result<()> {
let hash = self.fetch_fod(&url, &rev, &args)?;
let hash = self.fetch_fod(url, &rev, &args)?;
self.write_json(out, url, rev, hash, args)
}
}
@ -124,15 +124,15 @@ pub trait CvsFlakeFetcher: CvsFetcher {
fn fetch_nix_impl(
&self,
out: &mut impl Write,
url: Url,
url: &Url,
rev: String,
args: Vec<(String, String)>,
indent: String,
) -> Result<()> {
let hash = if args.is_empty() {
self.fetch(&url, &rev)?
self.fetch(url, &rev)?
} else {
self.fetch_fod(&url, &rev, &args)?
self.fetch_fod(url, &rev, &args)?
};
self.write_nix(out, url, rev, hash, args, indent)
}
@ -140,14 +140,14 @@ pub trait CvsFlakeFetcher: CvsFetcher {
fn fetch_json_impl(
&self,
out: &mut impl Write,
url: Url,
url: &Url,
rev: String,
args: Vec<(String, String)>,
) -> Result<()> {
let hash = if args.is_empty() {
self.fetch(&url, &rev)?
self.fetch(url, &rev)?
} else {
self.fetch_fod(&url, &rev, &args)?
self.fetch_fod(url, &rev, &args)?
};
self.write_json(out, url, rev, hash, args)
}

View File

@ -1,5 +1,6 @@
use anyhow::{Context, Result};
use indoc::writedoc;
use itertools::Itertools;
use serde_json::json;
use url::Url;
@ -7,54 +8,66 @@ use std::{fmt::Write as _, io::Write};
use crate::common::{flake_prefetch, fod_prefetch, url_prefetch};
pub trait SimpleFetcher<'a> {
pub trait SimpleFetcher<'a, const N: usize = 2> {
const HOST_KEY: &'static str = "domain";
const KEYS: [&'static str; N];
const NAME: &'static str;
fn host(&'a self) -> Option<&'a str>;
fn get_repo(&self, url: &Url) -> Option<(String, String)> {
let mut xs = url.path_segments()?;
let owner = xs.next()?;
let repo = xs.next()?;
Some((
owner.into(),
repo.strip_suffix(".git").unwrap_or(repo).into(),
))
fn get_values(&self, url: &'a Url) -> Option<[&'a str; N]> {
let mut xs: [_; N] = url
.path_segments()?
.chunks(N)
.into_iter()
.next()?
.collect::<Vec<_>>()
.try_into()
.ok()?;
xs[N - 1] = xs[N - 1].strip_suffix(".git").unwrap_or(xs[N - 1]);
Some(xs)
}
fn fetch_fod(
&'a self,
url: &Url,
url: &'a Url,
rev: &str,
args: &[(String, String)],
) -> Result<(String, String, String)> {
let (owner, repo) = self
.get_repo(url)
) -> Result<([&str; N], String)> {
let values = self
.get_values(url)
.with_context(|| format!("failed to parse {url}"))?;
let mut expr = format!(
r#"(import <nixpkgs> {{}}).{}{{owner="{owner}";repo="{repo}";rev="{rev}";hash="sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";"#,
Self::NAME
);
let mut expr = format!(r#"(import <nixpkgs> {{}}).{}{{"#, Self::NAME);
if let Some(host) = self.host() {
write!(expr, r#"{}="{host}""#, Self::HOST_KEY)?;
}
for (key, value) in Self::KEYS.iter().zip(values) {
write!(expr, r#"{key}="{value}";"#)?;
}
write!(
expr,
r#"rev="{rev}";hash="sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";"#
)?;
for (key, value) in args {
write!(expr, "{key}={value};")?;
}
expr.push('}');
let hash = fod_prefetch(expr)?;
Ok((owner, repo, hash))
Ok((values, hash))
}
fn write_nix(
&'a self,
out: &mut impl Write,
owner: String,
repo: String,
values: [&str; N],
rev: String,
hash: String,
args: Vec<(String, String)>,
@ -66,11 +79,13 @@ pub trait SimpleFetcher<'a> {
writeln!(out, r#"{indent} {} = "{host}";"#, Self::HOST_KEY)?;
}
for (key, value) in Self::KEYS.iter().zip(values) {
writeln!(out, r#"{indent} {key} = "{value}";"#)?;
}
writedoc!(
out,
r#"
{indent} owner = "{owner}";
{indent} repo = "{repo}";
{indent} rev = "{rev}";
{indent} hash = "{hash}";
"#
@ -88,15 +103,12 @@ pub trait SimpleFetcher<'a> {
fn write_json(
&'a self,
out: &mut impl Write,
owner: String,
repo: String,
values: [&str; N],
rev: String,
hash: String,
args: Vec<(String, String)>,
) -> Result<()> {
let mut fetcher_args = json! ({
"owner": owner,
"repo": repo,
"rev": rev,
"hash": hash,
});
@ -105,7 +117,11 @@ pub trait SimpleFetcher<'a> {
fetcher_args["host"] = json!(host);
}
for (key, value) in args {
for (key, value) in Self::KEYS
.into_iter()
.zip(values)
.chain(args.iter().map(|(x, y)| (x.as_str(), y.as_str())))
{
fetcher_args[key] = json!(value);
}
@ -121,37 +137,38 @@ pub trait SimpleFetcher<'a> {
}
}
pub trait SimpleFodFetcher<'a>: SimpleFetcher<'a> {
pub trait SimpleFodFetcher<'a, const N: usize = 2>: SimpleFetcher<'a, N> {
fn fetch_nix_impl(
&'a self,
out: &mut impl Write,
url: Url,
url: &'a Url,
rev: String,
args: Vec<(String, String)>,
indent: String,
) -> Result<()> {
let (owner, repo, hash) = self.fetch_fod(&url, &rev, &args)?;
self.write_nix(out, owner, repo, rev, hash, args, indent)
let (values, hash) = self.fetch_fod(url, &rev, &args)?;
self.write_nix(out, values, rev, hash, args, indent)
}
fn fetch_json_impl(
&'a self,
out: &mut impl Write,
url: Url,
url: &'a Url,
rev: String,
args: Vec<(String, String)>,
) -> Result<()> {
let (owner, repo, hash) = self.fetch_fod(&url, &rev, &args)?;
self.write_json(out, owner, repo, rev, hash, args)
let (values, hash) = self.fetch_fod(url, &rev, &args)?;
self.write_json(out, values, rev, hash, args)
}
}
pub trait SimpleFlakeFetcher<'a>: SimpleFetcher<'a> {
pub trait SimpleFlakeFetcher<'a>: SimpleFetcher<'a, 2> {
const KEYS: [&'static str; 2] = ["owner", "repo"];
const FLAKE_TYPE: &'static str;
fn fetch(&'a self, url: &Url, rev: &str) -> Result<(String, String, String)> {
let (owner, repo) = self
.get_repo(url)
fn fetch(&'a self, url: &'a Url, rev: &str) -> Result<([&str; 2], String)> {
let [owner, repo] = self
.get_values(url)
.with_context(|| format!("failed to parse {url} as a {} url", Self::FLAKE_TYPE))?;
let hash = flake_prefetch(if let Some(host) = self.host() {
@ -160,82 +177,82 @@ pub trait SimpleFlakeFetcher<'a>: SimpleFetcher<'a> {
format!("{}:{owner}/{repo}/{rev}", Self::FLAKE_TYPE)
})?;
Ok((owner, repo, hash))
Ok(([owner, repo], hash))
}
fn fetch_nix_impl(
&'a self,
out: &mut impl Write,
url: Url,
url: &'a Url,
rev: String,
args: Vec<(String, String)>,
indent: String,
) -> Result<()> {
let (owner, repo, hash) = if args.is_empty() {
self.fetch(&url, &rev)?
let (values, hash) = if args.is_empty() {
self.fetch(url, &rev)?
} else {
self.fetch_fod(&url, &rev, &args)?
self.fetch_fod(url, &rev, &args)?
};
self.write_nix(out, owner, repo, rev, hash, args, indent)
self.write_nix(out, values, rev, hash, args, indent)
}
fn fetch_json_impl(
&'a self,
out: &mut impl Write,
url: Url,
url: &'a Url,
rev: String,
args: Vec<(String, String)>,
) -> Result<()> {
let (owner, repo, hash) = if args.is_empty() {
self.fetch(&url, &rev)?
let (values, hash) = if args.is_empty() {
self.fetch(url, &rev)?
} else {
self.fetch_fod(&url, &rev, &args)?
self.fetch_fod(url, &rev, &args)?
};
self.write_json(out, owner, repo, rev, hash, args)
self.write_json(out, values, rev, hash, args)
}
}
pub trait SimpleUrlFetcher<'a>: SimpleFetcher<'a> {
fn get_url(&self, owner: &str, repo: &str, rev: &str) -> String;
pub trait SimpleUrlFetcher<'a, const N: usize = 2>: SimpleFetcher<'a, N> {
fn get_url(&self, values: [&str; N], rev: &str) -> String;
fn fetch(&'a self, url: &Url, rev: &str) -> Result<(String, String, String)> {
let (owner, repo) = self
.get_repo(url)
fn fetch(&'a self, url: &'a Url, rev: &str) -> Result<([&str; N], String)> {
let values = self
.get_values(url)
.with_context(|| format!("failed to parse {url}"))?;
let hash = url_prefetch(self.get_url(&owner, &repo, rev))?;
Ok((owner, repo, hash))
let hash = url_prefetch(self.get_url(values, rev))?;
Ok((values, hash))
}
fn fetch_nix_impl(
&'a self,
out: &mut impl Write,
url: Url,
url: &'a Url,
rev: String,
args: Vec<(String, String)>,
indent: String,
) -> Result<()> {
let (owner, repo, hash) = if args.is_empty() {
self.fetch(&url, &rev)?
let (values, hash) = if args.is_empty() {
self.fetch(url, &rev)?
} else {
self.fetch_fod(&url, &rev, &args)?
self.fetch_fod(url, &rev, &args)?
};
self.write_nix(out, owner, repo, rev, hash, args, indent)
self.write_nix(out, values, rev, hash, args, indent)
}
fn fetch_json_impl(
&'a self,
out: &mut impl Write,
url: Url,
url: &'a Url,
rev: String,
args: Vec<(String, String)>,
) -> Result<()> {
let (owner, repo, hash) = if args.is_empty() {
self.fetch(&url, &rev)?
let (values, hash) = if args.is_empty() {
self.fetch(url, &rev)?
} else {
self.fetch_fod(&url, &rev, &args)?
self.fetch_fod(url, &rev, &args)?
};
self.write_json(out, owner, repo, rev, hash, args)
self.write_json(out, values, rev, hash, args)
}
}

View File

@ -7,6 +7,7 @@ pub struct FetchFromBitBucket;
impl_fetcher!(FetchFromBitBucket);
impl<'a> SimpleFetcher<'a> for FetchFromBitBucket {
const KEYS: [&'static str; 2] = ["owner", "repo"];
const NAME: &'static str = "fetchFromBitBucket";
fn host(&'a self) -> Option<&'a str> {
@ -15,7 +16,7 @@ impl<'a> SimpleFetcher<'a> for FetchFromBitBucket {
}
impl<'a> SimpleUrlFetcher<'a> for FetchFromBitBucket {
fn get_url(&self, owner: &str, repo: &str, rev: &str) -> String {
fn get_url(&self, [owner, repo]: [&str; 2], rev: &str) -> String {
format!("https://bitbucket.org/{owner}/{repo}/get/{rev}.tar.gz")
}
}

View File

@ -7,6 +7,7 @@ pub struct FetchFromGitea(pub String);
impl_fetcher!(FetchFromGitea);
impl<'a> SimpleFetcher<'a> for FetchFromGitea {
const KEYS: [&'static str; 2] = ["owner", "repo"];
const NAME: &'static str = "fetchFromGitea";
fn host(&'a self) -> Option<&'a str> {
@ -15,7 +16,7 @@ impl<'a> SimpleFetcher<'a> for FetchFromGitea {
}
impl<'a> SimpleUrlFetcher<'a> for FetchFromGitea {
fn get_url(&self, owner: &str, repo: &str, rev: &str) -> String {
fn get_url(&self, [owner, repo]: [&str; 2], rev: &str) -> String {
format!("https://{}/{owner}/{repo}/archive/{rev}.tar.gz", self.0)
}
}

View File

@ -8,6 +8,7 @@ impl_fetcher!(FetchFromGitHub);
impl<'a> SimpleFetcher<'a> for FetchFromGitHub {
const HOST_KEY: &'static str = "githubBase";
const KEYS: [&'static str; 2] = ["owner", "repo"];
const NAME: &'static str = "fetchFromGitHub";
fn host(&'a self) -> Option<&'a str> {

View File

@ -7,6 +7,7 @@ pub struct FetchFromGitLab(pub Option<String>);
impl_fetcher!(FetchFromGitLab);
impl<'a> SimpleFetcher<'a> for FetchFromGitLab {
const KEYS: [&'static str; 2] = ["owner", "repo"];
const NAME: &'static str = "fetchFromGitLab";
fn host(&'a self) -> Option<&'a str> {

View File

@ -25,7 +25,7 @@ pub trait Fetcher {
fn fetch_nix(
&self,
out: &mut impl Write,
url: Url,
url: &Url,
rev: String,
args: Vec<(String, String)>,
indent: String,
@ -33,7 +33,7 @@ pub trait Fetcher {
fn fetch_json(
&self,
out: &mut impl Write,
url: Url,
url: &Url,
rev: String,
args: Vec<(String, String)>,
) -> Result<()>;
@ -57,7 +57,7 @@ macro_rules! impl_fetcher {
fn fetch_nix(
&self,
out: &mut impl ::std::io::Write,
url: ::url::Url,
url: &::url::Url,
rev: String,
args: Vec<(String, String)>,
indent: String,
@ -68,7 +68,7 @@ macro_rules! impl_fetcher {
fn fetch_json(
&self,
out: &mut impl ::std::io::Write,
url: ::url::Url,
url: &::url::Url,
rev: String,
args: Vec<(String, String)>,
) -> ::anyhow::Result<()> {

View File

@ -7,6 +7,7 @@ pub struct FetchFromSourcehut(pub Option<String>);
impl_fetcher!(FetchFromSourcehut);
impl<'a> SimpleFetcher<'a> for FetchFromSourcehut {
const KEYS: [&'static str; 2] = ["owner", "repo"];
const NAME: &'static str = "fetchFromSourcehut";
fn host(&'a self) -> Option<&'a str> {

View File

@ -95,9 +95,9 @@ 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)
} else {
fetcher.fetch_nix(out, opts.url, opts.rev, args, " ".repeat(opts.indent))
fetcher.fetch_nix(out, &opts.url, opts.rev, args, " ".repeat(opts.indent))
}?;
Ok(())