use std::collections::HashSet;
use crate::cache::{Cache, CacheConfig, DEFAULT_URI_TYPES};
fn extract_owner_prefix(uri: &str) -> Option<String> {
for prefix in ["github:", "gitlab:"] {
if let Some(rest) = uri.strip_prefix(prefix) {
if let Some(slash_pos) = rest.find('/') {
if slash_pos > 0 && rest.len() > slash_pos + 1 {
return Some(format!("{}{}/", prefix, &rest[..slash_pos]));
}
}
}
}
if let Some(rest) = uri.strip_prefix("sourcehut:")
&& let Some(slash_pos) = rest.find('/')
&& slash_pos > 0
&& rest.len() > slash_pos + 1
{
return Some(format!("sourcehut:{}/", &rest[..slash_pos]));
}
for host in ["github.com", "gitlab.com", "codeberg.org"] {
let pattern = format!("git+https://{}/", host);
if let Some(rest) = uri.strip_prefix(&pattern)
&& let Some(slash_pos) = rest.find('/')
&& slash_pos > 0
&& rest.len() > slash_pos + 1
{
return Some(format!("{}{}/", pattern, &rest[..slash_pos]));
}
}
None
}
pub fn uri_completion_items(id: Option<&str>, cache_config: &CacheConfig) -> Vec<String> {
let mut items: Vec<String> = Vec::new();
let mut seen: HashSet<String> = HashSet::new();
let cache = match cache_config {
CacheConfig::Default => Some(Cache::load()),
CacheConfig::Custom(path) => Some(Cache::from_path(path)),
CacheConfig::None => None,
};
if let Some(cache) = cache {
let cached_uris = cache.list_uris();
if let Some(id) = id {
for uri in cache.list_uris_for_id(id) {
if seen.insert(uri.clone()) {
items.push(uri);
}
}
}
for uri_type in DEFAULT_URI_TYPES {
let s = uri_type.to_string();
if seen.insert(s.clone()) {
items.push(s);
}
}
let mut owner_prefixes: Vec<String> = cached_uris
.iter()
.filter_map(|uri| extract_owner_prefix(uri))
.filter(|prefix| !seen.contains(prefix))
.collect::<HashSet<_>>()
.into_iter()
.collect();
owner_prefixes.sort();
for prefix in owner_prefixes {
seen.insert(prefix.clone());
items.push(prefix);
}
for uri in cached_uris {
if seen.insert(uri.clone()) {
items.push(uri);
}
}
} else {
items.extend(DEFAULT_URI_TYPES.iter().map(|s| s.to_string()));
}
items
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_extract_owner_prefix_github() {
assert_eq!(
extract_owner_prefix("github:mic92/vmsh"),
Some("github:mic92/".to_string())
);
assert_eq!(
extract_owner_prefix("github:NixOS/nixpkgs"),
Some("github:NixOS/".to_string())
);
assert_eq!(
extract_owner_prefix("github:nix-community/home-manager"),
Some("github:nix-community/".to_string())
);
}
#[test]
fn test_extract_owner_prefix_gitlab() {
assert_eq!(
extract_owner_prefix("gitlab:someorg/project"),
Some("gitlab:someorg/".to_string())
);
}
#[test]
fn test_extract_owner_prefix_sourcehut() {
assert_eq!(
extract_owner_prefix("sourcehut:~user/repo"),
Some("sourcehut:~user/".to_string())
);
}
#[test]
fn test_extract_owner_prefix_git_https() {
assert_eq!(
extract_owner_prefix("git+https://github.com/owner/repo"),
Some("git+https://github.com/owner/".to_string())
);
assert_eq!(
extract_owner_prefix("git+https://gitlab.com/org/project"),
Some("git+https://gitlab.com/org/".to_string())
);
assert_eq!(
extract_owner_prefix("git+https://codeberg.org/user/repo"),
Some("git+https://codeberg.org/user/".to_string())
);
}
#[test]
fn test_extract_owner_prefix_none() {
assert_eq!(extract_owner_prefix("path:/some/local/path"), None);
assert_eq!(extract_owner_prefix("flake:nixpkgs"), None);
assert_eq!(extract_owner_prefix("github:"), None);
assert_eq!(extract_owner_prefix("github:owner"), None); assert_eq!(extract_owner_prefix("github:owner/"), None); }
#[test]
fn test_extract_owner_prefix_with_query_params() {
assert_eq!(
extract_owner_prefix("github:NixOS/nixpkgs?ref=nixos-unstable"),
Some("github:NixOS/".to_string())
);
}
}