Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions apps/native/src-tauri/src/evolve/search_packages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,42 @@ pub struct SearchPackageResult {
pub install_via: SearchResultInstallTarget,
}

/// Wrapper for `nix registry list` that returns the raw output as a string, or an error if the command fails.
/// Clients can parse it themselves.
fn nix_registry_list(config_dir: &str) -> Result<String> {
let mut cmd = Command::new("nix");
cmd.args(["registry", "list"])
.current_dir(config_dir)
.env("PATH", crate::system::nix::get_nix_path())
.env("NIX_CONFIG", "experimental-features = nix-command flakes");

let output = cmd.output()?;

if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
let truncated_stderr = truncate_error(&stderr, 8000);
return Err(anyhow::anyhow!(
"nix registry list failed with status {:?}: {}",
output.status.code(),
truncated_stderr
));
}

Ok(String::from_utf8_lossy(&output.stdout).to_string())
}

/// Determine prior to searching whether a channel is registered and can be searched,
/// to avoid unnecessary command execution and errors.
/// Check if the output contains the channel in a "flake:channel" format.
fn channel_is_registered(registry_list: &str, channel: &str) -> bool {
// Split each line by whitespace and check if any token equals "flake:<channel>".
// Avoids partial matches.
let channel_pattern = format!("flake:{}", channel);
registry_list
.lines()
.any(|line| line.split_whitespace().any(|token| token == channel_pattern))
}

/// Search a single channel and return a list of SearchPackageResult
/// results.
fn search_single_channel(
Expand Down Expand Up @@ -154,11 +190,19 @@ fn collect_from_channels(
limit: u64,
structured: &mut Vec<SearchPackageResult>,
) -> Result<bool> {
let registry_list = nix_registry_list(config_dir)?;

for channel in channels {
if structured.len() >= limit as usize {
return Ok(true);
}

if !channel_is_registered(&registry_list, channel) {
// CONSIDER: Whether we need to surface this to the agent somehow.
log::warn!("Channel '{}' is not registered, skipping search for this channel", channel);
continue;
}

let channel_results = search_single_channel(config_dir, query_term, use_regex, channel)?;
let complete = process_channel_results(structured, channel_results, limit)?;
if complete {
Expand Down Expand Up @@ -398,4 +442,12 @@ mod tests {

assert_eq!(structured.len(), 2, "expected deduplication by attr_path");
}

#[test]
fn channel_registration_check() {
let registry_list = "global flake:agda github:agda/agd\nglobal flake:nixpkgs github:NixOS/nixpkgs/nixpkgs-unstable\nglobal flake:nix github:NixOS/nix\n";
assert!(channel_is_registered(registry_list, "nixpkgs"));
assert!(channel_is_registered(registry_list, "nix"));
assert!(!channel_is_registered(registry_list, "unregistered-channel"));
}
}
Loading