use std::{ffi::OsStr, path::PathBuf};
use clap_complete::engine::CompletionCandidate;
use logoxide::{Project, manifest::MANIFEST};
fn config() -> logoxide::config::Config {
logoxide::config::Config::load().unwrap_or_default()
}
fn project() -> Option<Project> {
let global = std::env::args().any(|a| a == "--global");
let dir = match !global && std::path::Path::new(MANIFEST).is_file() {
true => PathBuf::from("."),
false => (config().library(None, None).root()).to_path_buf(),
};
Project::open(dir).ok()
}
fn candidate(value: String, help: String) -> CompletionCandidate {
let help = logoxide::text::printable(&help).into_owned();
CompletionCandidate::new(value).help(Some(help.into()))
}
pub fn package(current: &OsStr) -> Vec<CompletionCandidate> {
let current = current.to_string_lossy();
let mut found = vec![];
let registries = project().map(|p| (config().registries(p.manifest()), p.dir().to_path_buf()));
if let Some((registries, dir)) = ®istries {
match logoxide::catalog::split_registry_name(¤t, registries)
.map(|(r, _)| r)
.or_else(|| {
current
.strip_suffix(':')
.filter(|r| registries.contains_key(*r))
}) {
Some(registry) => {
let location = ®istries[registry];
let http = logoxide_app::http::NoHttp;
let cache = logoxide_app::catalog_cache::CatalogCache::in_cache(
config().library(None, None).cache_dir(),
config().catalog_max_age(),
);
let fetch = logoxide_app::catalogs::CatalogFetch::new(
&http,
cache.as_ref(),
logoxide_app::catalog_cache::Freshness::Offline,
);
let Ok(catalog) =
logoxide_app::catalogs::load_registry(fetch, registry, location, dir)
else {
return vec![];
};
for entry in &catalog.entries {
let help = format!(
"{} {}",
entry.name,
entry.version.as_deref().unwrap_or_default()
);
let path = entry.path.as_deref().unwrap_or(&entry.id);
found.push(candidate(format!("{registry}:{path}"), help.clone()));
let shared = (catalog.entries.iter())
.filter(|e| e.id == entry.id)
.count()
> 1;
if !shared && path != entry.id {
found.push(candidate(format!("{registry}:{}", entry.id), help));
}
}
}
None => {
for (name, location) in registries {
found.push(candidate(
format!("{name}:"),
format!("registry: {location}"),
));
}
}
}
}
if !current.contains(':') {
for (host, url) in logoxide::git::HOSTS {
found.push(candidate(format!("{host}:"), format!("{url}/owner/repo")));
}
}
found.retain(|c| {
c.get_value()
.to_string_lossy()
.starts_with(current.as_ref())
});
found
}
pub fn dependency(current: &OsStr) -> Vec<CompletionCandidate> {
let current = current.to_string_lossy();
let Some(project) = project() else {
return vec![];
};
(project.manifest().dependencies.keys())
.filter(|name| name.starts_with(current.as_ref()))
.map(CompletionCandidate::new)
.collect()
}
pub fn registry(current: &OsStr) -> Vec<CompletionCandidate> {
let current = current.to_string_lossy();
let Some(project) = project() else {
return vec![];
};
(config().registries(project.manifest()).into_iter())
.filter(|(name, _)| name.starts_with(current.as_ref()))
.map(|(name, location)| candidate(name, location))
.collect()
}
pub fn searched(current: &OsStr) -> Vec<CompletionCandidate> {
let current = current.to_string_lossy();
let mut found = vec![];
if let Some(project) = project() {
found.extend(
(project.manifest().registries().into_iter())
.filter(|(name, _)| name.starts_with(current.as_ref()))
.map(|(name, location)| candidate(name, location)),
);
found.extend(
(project.manifest().catalogs().into_iter())
.filter(|c| c.starts_with(current.as_ref()))
.map(CompletionCandidate::new),
);
}
found
}