From d442bf689cdbd4e5bedd59cd83ff97d67d9dab85 Mon Sep 17 00:00:00 2001 From: grimhilt Date: Sun, 15 Sep 2024 18:16:46 +0200 Subject: [PATCH] feat(clone): read url --- src/commands.rs | 1 + src/commands/clone.rs | 118 +++++++++++++++++++++++++++++++++++++++ src/main.rs | 2 + src/subcommands.rs | 1 + src/subcommands/clone.rs | 45 +++++++++++++++ tests/status_new_test.rs | 2 +- 6 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 src/commands/clone.rs create mode 100644 src/subcommands/clone.rs diff --git a/src/commands.rs b/src/commands.rs index 90f61a8..5d7bbb7 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -4,3 +4,4 @@ pub mod status; pub mod reset; pub mod push; pub mod test; +pub mod clone; diff --git a/src/commands/clone.rs b/src/commands/clone.rs new file mode 100644 index 0000000..88b3d02 --- /dev/null +++ b/src/commands/clone.rs @@ -0,0 +1,118 @@ +use crate::config::config::Config; +use regex::Regex; + +pub struct CloneArgs { + pub remote: String, + pub depth: Option, +} + +pub fn exec(args: CloneArgs, config: Config) { + get_url_props(&args.remote); +} + +struct UrlProps<'a> { + is_secure: bool, + domain: &'a str, + path: &'a str, +} + +impl UrlProps<'_> { + fn new() -> Self { + UrlProps { + is_secure: true, + domain: "", + path: "", + } + } +} + +pub fn get_url_props(url: &str) -> UrlProps { + let mut url_props = UrlProps::new(); + + // Match protocol and domain + let re = Regex::new(r"((?https?)://)?(?[^/]*)").unwrap(); + let captures = re.captures(url).expect("fatal: invalid url"); + + // Assume is secure + let protocol = captures.name("protocol").map_or("https", |m| m.as_str()); + + url_props.is_secure = protocol == "https"; + url_props.domain = captures + .name("domain") + .map(|m| m.as_str()) + .expect("fatal: domain not found"); + + // Get rest of url + let end_of_domain_idx = captures + .name("domain") + .expect("Already unwraped before") + .end(); + let rest_of_url = &url[end_of_domain_idx..]; + + // Try webdav url + if let Some(rest_of_url) = rest_of_url.strip_prefix("/remote.php/dav/files") { + let re = Regex::new(r"[^\/]*(?.*)").unwrap(); + url_props.path = re + .captures(rest_of_url) + .expect("fatal: invalid webdav url") + .name("path") + .map_or("/", |m| m.as_str()); + return url_props; + } + + // Try 'dir' argument + let re = Regex::new(r"\?dir=(?[^&]*)").unwrap(); + if let Some(captures) = re.captures(rest_of_url) { + url_props.path = captures.name("path").map_or("/", |m| m.as_str()); + return url_props; + } + + // Try path next to domain + if rest_of_url.chars().nth(0).expect("fatal: invalid url") == '/' { + url_props.path = rest_of_url; + return url_props; + } + + panic!("fatal: invalid url (cannot found path)"); +} + +#[cfg(test)] +mod tests { + use super::*; + const DOMAIN: &str = "nextcloud.com"; + const SUBDOMAIN: &str = "nextcloud.example.com"; + + fn compare_url_props(url_to_test: &str, is_secure: bool, domain: &str) { + let path = "/foo/bar"; + let url_props = get_url_props(url_to_test); + assert_eq!(url_props.is_secure, is_secure); + assert_eq!(url_props.domain, domain); + assert_eq!(url_props.path, path); + } + + #[test] + fn get_url_props_from_browser_test() { + compare_url_props( + "https://nextcloud.com/apps/files/?dir=/foo/bar&fileid=166666", + true, + DOMAIN, + ); + compare_url_props( + "https://nextcloud.com/apps/files/files/625?dir=/foo/bar", + true, + DOMAIN, + ); + } + + #[test] + fn get_url_props_direct_url_test() { + compare_url_props("https://nextcloud.example.com/foo/bar", true, SUBDOMAIN); + compare_url_props("http://nextcloud.example.com/foo/bar", false, SUBDOMAIN); + compare_url_props("nextcloud.example.com/foo/bar", true, SUBDOMAIN); + } + + #[test] + fn get_url_props_with_port_test() { + compare_url_props("localhost:8080/foo/bar", true, "localhost:8080"); + } +} diff --git a/src/main.rs b/src/main.rs index 262fcc1..d88c97a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,7 @@ fn main() { subcommands::reset::create(), subcommands::push::create(), subcommands::test::create(), + subcommands::clone::create(), ]); // .setting(clap::AppSettings::SubcommandRequiredElseHelp); @@ -31,6 +32,7 @@ fn main() { Some(("reset", args)) => subcommands::reset::handler(args), Some(("push", args)) => subcommands::push::handler(args), Some(("test", args)) => subcommands::test::handler(args), + Some(("clone", args)) => subcommands::clone::handler(args), Some((_, _)) => {} None => {} }; diff --git a/src/subcommands.rs b/src/subcommands.rs index 5db3430..e211051 100644 --- a/src/subcommands.rs +++ b/src/subcommands.rs @@ -4,3 +4,4 @@ pub mod reset; pub mod status; pub mod push; pub mod test; +pub mod clone; diff --git a/src/subcommands/clone.rs b/src/subcommands/clone.rs new file mode 100644 index 0000000..11e023f --- /dev/null +++ b/src/subcommands/clone.rs @@ -0,0 +1,45 @@ +use clap::{Arg, Command, ArgMatches}; + +use crate::commands; +use crate::config::config::Config; + +pub fn create() -> Command { + // let remote_desc = sized_str(&format!("The repository to clone from. See the NEXTSYNC URLS section below for more information on specifying repositories.")); + // let depth_desc = sized_str(&format!("Depth of the recursive fetch of object properties. This value should be lower when there are a lot of files per directory and higher when there are a lot of subdirectories with fewer files. (Default: {})", clone::DEPTH)); + Command::new("clone") + .arg( + Arg::new("remote") + .required(true) + .num_args(1) + .value_name("REMOTE") + //.help(_desc) + ) + .arg( + Arg::new("depth") + .short('d') + .long("depth") + .required(false) + .num_args(1) + //.help(&depth_desc) + ) + .arg( + Arg::new("directory") + .required(false) + .num_args(1) + .value_name("DIRECTORY") + ) + .about("Clone a repository into a new directory") + .after_help("NEXTSYNC URLS\nThe following syntaxes may be used:\n\t- user@host.xz/path/to/repo\n\t- http[s]://host.xz/apps/files/?dir=/path/to/repo&fileid=111111\n\t- [http[s]://]host.xz/remote.php/dav/files/user/path/to/repo\n") +} + +pub fn handler(args: &ArgMatches) { + if let Some(val) = args.get_one::("directory") { + // global::global::set_dir_path(String::from(val.to_string())); + } + if let Some(remote) = args.get_one::("remote") { + commands::clone::exec(commands::clone::CloneArgs { + remote: remote.to_string(), + depth: args.get_one::("depth").cloned(), + }, Config::new()); + } +} diff --git a/tests/status_new_test.rs b/tests/status_new_test.rs index 101cdf2..1fbb797 100644 --- a/tests/status_new_test.rs +++ b/tests/status_new_test.rs @@ -26,7 +26,7 @@ fn compare_vect(vec1: Vec, vec2: Vec<&str>, config: &Config) { } fn status_expected(config: &Config, staged: Vec<&str>, not_staged: Vec<&str>) { - let res = get_obj_changes(&DEFAULT_STATUS_ARG, config); + let res = get_obj_changes(config); assert_eq!(res.staged.len(), staged.len()); assert_eq!(res.not_staged.len(), not_staged.len());