mirror of
https://github.com/nix-community/nurl.git
synced 2024-08-16 21:41:20 +03:00
enum dispatch
This commit is contained in:
parent
6fc0d29d5e
commit
2f2248c9ff
13
Cargo.lock
generated
13
Cargo.lock
generated
@ -60,6 +60,18 @@ dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum_dispatch"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1693044dcf452888dd3a6a6a0dab67f0652094e3920dfe029a54d2f37d9b7394"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.2.8"
|
||||
@ -167,6 +179,7 @@ version = "0.1.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"enum_dispatch",
|
||||
"indoc",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -13,6 +13,7 @@ categories = ["command-line-utilities"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.68"
|
||||
enum_dispatch = "0.3.9"
|
||||
indoc = "1.0.8"
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
serde_json = "1.0.91"
|
||||
|
@ -14,7 +14,7 @@ pub struct Opts {
|
||||
|
||||
/// specify the fetcher function instead of inferring from the URL
|
||||
#[arg(short, long)]
|
||||
pub fetcher: Option<Fetcher>,
|
||||
pub fetcher: Option<FetcherFunction>,
|
||||
|
||||
/// extra indentation (in number of spaces)
|
||||
#[arg(short, long, default_value_t = 0)]
|
||||
@ -23,7 +23,7 @@ pub struct Opts {
|
||||
|
||||
#[derive(Clone, Debug, ValueEnum)]
|
||||
#[clap(rename_all = "camelCase")]
|
||||
pub enum Fetcher {
|
||||
pub enum FetcherFunction {
|
||||
FetchFromGitHub,
|
||||
FetchFromGitLab,
|
||||
FetchFromSourcehut,
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::fetcher::UrlFlakeFetcher;
|
||||
use crate::{fetcher::UrlFlakeFetcher, impl_fetcher};
|
||||
|
||||
pub struct Fetchgit;
|
||||
impl_fetcher!(Fetchgit);
|
||||
|
||||
impl UrlFlakeFetcher for Fetchgit {
|
||||
const FLAKE_TYPE: &'static str = "git";
|
||||
|
@ -1,12 +1,13 @@
|
||||
use crate::fetcher::SimpleFlakeFetcher;
|
||||
use crate::{fetcher::SimpleFlakeFetcher, impl_fetcher};
|
||||
|
||||
pub struct FetchFromGitHub<'a>(pub Option<&'a str>);
|
||||
pub struct FetchFromGitHub(pub Option<String>);
|
||||
impl_fetcher!(FetchFromGitHub);
|
||||
|
||||
impl<'a> SimpleFlakeFetcher<'a> for FetchFromGitHub<'a> {
|
||||
impl<'a> SimpleFlakeFetcher<'a> for FetchFromGitHub {
|
||||
const FLAKE_TYPE: &'static str = "github";
|
||||
const NAME: &'static str = "fetchFromGitHub";
|
||||
|
||||
fn host(&self) -> Option<&'a str> {
|
||||
self.0
|
||||
fn host(&'a self) -> &'a Option<String> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
use crate::fetcher::SimpleFlakeFetcher;
|
||||
use crate::{fetcher::SimpleFlakeFetcher, impl_fetcher};
|
||||
|
||||
pub struct FetchFromGitLab<'a>(pub Option<&'a str>);
|
||||
pub struct FetchFromGitLab(pub Option<String>);
|
||||
impl_fetcher!(FetchFromGitLab);
|
||||
|
||||
impl<'a> SimpleFlakeFetcher<'a> for FetchFromGitLab<'a> {
|
||||
impl<'a> SimpleFlakeFetcher<'a> for FetchFromGitLab {
|
||||
const FLAKE_TYPE: &'static str = "gitlab";
|
||||
const NAME: &'static str = "fetchFromGitLab";
|
||||
|
||||
fn host(&self) -> Option<&'a str> {
|
||||
self.0
|
||||
fn host(&'a self) -> &'a Option<String> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::fetcher::UrlFlakeFetcher;
|
||||
use crate::{fetcher::UrlFlakeFetcher, impl_fetcher};
|
||||
|
||||
pub struct Fetchhg;
|
||||
impl_fetcher!(Fetchhg);
|
||||
|
||||
impl UrlFlakeFetcher for Fetchhg {
|
||||
const FLAKE_TYPE: &'static str = "hg";
|
||||
|
@ -4,6 +4,7 @@ mod gitlab;
|
||||
mod hg;
|
||||
mod sourcehut;
|
||||
|
||||
use enum_dispatch::enum_dispatch;
|
||||
pub use git::Fetchgit;
|
||||
pub use github::FetchFromGitHub;
|
||||
pub use gitlab::FetchFromGitLab;
|
||||
@ -20,47 +21,42 @@ use std::{
|
||||
process::{Command, Output, Stdio},
|
||||
};
|
||||
|
||||
#[enum_dispatch]
|
||||
pub trait Fetcher {
|
||||
fn fetch_nix(&self, out: &mut impl Write, url: &Url, rev: String, indent: &str) -> Result<()>;
|
||||
}
|
||||
|
||||
trait GetStdout {
|
||||
fn get_stdout(&mut self) -> Result<Vec<u8>>;
|
||||
#[enum_dispatch(Fetcher)]
|
||||
pub enum FetcherDispatch {
|
||||
FetchFromGitHub(FetchFromGitHub),
|
||||
FetchFromGitLab(FetchFromGitLab),
|
||||
FetchFromSourcehut(FetchFromSourcehut),
|
||||
Fetchgit(Fetchgit),
|
||||
Fetchhg(Fetchhg),
|
||||
}
|
||||
|
||||
impl GetStdout for Command {
|
||||
fn get_stdout(&mut self) -> Result<Vec<u8>> {
|
||||
let Output { stdout, status, .. } = self.stderr(Stdio::inherit()).output()?;
|
||||
if !status.success() {
|
||||
bail!("command exited with exit code {}", status);
|
||||
#[macro_export]
|
||||
macro_rules! impl_fetcher {
|
||||
($t:ident $($tt:tt)*) => {
|
||||
impl $($tt)* $crate::fetcher::Fetcher for $t $($tt)* {
|
||||
fn fetch_nix(
|
||||
&self,
|
||||
out: &mut impl ::std::io::Write,
|
||||
url: &::url::Url,
|
||||
rev: String,
|
||||
indent: &str,
|
||||
) -> ::anyhow::Result<()> {
|
||||
self.fetch_nix_imp(out, url, rev, indent)
|
||||
}
|
||||
}
|
||||
Ok(stdout)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flake_prefetch(flake_ref: String) -> Result<String> {
|
||||
#[derive(Deserialize)]
|
||||
struct PrefetchOutput {
|
||||
hash: String,
|
||||
}
|
||||
|
||||
eprintln!("$ nix flake prefetch --json {flake_ref}");
|
||||
Ok(serde_json::from_slice::<PrefetchOutput>(
|
||||
&Command::new("nix")
|
||||
.arg("flake")
|
||||
.arg("prefetch")
|
||||
.arg("--json")
|
||||
.arg(flake_ref)
|
||||
.get_stdout()?,
|
||||
)?
|
||||
.hash)
|
||||
};
|
||||
}
|
||||
|
||||
pub trait SimpleFlakeFetcher<'a> {
|
||||
const FLAKE_TYPE: &'static str;
|
||||
const NAME: &'static str;
|
||||
|
||||
fn host(&self) -> Option<&'a str>;
|
||||
fn host(&'a self) -> &'a Option<String>;
|
||||
|
||||
fn get_repo(&self, url: &Url) -> Option<(String, String)> {
|
||||
let mut xs = url.path_segments()?;
|
||||
@ -72,7 +68,13 @@ pub trait SimpleFlakeFetcher<'a> {
|
||||
))
|
||||
}
|
||||
|
||||
fn fetch_nix(&self, out: &mut impl Write, url: &Url, rev: String, indent: &str) -> Result<()> {
|
||||
fn fetch_nix_imp(
|
||||
&'a self,
|
||||
out: &mut impl Write,
|
||||
url: &Url,
|
||||
rev: String,
|
||||
indent: &str,
|
||||
) -> Result<()> {
|
||||
let (owner, repo) = self
|
||||
.get_repo(url)
|
||||
.with_context(|| format!("failed to parse {url} as a {} url", Self::FLAKE_TYPE))?;
|
||||
@ -107,7 +109,13 @@ pub trait UrlFlakeFetcher {
|
||||
const FLAKE_TYPE: &'static str;
|
||||
const NAME: &'static str;
|
||||
|
||||
fn fetch_nix(&self, out: &mut impl Write, url: &Url, rev: String, indent: &str) -> Result<()> {
|
||||
fn fetch_nix_imp(
|
||||
&self,
|
||||
out: &mut impl Write,
|
||||
url: &Url,
|
||||
rev: String,
|
||||
indent: &str,
|
||||
) -> Result<()> {
|
||||
let hash = flake_prefetch(format!(
|
||||
"{}+{url}?{}={rev}",
|
||||
Self::FLAKE_TYPE,
|
||||
@ -128,3 +136,35 @@ pub trait UrlFlakeFetcher {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
trait GetStdout {
|
||||
fn get_stdout(&mut self) -> Result<Vec<u8>>;
|
||||
}
|
||||
|
||||
impl GetStdout for Command {
|
||||
fn get_stdout(&mut self) -> Result<Vec<u8>> {
|
||||
let Output { stdout, status, .. } = self.stderr(Stdio::inherit()).output()?;
|
||||
if !status.success() {
|
||||
bail!("command exited with exit code {}", status);
|
||||
}
|
||||
Ok(stdout)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flake_prefetch(flake_ref: String) -> Result<String> {
|
||||
#[derive(Deserialize)]
|
||||
struct PrefetchOutput {
|
||||
hash: String,
|
||||
}
|
||||
|
||||
eprintln!("$ nix flake prefetch --json {flake_ref}");
|
||||
Ok(serde_json::from_slice::<PrefetchOutput>(
|
||||
&Command::new("nix")
|
||||
.arg("flake")
|
||||
.arg("prefetch")
|
||||
.arg("--json")
|
||||
.arg(flake_ref)
|
||||
.get_stdout()?,
|
||||
)?
|
||||
.hash)
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
use crate::fetcher::SimpleFlakeFetcher;
|
||||
use crate::{fetcher::SimpleFlakeFetcher, impl_fetcher};
|
||||
|
||||
pub struct FetchFromSourcehut<'a>(pub Option<&'a str>);
|
||||
pub struct FetchFromSourcehut(pub Option<String>);
|
||||
impl_fetcher!(FetchFromSourcehut);
|
||||
|
||||
impl<'a> SimpleFlakeFetcher<'a> for FetchFromSourcehut<'a> {
|
||||
impl<'a> SimpleFlakeFetcher<'a> for FetchFromSourcehut {
|
||||
const FLAKE_TYPE: &'static str = "sourcehut";
|
||||
const NAME: &'static str = "fetchFromSourcehut";
|
||||
|
||||
fn host(&self) -> Option<&'a str> {
|
||||
self.0
|
||||
fn host(&'a self) -> &'a Option<String> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
56
src/main.rs
56
src/main.rs
@ -6,10 +6,10 @@ use clap::Parser;
|
||||
use url::Host;
|
||||
|
||||
use crate::{
|
||||
cli::{Fetcher, Opts},
|
||||
cli::{FetcherFunction, Opts},
|
||||
fetcher::{
|
||||
FetchFromGitHub, FetchFromGitLab, FetchFromSourcehut, Fetchgit, Fetchhg,
|
||||
SimpleFlakeFetcher, UrlFlakeFetcher,
|
||||
FetchFromGitHub, FetchFromGitLab, FetchFromSourcehut, Fetcher, FetcherDispatch, Fetchgit,
|
||||
Fetchhg,
|
||||
},
|
||||
};
|
||||
|
||||
@ -18,54 +18,50 @@ use std::io::stdout;
|
||||
fn main() -> Result<()> {
|
||||
let opts = Opts::parse();
|
||||
|
||||
let out = &mut stdout().lock();
|
||||
let indent = &" ".repeat(opts.indent);
|
||||
|
||||
match (opts.fetcher, opts.url.host()) {
|
||||
(Some(Fetcher::FetchFromGitHub), Some(host)) => {
|
||||
FetchFromGitHub((host != Host::Domain("github.com")).then_some(&host.to_string()))
|
||||
.fetch_nix(out, &opts.url, opts.rev, indent)?;
|
||||
let fetcher: FetcherDispatch = match (opts.fetcher, opts.url.host()) {
|
||||
(None | Some(FetcherFunction::FetchFromGitHub), Some(Host::Domain("github.com"))) => {
|
||||
FetchFromGitHub(None).into()
|
||||
}
|
||||
(None, Some(Host::Domain("github.com"))) => {
|
||||
FetchFromGitHub(None).fetch_nix(out, &opts.url, opts.rev, indent)?;
|
||||
(Some(FetcherFunction::FetchFromGitHub), Some(host)) => {
|
||||
FetchFromGitHub(Some(host.to_string())).into()
|
||||
}
|
||||
|
||||
(Some(Fetcher::FetchFromGitLab), Some(host)) => {
|
||||
FetchFromGitLab((host != Host::Domain("github.com")).then_some(&host.to_string()))
|
||||
.fetch_nix(out, &opts.url, opts.rev, indent)?;
|
||||
(None | Some(FetcherFunction::FetchFromGitLab), Some(Host::Domain("gitlab.com"))) => {
|
||||
FetchFromGitLab(None).into()
|
||||
}
|
||||
(None, Some(Host::Domain(host))) if host.starts_with("gitlab.") => {
|
||||
FetchFromGitLab((host != "gitlab.com").then_some(host))
|
||||
.fetch_nix(out, &opts.url, opts.rev, indent)?;
|
||||
FetchFromGitLab(Some(host.into())).into()
|
||||
}
|
||||
(Some(FetcherFunction::FetchFromGitLab), Some(host)) => {
|
||||
FetchFromGitLab(Some(host.to_string())).into()
|
||||
}
|
||||
|
||||
(Some(Fetcher::FetchFromSourcehut), Some(host)) => {
|
||||
FetchFromSourcehut((host != Host::Domain("git.sr.ht")).then_some(&host.to_string()))
|
||||
.fetch_nix(out, &opts.url, opts.rev, indent)?;
|
||||
(None | Some(FetcherFunction::FetchFromSourcehut), Some(Host::Domain("git.sr.ht"))) => {
|
||||
FetchFromSourcehut(None).into()
|
||||
}
|
||||
(None, Some(Host::Domain("git.sr.ht"))) => {
|
||||
FetchFromSourcehut(None).fetch_nix(out, &opts.url, opts.rev, indent)?;
|
||||
(Some(FetcherFunction::FetchFromSourcehut), Some(host)) => {
|
||||
FetchFromSourcehut(Some(host.to_string())).into()
|
||||
}
|
||||
|
||||
(
|
||||
Some(
|
||||
fetcher @ (Fetcher::FetchFromGitHub
|
||||
| Fetcher::FetchFromGitLab
|
||||
| Fetcher::FetchFromSourcehut),
|
||||
fetcher @ (FetcherFunction::FetchFromGitHub
|
||||
| FetcherFunction::FetchFromGitLab
|
||||
| FetcherFunction::FetchFromSourcehut),
|
||||
),
|
||||
None,
|
||||
) => {
|
||||
bail!("{fetcher:?} does not support URLs without a host");
|
||||
}
|
||||
|
||||
(Some(Fetcher::Fetchgit), _) | (None, _) => {
|
||||
Fetchgit.fetch_nix(out, &opts.url, opts.rev, indent)?;
|
||||
}
|
||||
(Some(FetcherFunction::Fetchgit), _) | (None, _) => Fetchgit.into(),
|
||||
|
||||
(Some(Fetcher::Fetchhg), _) => {
|
||||
Fetchhg.fetch_nix(out, &opts.url, opts.rev, indent)?;
|
||||
}
|
||||
}
|
||||
(Some(FetcherFunction::Fetchhg), _) => Fetchhg.into(),
|
||||
};
|
||||
|
||||
fetcher.fetch_nix(&mut stdout().lock(), &opts.url, opts.rev, indent)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user