mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: yazi-cli: support raw git urls
This commit is contained in:
parent
9782c0fa8f
commit
2ec2bb0254
3 changed files with 181 additions and 31 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -3258,6 +3258,7 @@ dependencies = [
|
|||
"clap_complete_nushell",
|
||||
"crossterm",
|
||||
"md-5",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"toml_edit",
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ anyhow = { workspace = true }
|
|||
clap = { workspace = true }
|
||||
crossterm = { workspace = true }
|
||||
md-5 = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
toml_edit = "0.22.22"
|
||||
|
|
|
|||
|
|
@ -1,13 +1,33 @@
|
|||
use std::{borrow::Cow, io::BufWriter, path::PathBuf};
|
||||
use std::{borrow::Cow, io::BufWriter, path::PathBuf, sync::OnceLock};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use anyhow::{Error, Result};
|
||||
use md5::{Digest, Md5};
|
||||
use regex::Regex;
|
||||
use yazi_shared::Xdg;
|
||||
|
||||
static PACKAGE_RAW_URL_RE: OnceLock<Regex> = OnceLock::new();
|
||||
static PACKAGE_SHORT_URL_RE: OnceLock<Regex> = OnceLock::new();
|
||||
|
||||
#[inline]
|
||||
fn package_raw_url_re() -> &'static Regex {
|
||||
PACKAGE_RAW_URL_RE.get_or_init(|| {
|
||||
Regex::new(r"^(?P<proto>[^:]+)://(?P<host>[^/]+)/(?P<url_path>[^:]+)(:(?P<child>.*))?$")
|
||||
.unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn package_short_url_re() -> &'static Regex {
|
||||
PACKAGE_SHORT_URL_RE.get_or_init(|| {
|
||||
Regex::new(r"^((?P<host>[^/]+)/)?(?P<owner>[^/]+)/(?P<repo>[^:]+)(:(?P<child>.*))?$").unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Package {
|
||||
pub(crate) proto: String,
|
||||
pub(crate) host: String,
|
||||
pub(crate) owner: String,
|
||||
pub(crate) repo_name: String,
|
||||
pub(crate) url_path: String,
|
||||
pub(crate) child: String,
|
||||
pub(crate) rev: String,
|
||||
pub(super) is_flavor: bool,
|
||||
|
|
@ -15,41 +35,52 @@ pub(crate) struct Package {
|
|||
|
||||
impl Package {
|
||||
pub(super) fn new(url: &str, rev: Option<&str>) -> Result<Self> {
|
||||
let mut parts = url.splitn(2, ':');
|
||||
let rev = rev.unwrap_or_default().to_owned();
|
||||
let is_flavor = false;
|
||||
|
||||
let mut repo_part = parts.next().unwrap_or_default().to_owned();
|
||||
let child = if let Some(s) = parts.next() {
|
||||
format!("{s}.yazi")
|
||||
if let Some(raw_url_match) = package_raw_url_re().captures(url) {
|
||||
let proto = raw_url_match["proto"].to_owned();
|
||||
let host = raw_url_match["host"].to_owned();
|
||||
let mut url_path = raw_url_match["url_path"].to_owned();
|
||||
let child = if let Some(child) = raw_url_match.name("child") {
|
||||
format!("{}.yazi", child.as_str())
|
||||
} else {
|
||||
url_path.push_str(".yazi");
|
||||
String::new()
|
||||
};
|
||||
|
||||
Ok(Self { proto, host, url_path, child, rev, is_flavor })
|
||||
} else if let Some(short_url_match) = package_short_url_re().captures(url) {
|
||||
let proto = "https".to_owned();
|
||||
let host =
|
||||
short_url_match.name("host").map(|m| m.as_str()).unwrap_or("github.com").to_owned();
|
||||
let owner = &short_url_match["owner"];
|
||||
let repo = &short_url_match["repo"];
|
||||
let mut url_path = format!("{owner}/{repo}");
|
||||
|
||||
let child = if let Some(child) = short_url_match.name("child") {
|
||||
format!("{}.yazi", child.as_str())
|
||||
} else {
|
||||
url_path.push_str(".yazi");
|
||||
String::new()
|
||||
};
|
||||
|
||||
Ok(Self { proto, host, url_path, child, rev, is_flavor })
|
||||
} else {
|
||||
repo_part.push_str(".yazi");
|
||||
String::new()
|
||||
};
|
||||
|
||||
let mut repo = repo_part.rsplit('/');
|
||||
let repo_name = repo.next().context("failed to get repo name")?.to_owned();
|
||||
let owner = repo.next().context("failed to get repo owner")?.to_owned();
|
||||
let host = repo.next().unwrap_or("github.com").to_owned();
|
||||
|
||||
Ok(Self {
|
||||
repo_name,
|
||||
owner,
|
||||
host,
|
||||
child,
|
||||
rev: rev.unwrap_or_default().to_owned(),
|
||||
is_flavor: false,
|
||||
})
|
||||
Err(Error::msg("invalid package url"))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn use_(&self) -> Cow<str> {
|
||||
if self.child.is_empty() {
|
||||
format!("{}/{}/{}", self.host, self.owner, self.repo_name.trim_end_matches(".yazi")).into()
|
||||
format!("{}://{}/{}", self.proto, self.host, self.url_path.trim_end_matches(".yazi")).into()
|
||||
} else {
|
||||
format!(
|
||||
"{}/{}/{}:{}",
|
||||
"{}://{}/{}:{}",
|
||||
self.proto,
|
||||
self.host,
|
||||
self.owner,
|
||||
self.repo_name,
|
||||
self.url_path,
|
||||
self.child.trim_end_matches(".yazi")
|
||||
)
|
||||
.into()
|
||||
|
|
@ -58,7 +89,11 @@ impl Package {
|
|||
|
||||
#[inline]
|
||||
pub(super) fn name(&self) -> &str {
|
||||
if self.child.is_empty() { self.repo_name.as_str() } else { self.child.as_str() }
|
||||
if self.child.is_empty() {
|
||||
self.url_path.rsplit('/').next().unwrap_or(&self.url_path)
|
||||
} else {
|
||||
self.child.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -70,7 +105,7 @@ impl Package {
|
|||
|
||||
#[inline]
|
||||
pub(super) fn remote(&self) -> String {
|
||||
format!("https://{}/{}/{}.git", self.host, self.owner, self.repo_name)
|
||||
format!("{}://{}/{}", self.proto, self.host, self.url_path)
|
||||
}
|
||||
|
||||
pub(super) fn header(&self, s: &str) -> Result<()> {
|
||||
|
|
@ -90,3 +125,116 @@ impl Package {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn two_component_short_url() -> Result<()> {
|
||||
let url = "owner/repo";
|
||||
|
||||
let pkg = Package::new(url, None)?;
|
||||
|
||||
assert_eq!(pkg.proto, "https");
|
||||
assert_eq!(pkg.host, "github.com");
|
||||
assert_eq!(pkg.url_path, "owner/repo.yazi");
|
||||
assert_eq!(pkg.child, "");
|
||||
|
||||
assert_eq!(pkg.remote(), "https://github.com/owner/repo.yazi");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn three_component_short_url() -> Result<()> {
|
||||
let url = "codeberg.org/owner/repo";
|
||||
|
||||
let pkg = Package::new(url, None)?;
|
||||
|
||||
assert_eq!(pkg.proto, "https");
|
||||
assert_eq!(pkg.host, "codeberg.org");
|
||||
assert_eq!(pkg.url_path, "owner/repo.yazi");
|
||||
assert_eq!(pkg.child, "");
|
||||
|
||||
assert_eq!(pkg.remote(), "https://codeberg.org/owner/repo.yazi");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_component_short_url_with_child_path() -> Result<()> {
|
||||
let url = "owner/repo:my-plugin";
|
||||
|
||||
let pkg = Package::new(url, None)?;
|
||||
|
||||
assert_eq!(pkg.proto, "https");
|
||||
assert_eq!(pkg.host, "github.com");
|
||||
assert_eq!(pkg.url_path, "owner/repo");
|
||||
assert_eq!(pkg.child, "my-plugin.yazi");
|
||||
|
||||
assert_eq!(pkg.remote(), "https://github.com/owner/repo");
|
||||
assert_eq!(pkg.use_(), "https://github.com/owner/repo:my-plugin");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn raw_ssh_url() -> Result<()> {
|
||||
let url = "ssh://git@my-host:6969/my-plugin";
|
||||
|
||||
let pkg = Package::new(url, None)?;
|
||||
|
||||
assert_eq!(pkg.proto, "ssh");
|
||||
assert_eq!(pkg.host, "git@my-host:6969");
|
||||
assert_eq!(pkg.url_path, "my-plugin.yazi");
|
||||
assert_eq!(pkg.child, "");
|
||||
|
||||
assert_eq!(pkg.remote(), "ssh://git@my-host:6969/my-plugin.yazi");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn raw_ssh_url_with_child_path() -> Result<()> {
|
||||
let url = "ssh://git@192.168.0.69:2222/~/my-repo.git:my-plugin";
|
||||
|
||||
let pkg = Package::new(url, None)?;
|
||||
|
||||
assert_eq!(pkg.proto, "ssh");
|
||||
assert_eq!(pkg.host, "git@192.168.0.69:2222");
|
||||
assert_eq!(pkg.url_path, "~/my-repo.git");
|
||||
assert_eq!(pkg.child, "my-plugin.yazi");
|
||||
|
||||
assert_eq!(pkg.remote(), "ssh://git@192.168.0.69:2222/~/my-repo.git");
|
||||
assert_eq!(pkg.use_(), "ssh://git@192.168.0.69:2222/~/my-repo.git:my-plugin");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn raw_http_url_with_non_standard_path() -> Result<()> {
|
||||
let url = "https://example.com/xxx/yyy/zzz/owner/repo:my-plugin";
|
||||
|
||||
let pkg = Package::new(url, None)?;
|
||||
|
||||
assert_eq!(pkg.proto, "https");
|
||||
assert_eq!(pkg.host, "example.com");
|
||||
assert_eq!(pkg.url_path, "xxx/yyy/zzz/owner/repo");
|
||||
assert_eq!(pkg.child, "my-plugin.yazi");
|
||||
|
||||
assert_eq!(pkg.remote(), "https://example.com/xxx/yyy/zzz/owner/repo");
|
||||
assert_eq!(pkg.use_(), "https://example.com/xxx/yyy/zzz/owner/repo:my-plugin");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_url() {
|
||||
let url = "one-component-url???";
|
||||
|
||||
let pkg = Package::new(url, None);
|
||||
|
||||
assert!(pkg.is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue