switch to git-url to support git urls without a scheme

This commit is contained in:
figsoda 2023-01-23 20:39:21 -05:00
parent b97a16d0e0
commit af7bce854e
14 changed files with 167 additions and 56 deletions

82
Cargo.lock generated
View File

@ -321,6 +321,50 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "git-features"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0019327672cb759f851d1b18fdcc36bb797dc62b925cb93c8c881b54735eb2c2"
dependencies = [
"git-hash",
"libc",
]
[[package]]
name = "git-hash"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1532d82bf830532f8d545c5b7b568e311e3593f16cf7ee9dd0ce03c74b12b99d"
dependencies = [
"hex",
"thiserror",
]
[[package]]
name = "git-path"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e40e68481a06da243d3f4dfd86a4be39c24eefb535017a862e845140dcdb878a"
dependencies = [
"bstr",
"thiserror",
]
[[package]]
name = "git-url"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc9a3df0498c511cf34739eab2692352939b54075c2fc96e8f688d402f3f1250"
dependencies = [
"bstr",
"git-features",
"git-path",
"home",
"thiserror",
"url",
]
[[package]]
name = "glob"
version = "0.3.1"
@ -357,6 +401,21 @@ dependencies = [
"libc",
]
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "home"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408"
dependencies = [
"winapi",
]
[[package]]
name = "humantime"
version = "2.1.0"
@ -536,10 +595,12 @@ version = "0.3.5"
dependencies = [
"anyhow",
"assert_cmd",
"bstr",
"clap",
"clap_complete",
"clap_mangen",
"enum_dispatch",
"git-url",
"itertools",
"nu-glob",
"once_cell",
@ -549,7 +610,6 @@ dependencies = [
"serde_json",
"trycmd",
"ureq",
"url",
]
[[package]]
@ -931,6 +991,26 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8"
[[package]]
name = "thiserror"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tinyvec"
version = "1.6.0"

View File

@ -13,7 +13,9 @@ categories = ["command-line-utilities"]
[dependencies]
anyhow = "1.0.68"
bstr = "1.1.0"
enum_dispatch = "0.3.11"
git-url = "0.13.1"
itertools = "0.10.5"
once_cell = "1.17.0"
owo-colors = { version = "3.5.0", features = ["supports-colors"] }
@ -21,7 +23,6 @@ rustc-hash = "1.1.0"
serde = { version = "1.0.152", features = ["derive"] }
serde_json = "1.0.91"
ureq = { version = "2.6.2", features = ["json"] }
url = "2.3.1"
[dependencies.clap]
version = "4.1.1"
@ -31,7 +32,6 @@ features = ["cargo", "derive", "unicode", "wrap_help"]
clap = { version = "4.1.1", features = ["derive"] }
clap_complete = "4.1.0"
clap_mangen = "0.2.7"
url = "2.3.1"
[dev-dependencies]
assert_cmd = "2.0.8"

View File

@ -1,5 +1,4 @@
use clap::{Parser, ValueEnum};
use url::Url;
/// Generate Nix fetcher calls from repository URLs
/// https://github.com/nix-community/nurl
@ -9,10 +8,10 @@ pub struct Opts {
/// URL to the repository to be fetched
#[arg(
required_unless_present = "command",
default_value = "x:", // placeholder value, will not be accessed
default_value_t, // placeholder, will not be accessed
hide_default_value = true
)]
pub url: Url,
pub url: String,
/// The revision or reference to be fetched
pub rev: Option<String>,

View File

@ -1,11 +1,10 @@
use anyhow::{bail, Context, Result};
use rustc_hash::FxHashMap;
use serde_json::json;
use url::Url;
use std::io::Write;
use crate::fetcher::Fetcher;
use crate::{fetcher::Fetcher, Url};
pub struct BuiltinsFetchGit;

View File

