use std::path::Path;
use anyhow::anyhow;
use clap::Parser;
use crate::registry::{SearchVersion, SemVersion};
mod args;
mod registry;
#[tokio::main]
async fn main() {
let args = args::App::parse();
let binding = args.global_opts.directory.unwrap_or(".".to_string());
let path = Path::new(&binding);
match args.command {
args::Command::Registry(a) => match a.command {
args::RegistrySubbommand::Compile {} => {
let _ = registry::proj2fb(path, path).await;
}
args::RegistrySubbommand::Sync {} => {
todo!("sync command");
}
},
args::Command::Add(a) => {
for i in a.pkgs {
let pkg_query_parsed = parse_pkg(&i);
let mut search_engine = registry::SearchEngine::new(5);
let result = match search_engine
.search_package(
pkg_query_parsed.pkg_name,
pkg_query_parsed.pkg_namespace,
pkg_query_parsed.pkg_authority,
pkg_query_parsed.pkg_version,
pkg_query_parsed.pkg_hash,
pkg_query_parsed.reg_ref,
)
.await
{
Err(e) => {
eprintln!("{}", e);
continue;
}
Ok(o) => o,
};
if result.is_empty() {
println!("'{}' NOT Found :(", i)
} else {
if result.len() == 1 {
let result = &result[0];
let version_item = &result.versions;
let v = get_version(version_item.to_vec(), pkg_query_parsed.pkg_version)
.unwrap();
let mut tag = v.tag.unwrap_or("".to_string());
if tag != String::new() {
tag = format!("-{}", tag);
}
let h = v.hash;
println!(
"adding {}@{}.{}.{}{}%{:16x}{:16x}{:16x}{:16x} from ",
result.name, v.major, v.minor, v.patch, tag, h[0], h[1], h[2], h[3]
)
}
}
}
}
}
}
fn get_version(
versions: Vec<SearchVersion>,
version: Option<&str>,
) -> anyhow::Result<SearchVersion> {
if let Some(version) = version {
let v = SemVersion::parse(version);
for i in versions {
if i.major == v.major
&& i.minor == v.minor
&& i.patch == v.patch
&& i.tag.as_deref().unwrap_or("") == v.clone().tag.unwrap_or("".to_string())
{
return Ok(i);
}
}
} else {
let mut major: u16 = 0;
let mut minor: u8 = 0;
let mut patch: u8 = 0;
let mut newest_version: Option<SearchVersion> = None;
for i in versions {
let ma = i.major;
let mi = i.minor;
let pa = i.patch;
if ma > major {
major = ma;
if mi > minor {
minor = mi;
if pa > patch {
patch = pa;
newest_version = Some(i);
}
}
}
}
if major == 0 && minor == 0 && patch == 0 {
return Err(anyhow!(
"No version found (NOTE: Version 0.0.0 is forbidden)"
));
} else {
return Ok(newest_version.unwrap());
}
}
Err(anyhow!("No version found"))
}
struct Pkg<'a> {
pkg_name: &'a str,
pkg_namespace: &'a str,
pkg_authority: Option<&'a str>,
pkg_version: Option<&'a str>,
pkg_hash: Option<&'a str>,
reg_ref: Option<&'a str>,
}
fn parse_pkg<'a>(pkg_input: &'a str) -> Pkg<'a> {
let mut input = pkg_input;
let mut reg_ref = None;
let mut authority: Option<&str> = None;
let mut version: Option<&str> = None;
let mut hash: Option<&str> = None;
if let Some(pos) = input.rfind('#') {
let left = &input[..pos];
let right = &input[pos + 1..];
if left.starts_with("github:")
|| left.starts_with("http://")
|| left.starts_with("https://")
{
reg_ref = Some(left);
input = right;
} else {
authority = Some(left);
input = right;
}
}
if let Some(pos) = input.rfind('%') {
hash = Some(&input[pos + 1..]);
input = &input[..pos];
}
if let Some(pos) = input.rfind('@') {
version = Some(&input[pos + 1..]);
input = &input[..pos];
}
let (namespace, name) = match input.rfind('/') {
Some(pos) => (&input[..pos], &input[pos + 1..]),
None => ("", input),
};
Pkg {
pkg_name: &name,
pkg_namespace: &namespace,
pkg_authority: authority,
pkg_version: version,
pkg_hash: hash,
reg_ref,
}
}