@ -1,8 +1,7 @@
use url::Url;
use crate::{
impl_fetcher,
simple::{SimpleFetcher, SimpleUrlFetcher},
Url,
};
pub struct FetchCrate(pub bool);
@ -14,7 +13,7 @@ impl<'a> SimpleFetcher<'a, 1> for FetchCrate {
const REV_KEY: &'static str = "version";
fn get_values(&self, url: &'a Url) -> Option<[&'a str; 1]> {
let mut xs = url.path_segments()?;
let mut xs = url.path_segments();
Some([if self.0 {
xs.nth(1)?
} else {

View File

@ -1,9 +1,7 @@
use url::Url;
use crate::{
impl_fetcher,
simple::{SimpleFetcher, SimpleFlakeFetcher},
GitScheme,
GitScheme, Url,
};
pub struct Fetchgit(pub GitScheme);
@ -15,9 +13,9 @@ impl<'a> SimpleFetcher<'a, 1> for Fetchgit {
fn get_values(&self, url: &'a Url) -> Option<[&'a str; 1]> {
Some([if matches!(self.0, GitScheme::Plus) {
url.as_ref().strip_prefix("git+")?
url.as_str().strip_prefix("git+")?
} else {
url.as_ref()
url.as_str()
}])
}
}

View File

@ -1,8 +1,7 @@
use url::Url;
use crate::{
impl_fetcher,
simple::{SimpleFetcher, SimpleUrlFetcher},
Url,
};
pub struct FetchFromGitiles;
@ -12,8 +11,8 @@ impl<'a> SimpleFetcher<'a, 1> for FetchFromGitiles {
const KEYS: [&'static str; 1] = ["url"];
const NAME: &'static str = "fetchFromGitiles";
fn get_values(&self, url: &'a Url) -> Option<[&'a str; 1]> {
Some([url.as_ref()])
fn get_values(&self, url: &'a Url<'a>) -> Option<[&'a str; 1]> {
Some([url.as_str()])
}
}

View File

@ -1,13 +1,13 @@
use anyhow::{Context, Result};
use once_cell::unsync::OnceCell;
use serde::Deserialize;
use url::Url;
use std::fmt::Write;
use crate::{
impl_fetcher,
simple::{SimpleFetcher, SimpleFlakeFetcher},
Url,
};
pub struct FetchFromGitLab<'a> {
@ -42,8 +42,8 @@ impl<'a> SimpleFetcher<'a, 2> for FetchFromGitLab<'a> {
self.group.get().copied()
}
fn get_values(&self, url: &'a Url) -> Option<[&'a str; 2]> {
let mut xs = url.path_segments()?;
fn get_values(&self, url: &'a Url<'a>) -> Option<[&'a str; 2]> {
let mut xs = url.path_segments();
let x = xs.next()?;
let y = xs.next()?;
Some(match xs.next() {

View File

@ -1,8 +1,7 @@
use url::Url;
use crate::{
impl_fetcher,
simple::{SimpleFetcher, SimpleUrlFetcher},
Url,
};
pub struct FetchHex;
@ -14,8 +13,8 @@ impl<'a> SimpleFetcher<'a, 1> for FetchHex {
const NAME: &'static str = "fetchHex";
const REV_KEY: &'static str = "version";
fn get_values(&self, url: &'a Url) -> Option<[&'a str; 1]> {
Some([url.path_segments()?.nth(1)?])
fn get_values(&self, url: &'a Url<'a>) -> Option<[&'a str; 1]> {
Some([url.path_segments().nth(1)?])
}
}

View File

@ -1,8 +1,7 @@
use url::Url;
use crate::{
impl_fetcher,
simple::{SimpleFetcher, SimpleFlakeFetcher},
Url,
};
pub struct Fetchhg(pub bool);
@ -13,11 +12,11 @@ impl<'a> SimpleFetcher<'a, 1> for Fetchhg {
const KEYS: [&'static str; 1] = ["url"];
const NAME: &'static str = "fetchhg";
fn get_values(&self, url: &'a Url) -> Option<[&'a str; 1]> {
fn get_values(&self, url: &'a Url<'a>) -> Option<[&'a str; 1]> {
Some([if self.0 {
url.as_ref().strip_prefix("hg+")?
url.as_str().strip_prefix("hg+")?
} else {
url.as_ref()
url.as_str()
}])
}
}

View File

@ -26,10 +26,10 @@ pub use repo_or_cz::FetchFromRepoOrCz;
pub use sourcehut::FetchFromSourcehut;
pub use svn::Fetchsvn;
use crate::Url;
use anyhow::Result;
use enum_dispatch::enum_dispatch;
use rustc_hash::FxHashMap;
use url::Url;
use std::io::Write;
@ -49,7 +49,7 @@ pub trait Fetcher<'a> {
fn fetch_hash(
&'a self,
out: &mut impl ::std::io::Write,
url: &'a Url,
url: &'a Url<'a>,
rev: Option<String>,
args: Vec<(String, String)>,
args_str: Vec<(String, String)>,
@ -58,7 +58,7 @@ pub trait Fetcher<'a> {
fn fetch_json(
&'a self,
out: &mut impl Write,
url: &'a Url,
url: &'a Url<'a>,
rev: Option<String>,
args: Vec<(String, String)>,
args_str: Vec<(String, String)>,
@ -69,7 +69,7 @@ pub trait Fetcher<'a> {
fn to_json(
&'a self,
out: &mut impl ::std::io::Write,
url: &'a ::url::Url,
url: &'a Url<'a>,
rev: Option<String>,
) -> ::anyhow::Result<()>;
}
@ -98,7 +98,7 @@ macro_rules! impl_fetcher {
fn fetch_nix(
&'a self,
out: &mut impl ::std::io::Write,
url: &'a ::url::Url,
url: &'a $crate::Url<'a>,
rev: Option<String>,
args: Vec<(String, String)>,
args_str: Vec<(String, String)>,
@ -124,7 +124,7 @@ macro_rules! impl_fetcher {
fn fetch_hash(
&'a self,
out: &mut impl ::std::io::Write,
url: &'a ::url::Url,
url: &'a $crate::Url<'a>,
rev: Option<String>,
args: Vec<(String, String)>,
args_str: Vec<(String, String)>,
@ -149,7 +149,7 @@ macro_rules! impl_fetcher {
fn fetch_json(
&'a self,
out: &mut impl ::std::io::Write,
url: &'a ::url::Url,
url: &'a $crate::Url<'a>,
rev: Option<String>,
args: Vec<(String, String)>,
args_str: Vec<(String, String)>,
@ -184,7 +184,7 @@ macro_rules! impl_fetcher {
fn to_json(
&'a self,
out: &mut impl ::std::io::Write,
url: &'a ::url::Url,
url: &'a $crate::Url<'a>,
rev: Option<String>,
) -> ::anyhow::Result<()> {
use anyhow::Context;

View File

@ -1,8 +1,7 @@
use url::Url;
use crate::{
impl_fetcher,
simple::{SimpleFetcher, SimpleFodFetcher},
Url,
};
pub struct Fetchsvn;
@ -14,7 +13,7 @@ impl<'a> SimpleFetcher<'a, 1> for Fetchsvn {
const NAME: &'static str = "fetchsvn";
fn get_values(&self, url: &'a Url) -> Option<[&'a str; 1]> {
Some([url.as_ref()])
Some([url.as_str()])
}
}

View File

@ -6,7 +6,9 @@ mod prefetch;
mod simple;
use anyhow::{bail, Result};
use bstr::ByteSlice;
use clap::{Parser, ValueEnum};
use git_url::Scheme;
use itertools::Itertools;
use rustc_hash::FxHashMap;
@ -19,7 +21,32 @@ use crate::{
},
};
use std::io::{stdout, Write};
use std::{
fmt::{self, Display, Formatter},
io::{stdout, Write},
str::Split,
};
pub struct Url<'a> {
url: &'a str,
path: &'a str,
}
impl<'a> Url<'a> {
fn as_str(&'a self) -> &str {
self.url
}
fn path_segments(&self) -> Split<char> {
self.path.split('/')
}
}
impl<'a> Display for Url<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
pub enum GitScheme {
Yes,
@ -56,7 +83,9 @@ fn main() -> Result<()> {
return Ok(());
}
let fetcher: FetcherDispatch = match (opts.fetcher, opts.url.host_str(), opts.url.scheme()) {
let url: git_url::Url = opts.url.try_into()?;
let fetcher: FetcherDispatch = match (opts.fetcher, url.host(), &url.scheme) {
(Some(FetcherFunction::BuiltinsFetchGit), ..) => BuiltinsFetchGit.into(),
(None | Some(FetcherFunction::FetchCrate), Some("crates.io"), _) => FetchCrate(true).into(),
@ -136,32 +165,43 @@ fn main() -> Result<()> {
bail!("fetchHex only supports hex.pm");
}
(None | Some(FetcherFunction::Fetchgit), _, "git") => Fetchgit(GitScheme::Yes).into(),
(None | Some(FetcherFunction::Fetchgit), _, scheme) if scheme.starts_with("git+") => {
(None | Some(FetcherFunction::Fetchgit), _, Scheme::Git) => Fetchgit(GitScheme::Yes).into(),
(None | Some(FetcherFunction::Fetchgit), _, Scheme::Ext(scheme))
if scheme.starts_with("git+") =>
{
Fetchgit(GitScheme::Plus).into()
}
(Some(FetcherFunction::Fetchgit), ..) => Fetchgit(GitScheme::No).into(),
(None | Some(FetcherFunction::Fetchhg), _, scheme) if scheme.starts_with("hg+") => {
(None | Some(FetcherFunction::Fetchhg), _, Scheme::Ext(scheme))
if scheme.starts_with("hg+") =>
{
Fetchhg(true).into()
}
(Some(FetcherFunction::Fetchhg), ..) => Fetchhg(false).into(),
(None, _, "svn") => Fetchsvn.into(),
(None, _, Scheme::Ext(scheme)) if scheme == "svn" => Fetchsvn.into(),
(Some(FetcherFunction::Fetchsvn), ..) => Fetchsvn.into(),
(None, ..) => Fetchgit(GitScheme::No).into(),
};
let url_bstring = url.to_bstring();
let path = url.path.to_str()?;
let url = Url {
url: url_bstring.to_str()?,
path: path.strip_prefix('/').unwrap_or(path),
};
let out = &mut stdout().lock();
let args = opts.args.into_iter().tuples().collect();
let args_str = opts.args_str.into_iter().tuples().collect();
if opts.hash {
fetcher.fetch_hash(out, &opts.url, opts.rev, args, args_str)
fetcher.fetch_hash(out, &url, opts.rev, args, args_str)
} else if opts.json {
fetcher.fetch_json(
out,
&opts.url,
&url,
opts.rev,
args,
args_str,
@ -169,7 +209,7 @@ fn main() -> Result<()> {
opts.overwrites_str.into_iter().tuples().collect(),
)
} else if opts.parse {
fetcher.to_json(out, &opts.url, opts.rev)
fetcher.to_json(out, &url, opts.rev)
} else {
let mut overwrites: FxHashMap<_, _> = opts.overwrites.into_iter().tuples().collect();
@ -179,7 +219,7 @@ fn main() -> Result<()> {
fetcher.fetch_nix(
out,
&opts.url,
&url,
opts.rev,
args,
args_str,

View File

@ -1,8 +1,8 @@
use crate::Url;
use anyhow::{bail, Result};
use itertools::Itertools;
use rustc_hash::FxHashMap;
use serde_json::{json, Value};
use url::Url;
use std::{fmt::Write as _, io::Write};
@ -23,9 +23,9 @@ pub trait SimpleFetcher<'a, const N: usize> {
None
}
fn get_values(&self, url: &'a Url) -> Option<[&'a str; N]> {
fn get_values(&self, url: &'a Url<'a>) -> Option<[&'a str; N]> {
let mut xs: [_; N] = url
.path_segments()?
.path_segments()
.chunks(N)
.into_iter()
.next()?