Function bodies 391 total
main function · rust · L10-L26 (17 LOC)build.rs
fn main() {
// Tell Cargo to rerun this build script if settings.toml changes
println!("cargo:rerun-if-changed=settings.toml");
// Tell Cargo to rerun this build script if any provider toml changes
println!("cargo:rerun-if-changed=providers");
for entry in std::fs::read_dir("providers").unwrap().flatten() {
println!("cargo:rerun-if-changed={}", entry.path().display());
}
// Generate settings code
generate_settings::generate().expect("Failed to generate settings code");
// Generate provider code
generate_providers::generate().expect("Failed to generate provider code");
}prompt_and_run_auth function · rust · L17-L72 (56 LOC)src/auth_prompt.rs
pub fn prompt_and_run_auth(
config: &Config,
provider_config: &ProviderConfig,
provider_name: &str,
error: &FnoxError,
) -> Result<bool> {
// Check if we should prompt
if !config.should_prompt_auth() {
return Ok(false);
}
// Get the auth command for this provider
let Some(auth_command) = provider_config.default_auth_command() else {
return Ok(false);
};
// Show the error and prompt
eprintln!(
"Authentication failed for provider '{}': {}",
provider_name, error
);
let user_confirmed = Confirm::new(format!("Run `{}` to authenticate?", auth_command))
.affirmative("Yes")
.negative("No")
.run()
.map_err(|e| FnoxError::Provider(format!("Failed to show prompt: {}", e)))?;
if !user_confirmed {
return Ok(false);
}
// Run the auth command
eprintln!("Running: {}", auth_command);
let status = if cfg!(target_os = "windows") {
Command::new("assert_command_order function · rust · L52-L60 (9 LOC)src/clap_sort.rs
pub fn assert_command_order(cmd: &Command) {
assert_subcommands_sorted(cmd);
assert_arguments_sorted(cmd);
// Recursively check subcommands
for subcmd in cmd.get_subcommands() {
assert_command_order(subcmd);
}
}format_diff function · rust · L63-L78 (16 LOC)src/clap_sort.rs
fn format_diff<T: std::fmt::Display>(current: &[T], expected: &[T]) -> String {
let mut output = String::new();
output.push_str("\n\n");
output.push_str("Current order:\n");
for item in current {
output.push_str(&format!(" • {}\n", item));
}
output.push_str("\nExpected order:\n");
for item in expected {
output.push_str(&format!(" • {}\n", item));
}
output
}assert_subcommands_sorted function · rust · L81-L95 (15 LOC)src/clap_sort.rs
fn assert_subcommands_sorted(cmd: &Command) {
let subcommand_names: Vec<&str> = cmd.get_subcommands().map(|c| c.get_name()).collect();
let mut sorted_names = subcommand_names.clone();
sorted_names.sort_unstable();
if subcommand_names != sorted_names {
let diff = format_diff(&subcommand_names, &sorted_names);
let error = OrderingError {
command: cmd.get_name().to_string(),
details: format!("Subcommands must be sorted alphabetically.{}", diff),
};
panic!("{:?}", miette::Report::new(error));
}
}assert_arguments_sorted function · rust · L101-L259 (159 LOC)src/clap_sort.rs
fn assert_arguments_sorted(cmd: &Command) {
let args: Vec<&Arg> = cmd.get_arguments().collect();
let mut positional = Vec::new();
let mut with_short = Vec::new();
let mut long_only = Vec::new();
for arg in &args {
if arg.is_positional() {
positional.push(*arg);
} else if arg.get_short().is_some() {
with_short.push(*arg);
} else if arg.get_long().is_some() {
long_only.push(*arg);
}
}
// Check positional args are sorted
let positional_ids: Vec<&str> = positional.iter().map(|a| a.get_id().as_str()).collect();
let mut sorted_positional = positional_ids.clone();
sorted_positional.sort_unstable();
if positional_ids != sorted_positional {
let diff = format_diff(&positional_ids, &sorted_positional);
let error = OrderingError {
command: cmd.get_name().to_string(),
details: format!(
"Positional arguments must be sorted alphabetest_correctly_sorted_subcommands_pass function · rust · L267-L275 (9 LOC)src/clap_sort.rs
fn test_correctly_sorted_subcommands_pass() {
let cli = Command::new("test")
.subcommand(Command::new("alpha"))
.subcommand(Command::new("beta"))
.subcommand(Command::new("zebra"));
// Should not panic
assert_command_order(&cli);
}Powered by Repobility — scan your code at https://repobility.com
test_incorrectly_sorted_subcommands_panic function · rust · L279-L286 (8 LOC)src/clap_sort.rs
fn test_incorrectly_sorted_subcommands_panic() {
let cli = Command::new("test")
.subcommand(Command::new("zebra"))
.subcommand(Command::new("alpha"))
.subcommand(Command::new("beta"));
assert_command_order(&cli);
}test_correctly_sorted_arguments_pass function · rust · L289-L309 (21 LOC)src/clap_sort.rs
fn test_correctly_sorted_arguments_pass() {
let cli = Command::new("test")
.arg(Arg::new("input")) // Positional
.arg(
Arg::new("debug")
.short('d')
.long("debug")
.action(ArgAction::SetTrue),
)
.arg(Arg::new("output").short('o').long("output"))
.arg(
Arg::new("verbose")
.short('v')
.long("verbose")
.action(ArgAction::SetTrue),
)
.arg(Arg::new("age-key-file").long("age-key-file"));
// Should not panic
assert_command_order(&cli);
}test_incorrectly_grouped_arguments_panic function · rust · L313-L325 (13 LOC)src/clap_sort.rs
fn test_incorrectly_grouped_arguments_panic() {
// Long-only flag before short flag (wrong order)
let cli = Command::new("test")
.arg(Arg::new("age-key-file").long("age-key-file"))
.arg(
Arg::new("verbose")
.short('v')
.long("verbose")
.action(ArgAction::SetTrue),
);
assert_command_order(&cli);
}test_short_flags_not_sorted_panic function · rust · L329-L351 (23 LOC)src/clap_sort.rs
fn test_short_flags_not_sorted_panic() {
let cli = Command::new("test")
.arg(
Arg::new("zebra")
.short('z')
.long("zebra")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("alpha")
.short('a')
.long("alpha")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("beta")
.short('b')
.long("beta")
.action(ArgAction::SetTrue),
);
assert_command_order(&cli);
}test_long_only_flags_not_sorted_panic function · rust · L355-L362 (8 LOC)src/clap_sort.rs
fn test_long_only_flags_not_sorted_panic() {
let cli = Command::new("test")
.arg(Arg::new("zebra").long("zebra").action(ArgAction::SetTrue))
.arg(Arg::new("alpha").long("alpha").action(ArgAction::SetTrue))
.arg(Arg::new("beta").long("beta").action(ArgAction::SetTrue));
assert_command_order(&cli);
}test_positional_args_not_sorted_panic function · rust · L366-L372 (7 LOC)src/clap_sort.rs
fn test_positional_args_not_sorted_panic() {
let cli = Command::new("test")
.arg(Arg::new("pos2"))
.arg(Arg::new("pos1"));
assert_command_order(&cli);
}test_recursive_subcommand_validation function · rust · L375-L384 (10 LOC)src/clap_sort.rs
fn test_recursive_subcommand_validation() {
let cli = Command::new("test").subcommand(
Command::new("parent")
.subcommand(Command::new("child-a"))
.subcommand(Command::new("child-z")),
);
// Should not panic - both levels are sorted
assert_command_order(&cli);
}test_recursive_subcommand_validation_fails_deep function · rust · L388-L396 (9 LOC)src/clap_sort.rs
fn test_recursive_subcommand_validation_fails_deep() {
let cli = Command::new("test").subcommand(
Command::new("parent")
.subcommand(Command::new("child-z"))
.subcommand(Command::new("child-a")), // Wrong order
);
assert_command_order(&cli);
}Open data scored by Repobility · https://repobility.com
test_complete_correctly_ordered_cli function · rust · L399-L421 (23 LOC)src/clap_sort.rs
fn test_complete_correctly_ordered_cli() {
let cli = Command::new("test")
.arg(Arg::new("file")) // Positional
.arg(Arg::new("config").short('c').long("config"))
.arg(Arg::new("profile").short('p').long("profile"))
.arg(
Arg::new("verbose")
.short('v')
.long("verbose")
.action(ArgAction::SetTrue),
)
.arg(Arg::new("age-key-file").long("age-key-file"))
.arg(
Arg::new("no-color")
.long("no-color")
.action(ArgAction::SetTrue),
)
.subcommand(Command::new("alpha"))
.subcommand(Command::new("beta"));
// Should not panic - everything is correctly ordered
assert_command_order(&cli);
}test_empty_command function · rust · L424-L429 (6 LOC)src/clap_sort.rs
fn test_empty_command() {
let cli = Command::new("test");
// Should not panic - no subcommands or args to check
assert_command_order(&cli);
}test_only_subcommands function · rust · L432-L440 (9 LOC)src/clap_sort.rs
fn test_only_subcommands() {
let cli = Command::new("test")
.subcommand(Command::new("a"))
.subcommand(Command::new("b"))
.subcommand(Command::new("c"));
// Should not panic
assert_command_order(&cli);
}test_only_args function · rust · L443-L460 (18 LOC)src/clap_sort.rs
fn test_only_args() {
let cli = Command::new("test")
.arg(
Arg::new("alpha")
.short('a')
.long("alpha")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("beta")
.short('b')
.long("beta")
.action(ArgAction::SetTrue),
);
// Should not panic
assert_command_order(&cli);
}run function · rust · L18-L44 (27 LOC)src/commands/activate.rs
pub async fn run(&self) -> Result<()> {
let shell_name = match &self.shell {
Some(s) => s.clone(),
None => shell::detect_shell().ok_or_else(|| {
anyhow::anyhow!(
"Could not detect shell. Please specify shell explicitly: fnox activate <shell>"
)
})?,
};
let shell = shell::get_shell(Some(&shell_name))?;
// Get the current executable path
let exe = std::env::current_exe()
.or_else(|_| which::which("fnox"))
.unwrap_or_else(|_| std::path::PathBuf::from("fnox"));
let opts = ActivateOptions {
exe,
no_hook_env: self.no_hook_env,
};
let activation_code = shell.activate(opts);
print!("{}", activation_code);
Ok(())
}run function · rust · L17-L174 (158 LOC)src/commands/check.rs
pub async fn run(&self, cli: &Cli, config: Config) -> Result<()> {
config.validate()?;
let profile = Config::get_profile(cli.profile.as_deref());
// Load config
println!("Checking configuration for profile: {}", profile);
let mut issues = Vec::new();
let mut warnings = Vec::new();
// Check secrets
if let Ok(secrets) = config.get_secrets(&profile) {
if secrets.is_empty() {
warnings.push("No secrets defined in profile".to_string());
} else {
println!("Found {} secret(s) in profile", secrets.len());
for (name, secret_config) in secrets {
// Check if secret has a value source
if !secret_config.has_value() {
match secret_config.if_missing {
Some(crate::config::IfMissing::Error) => {
issues.push(format!(
run function · rust · L12-L99 (88 LOC)src/commands/ci_redact.rs
pub async fn run(&self, cli: &Cli, config: Config) -> Result<()> {
let profile = Config::get_profile(cli.profile.as_deref());
tracing::debug!("Redacting secrets from profile '{}'", profile);
// Check if we're in CI and get the vendor
let ci_info = ci_info::get();
if !ci_info.ci {
return Err(FnoxError::Config(
"Not running in a CI environment. The ci-redact command is only for CI/CD pipelines.".to_string()
));
}
// Determine the masking format based on CI vendor
let mask_fn: MaskFn = match ci_info.vendor {
Some(ci_info::types::Vendor::GitHubActions) => {
Box::new(|key: &str, value: &str| {
// GitHub Actions doesn't properly handle multiline secrets
// Warn the user instead of attempting to mask
if value.contains('\n') {
tracing::warn!(
"Secrrun function · rust · L15-L41 (27 LOC)src/commands/completion.rs
pub async fn run(&self, _cli: &Cli) -> Result<()> {
let output = Command::new("usage")
.args([
"g",
"completion",
&self.shell,
"fnox",
"--usage-cmd",
"fnox usage",
"--cache-key",
env!("CARGO_PKG_VERSION"),
])
.output()?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(crate::error::FnoxError::Config(format!(
"Failed to generate completions: {}",
stderr
)));
}
let stdout = String::from_utf8_lossy(&output.stdout);
print!("{}", stdout);
Ok(())
}Provenance: Repobility (https://repobility.com) — every score reproducible from /scan/
run function · rust · L21-L39 (19 LOC)src/commands/config_files.rs
pub async fn run(&self, _cli: &Cli) -> Result<()> {
let profile = crate::settings::Settings::get().profile.clone();
let filenames = all_config_filenames(Some(&profile));
let current_dir = env::current_dir().map_err(|e| {
crate::error::FnoxError::Config(format!("Failed to get current directory: {}", e))
})?;
let mut printed = HashSet::new();
self.collect_recursive(¤t_dir, &filenames, &mut printed)?;
// Global config is always checked
let global = Config::global_config_path();
if global.exists() && printed.insert(global.clone()) {
println!("{}", global.display());
}
Ok(())
}collect_recursive function · rust · L40-L85 (46 LOC)src/commands/config_files.rs
fn collect_recursive(
&self,
dir: &Path,
filenames: &[String],
printed: &mut HashSet<PathBuf>,
) -> Result<()> {
let mut found_root = false;
for filename in filenames {
let path = dir.join(filename);
if path.exists() && printed.insert(path.clone()) {
println!("{}", path.display());
if let Ok(content) = std::fs::read_to_string(&path)
&& let Ok(partial) = toml_edit::de::from_str::<PartialConfig>(&content)
{
// Print imported config files
for import_path in &partial.import {
let import = if Path::new(import_path).is_absolute() {
PathBuf::from(import_path)
} else {
dir.join(import_path)
};
if import.exists() && printed.insert(import.clone()) {
run function · rust · L21-L41 (21 LOC)src/commands/deactivate.rs
pub async fn run(&self, _cli: &Cli, _config: Config) -> Result<()> {
// Check if fnox is activated in the current shell
if std::env::var("FNOX_SHELL").is_err() {
anyhow::bail!(
"fnox is not activated in this shell session.\n\
Run the activation command for your shell to enable fnox."
);
}
let shell = shell::get_shell(None)?;
// First, restore the original environment (unset all loaded secrets)
let output = clear_old_env(&*shell);
print!("{}", output);
// Then output shell-specific deactivation commands
let deactivate_output = shell.deactivate();
print!("{}", deactivate_output);
Ok(())
}clear_old_env function · rust · L45-L56 (12 LOC)src/commands/deactivate.rs
fn clear_old_env(shell: &dyn shell::Shell) -> String {
// Get the previous session state (if any)
let prev_session = &*PREV_SESSION;
// Unset all loaded secrets from the previous session
let mut output = String::new();
for key in prev_session.secret_hashes.keys() {
output.push_str(&shell.unset_env(key));
}
output
}run function · rust · L18-L89 (72 LOC)src/commands/decrypt.rs
pub async fn run(&self, cli: &Cli, mut config: Config) -> Result<()> {
tracing::debug!("Decrypting configuration file");
if config.encryption.is_none() {
return Err(FnoxError::EncryptionNotConfigured);
}
let encryption_config = config.encryption.as_ref().unwrap();
tracing::debug!("Using encryption type: {}", encryption_config.key_type);
if encryption_config.key_type != "age" {
return Err(FnoxError::UnsupportedEncryptionType {
encryption_type: encryption_config.key_type.clone(),
});
}
if encryption_config.encrypted_data.is_none() {
println!("No encrypted data found. Configuration may already be decrypted.");
return Ok(());
}
// Determine identity file path
let identity_path = if let Some(ref key_path) = self.key {
key_path.clone()
} else if let Some(env_key) = (*env::FNOX_AGE_KEY).clone() {
decrypt_with_encryptor function · rust · L90-L138 (49 LOC)src/commands/decrypt.rs
async fn decrypt_with_encryptor(
&self,
cli: &Cli,
mut config: Config,
decryptor: AgeEncryptor,
) -> Result<()> {
let encryption_config = config.encryption.as_ref().unwrap();
let encrypted_data = encryption_config.encrypted_data.as_ref().unwrap();
// Base64 decode
use base64::Engine;
let ciphertext = base64::engine::general_purpose::STANDARD
.decode(encrypted_data)
.map_err(|e| FnoxError::AgeDecryptionFailed {
details: format!("Failed to decode encrypted data: {}", e),
})?;
// Decrypt
let plaintext = decryptor.decrypt(&ciphertext).await.map_err(|e| {
FnoxError::AgeDecryptionFailed {
details: e.to_string(),
}
})?;
// Deserialize secrets
let secrets: HashMap<String, SecretValue> =
serde_json::from_slice(&plaintext).map_err(|e| FnoxError::AgeDecryptionFailed {
run function · rust · L13-L130 (118 LOC)src/commands/doctor.rs
pub async fn run(&self, cli: &Cli, config: Config) -> Result<()> {
let profile = Config::get_profile(cli.profile.as_deref());
println!("🏥 Fnox Doctor Report");
println!("====================");
println!();
// Config file info
println!("📄 Configuration:");
println!(" File: fnox.toml");
println!(" Profile: {}", profile);
config.validate()?;
println!(" Status: ✓ Loaded successfully");
println!();
// Secrets info
println!("🔐 Secrets:");
match config.get_secrets(&profile) {
Ok(secrets) => {
println!(" Count: {}", secrets.len());
if !secrets.is_empty() {
let mut with_values = 0;
let mut required = 0;
let mut with_providers = 0;
for secret in secrets.values() {
if secret.has_value() {
with_values run function · rust · L49-L182 (134 LOC)src/commands/edit.rs
pub async fn run(&self, cli: &Cli, config: Config) -> Result<()> {
let profile = Config::get_profile(cli.profile.as_deref());
tracing::debug!("Starting enhanced edit with profile: {}", profile);
// Step 1: Load raw TOML with toml_edit to preserve formatting
let toml_content =
fs::read_to_string(&cli.config).map_err(|source| FnoxError::ConfigReadFailed {
path: cli.config.clone(),
source,
})?;
let doc = toml_content
.parse::<DocumentMut>()
.map_err(|e| FnoxError::Config(format!("Failed to parse TOML: {}", e)))?;
// Step 2: Collect all secrets from all profiles
let mut all_secrets = Vec::new();
// Collect secrets from top-level [secrets] section
if !config.secrets.is_empty() {
self.collect_secrets(&config, "default", &config.secrets, &mut all_secrets)
.await?;
}
// Collect secrets from alRepobility · severity-and-effort ranking · https://repobility.com
collect_secrets function · rust · L185-L228 (44 LOC)src/commands/edit.rs
async fn collect_secrets(
&self,
config: &Config,
profile: &str,
secrets: &IndexMap<String, SecretConfig>,
all_secrets: &mut Vec<SecretEntry>,
) -> Result<()> {
for (key, secret_config) in secrets {
// Determine provider and check if read-only
let provider_name = if let Some(prov) = secret_config.provider() {
Some(prov.to_string())
} else {
config.get_default_provider(profile)?
};
let (is_read_only, resolved_provider_name) = if let Some(ref prov_name) = provider_name
{
let providers = config.get_providers(profile);
if let Some(provider_config) = providers.get(prov_name) {
let provider =
get_provider_resolved(config, profile, prov_name, provider_config).await?;
let capabilities = provider.capabilities();
let is_reacreate_decrypted_temp_file function · rust · L231-L303 (73 LOC)src/commands/edit.rs
fn create_decrypted_temp_file(
&self,
doc: &DocumentMut,
all_secrets: &[SecretEntry],
) -> Result<NamedTempFile> {
let mut temp_file = tempfile::Builder::new()
.suffix(".toml")
.tempfile()
.map_err(|e| FnoxError::Config(format!("Failed to create temporary file: {}", e)))?;
// Set restrictive permissions (Unix only)
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = temp_file
.as_file()
.metadata()
.map_err(|e| FnoxError::Config(format!("Failed to get file metadata: {}", e)))?
.permissions();
perms.set_mode(0o600);
temp_file
.as_file()
.set_permissions(perms)
.map_err(|e| FnoxError::Config(format!("Failed to set file permissions: {}", e)))?;
}
// Clone the document and replace encrypted values wreencrypt_secrets function · rust · L337-L385 (49 LOC)src/commands/edit.rs
async fn reencrypt_secrets(
&self,
config: &Config,
modified_doc: &mut DocumentMut,
all_secrets: &[SecretEntry],
) -> Result<()> {
// Create a map of secrets by (profile, key) to avoid collisions
let secrets_map: HashMap<_, _> = all_secrets
.iter()
.map(|s| ((s.profile.clone(), s.key.clone()), s))
.collect();
// Process [secrets] section
if let Some(secrets_table) = modified_doc
.get_mut("secrets")
.and_then(|item| item.as_table_mut())
{
self.reencrypt_secrets_table(config, secrets_table, "default", &secrets_map)
.await?;
}
// Process [profiles.*] sections
if let Some(profiles_table) = modified_doc
.get_mut("profiles")
.and_then(|item| item.as_table_mut())
{
// Collect profile names first to avoid borrow issues
let profile_names: Vec<_> = pset_secret_value function · rust · L528-L534 (7 LOC)src/commands/edit.rs
fn set_secret_value(item: &mut toml_edit::Item, value: &str) {
if let Some(inline_table) = item.as_inline_table_mut() {
inline_table.insert("value", Value::from(value));
} else if let Some(table) = item.as_table_mut() {
table.insert("value", toml_edit::value(value));
}
}strip_temp_header function · rust · L537-L544 (8 LOC)src/commands/edit.rs
fn strip_temp_header(content: &str) -> String {
// Only strip if the content starts with our exact header
// This avoids accidentally removing user comments that happen to match patterns
content
.strip_prefix(TEMP_FILE_HEADER)
.unwrap_or(content)
.to_string()
}run function · rust · L16-L84 (69 LOC)src/commands/encrypt.rs
pub async fn run(&self, cli: &Cli, mut config: Config) -> Result<()> {
tracing::debug!("Encrypting configuration file");
if config.encryption.is_none() {
return Err(FnoxError::EncryptionNotConfigured);
}
let encryption_config = config.encryption.as_ref().unwrap();
tracing::debug!("Using encryption type: {}", encryption_config.key_type);
if encryption_config.key_type != "age" {
return Err(FnoxError::UnsupportedEncryptionType {
encryption_type: encryption_config.key_type.clone(),
});
}
// Check if already encrypted
if encryption_config.encrypted_data.is_some() {
println!("Configuration is already encrypted.");
return Ok(());
}
// Get recipients
let recipients = if encryption_config.recipients.is_empty() {
return Err(FnoxError::AgeNotConfigured);
} else {
encryption_config.recipirun function · rust · L18-L89 (72 LOC)src/commands/exec.rs
pub async fn run(&self, cli: &Cli, config: Config) -> Result<()> {
if self.command.is_empty() {
return Err(FnoxError::CommandNotSpecified);
}
let profile = Config::get_profile(cli.profile.as_deref());
tracing::debug!("Running command with secrets from profile '{}'", profile);
// Get the profile secrets
let profile_secrets = config.get_secrets(&profile)?;
let mut cmd = Command::new(&self.command[0]);
if self.command.len() > 1 {
cmd.args(&self.command[1..]);
}
// Resolve secrets using batch resolution for better performance
let resolved_secrets = resolve_secrets_batch(&config, &profile, &profile_secrets).await?;
// Keep temp files alive for the duration of the command
let mut _temp_files: Vec<NamedTempFile> = Vec::new();
// Add resolved secrets as environment variables
for (key, value) in resolved_secrets {
if let Some(valuerun function · rust · L58-L146 (89 LOC)src/commands/export.rs
pub async fn run(&self, cli: &Cli, config: Config) -> Result<()> {
let profile = Config::get_profile(cli.profile.as_deref());
tracing::debug!("Exporting secrets from profile '{}'", profile);
let profile_secrets = config.get_secrets(&profile)?;
// Resolve secrets using batch resolution for better performance
let resolved_secrets = resolve_secrets_batch(&config, &profile, &profile_secrets).await?;
// Build secrets map, preserving insertion order
// For file-based secrets, create persistent temp files
let mut secrets = IndexMap::new();
for (key, value_opt) in resolved_secrets {
if let Some(value) = value_opt {
// Check if this secret should be file-based
if let Some(secret_config) = profile_secrets.get(&key) {
if secret_config.as_file {
// Create a persistent temp file for this secret
match create_perPowered by Repobility — scan your code at https://repobility.com
export_as_env function · rust · L147-L163 (17 LOC)src/commands/export.rs
fn export_as_env(&self, data: &ExportData) -> Result<String> {
let mut output = String::new();
if let Some(metadata) = &data.metadata {
output.push_str(&format!("# Exported from profile: {}\n", metadata.profile));
output.push_str(&format!("# Exported at: {}\n", metadata.exported_at));
output.push_str(&format!("# Total secrets: {}\n", metadata.total_secrets));
output.push('\n');
}
for (key, value) in &data.secrets {
output.push_str(&format!("export {}='{}'\n", key, value));
}
Ok(output)
}run function · rust · L15-L58 (44 LOC)src/commands/get.rs
pub async fn run(&self, cli: &Cli, config: Config) -> Result<()> {
let profile = Config::get_profile(cli.profile.as_deref());
tracing::debug!("Getting secret '{}' from profile '{}'", self.key, profile);
// Validate the configuration first
config.validate()?;
// Get the profile secrets
let profile_secrets = config.get_secrets(&profile)?;
// Get the secret config
let secret_config = profile_secrets.get(&self.key).ok_or_else(|| {
// Find similar secret names for suggestion
let available_keys: Vec<_> = profile_secrets.keys().map(|s| s.as_str()).collect();
let similar = find_similar(&self.key, available_keys);
let suggestion = format_suggestions(&similar);
FnoxError::SecretNotFound {
key: self.key.clone(),
profile: profile.clone(),
config_path: config.secret_sources.get(&self.key).cloned(),
suggestfrom_string function · rust · L20-L26 (7 LOC)src/commands/hook_env.rs
fn from_string(s: &str) -> Self {
match s.to_lowercase().as_str() {
"none" | "off" | "false" | "0" => Self::None,
"debug" | "verbose" => Self::Debug,
_ => Self::Normal, // default to normal
}
}run function · rust · L46-L142 (97 LOC)src/commands/hook_env.rs
pub async fn run(&self) -> Result<()> {
// Get settings for output mode
let settings =
Settings::try_get().map_err(|e| anyhow::anyhow!("Failed to get settings: {}", e))?;
let output_mode = OutputMode::from_string(&settings.shell_integration_output);
// Detect shell
let shell_name = match &self.shell {
Some(s) => s.clone(),
None => shell::detect_shell().unwrap_or_else(|| "bash".to_string()),
};
let shell = shell::get_shell(Some(&shell_name))?;
if output_mode.should_show_debug() {
eprintln!(
"fnox: hook-env running in {:?}",
std::env::current_dir().ok()
);
}
// Check if we can exit early (optimization)
if hook_env::should_exit_early() {
if output_mode.should_show_debug() {
eprintln!("fnox: early exit - no changes detected");
}
// Nothing changed, no oucalculate_changes function · rust · L146-L178 (33 LOC)src/commands/hook_env.rs
fn calculate_changes(
old_hashes: &indexmap::IndexMap<String, String>,
new_secrets: &HashMap<String, String>,
) -> (Vec<(String, String)>, Vec<String>) {
use crate::hook_env::{PREV_SESSION, hash_secret_value_with_session};
let mut added = Vec::new();
let mut removed = Vec::new();
// Find additions and changes by comparing hashes
for (key, new_value) in new_secrets {
// Use the previous session's hash_key for comparison
let new_hash = hash_secret_value_with_session(&PREV_SESSION, key, new_value);
match old_hashes.get(key) {
Some(old_hash) if old_hash == &new_hash => {
// Hash matches, no change
}
_ => {
// New or changed value (hash differs or key is new)
added.push((key.clone(), new_value.clone()));
}
}
}
// Find removals - keys that were in old session but not in new
for key in old_hashes.keys() {
if !new_secload_secrets_from_config function · rust · L189-L285 (97 LOC)src/commands/hook_env.rs
async fn load_secrets_from_config() -> Result<LoadedSecrets> {
use crate::secret_resolver::resolve_secrets_batch;
// Use load_smart to ensure provider inheritance from parent configs
// This handles fnox.toml and fnox.local.toml with proper recursion
let settings =
Settings::try_get().map_err(|e| anyhow::anyhow!("Failed to get settings: {}", e))?;
let filenames = crate::config::all_config_filenames(Some(&settings.profile));
let mut last_error = None;
let mut config = None;
for filename in &filenames {
match Config::load_smart(filename) {
Ok(c) => {
config = Some(c);
break;
}
Err(e) => {
// Only store parse errors (not "file not found" errors)
// to show detailed error messages for actual config issues
let is_not_found = matches!(&e, crate::error::FnoxError::ConfigNotFound { .. });
if !is_not_found {
cleanup_old_temp_files function · rust · L288-L303 (16 LOC)src/commands/hook_env.rs
fn cleanup_old_temp_files(
old_files: &HashMap<String, String>,
new_files: &HashMap<String, String>,
) {
for (key, old_path) in old_files {
// Only delete if this secret is no longer file-based or has a different path
if !new_files.contains_key(key) || new_files.get(key) != Some(old_path) {
if let Err(e) = fs::remove_file(old_path) {
// Log but don't fail - file might already be deleted
tracing::debug!("failed to clean up temp file for '{}': {}", key, e);
} else {
tracing::debug!("cleaned up temp file for secret '{}'", key);
}
}
}
}run function · rust · L66-L257 (192 LOC)src/commands/import.rs
pub async fn run(&self, cli: &Cli, merged_config: Config) -> Result<()> {
let profile = Config::get_profile(cli.profile.as_deref());
tracing::debug!(
"Importing secrets in {} format into profile '{}'",
self.format,
profile
);
let input = self.read_input()?;
let mut secrets = self.parse_input(&input)?;
// When importing from stdin, --force or --dry-run is required because stdin is consumed
// by read_input() and won't be available for the confirmation prompt
// (dry-run doesn't need confirmation since it doesn't modify anything)
if self.input.is_none() && !self.force && !self.dry_run {
return Err(FnoxError::ImportStdinRequiresForce);
}
// Apply filter if specified
if let Some(ref filter) = self.filter {
let regex = Regex::new(filter).map_err(|e| FnoxError::InvalidRegexFilter {
pattern: filter.clone(),
Open data scored by Repobility · https://repobility.com
read_input function · rust · L258-L276 (19 LOC)src/commands/import.rs
fn read_input(&self) -> Result<String> {
if let Some(ref input_path) = self.input {
// Read from specified file
let input =
std::fs::read_to_string(input_path).map_err(|e| FnoxError::ImportReadFailed {
path: input_path.clone(),
source: e,
})?;
Ok(input)
} else {
// Read from stdin
let mut input = String::new();
io::stdin()
.read_to_string(&mut input)
.map_err(|source| FnoxError::StdinReadFailed { source })?;
Ok(input)
}
}parse_input function · rust · L277-L291 (15 LOC)src/commands/import.rs
fn parse_input(&self, input: &str) -> Result<HashMap<String, String>> {
let source_name = self
.input
.as_ref()
.map(|p| p.display().to_string())
.unwrap_or_else(|| "<stdin>".to_string());
match self.format {
ImportFormat::Env => self.parse_env(input),
ImportFormat::Json => self.parse_json(input, &source_name),
ImportFormat::Yaml => self.parse_yaml(input, &source_name),
ImportFormat::Toml => self.parse_toml(input, &source_name),
}
}parse_env function · rust · L292-L313 (22 LOC)src/commands/import.rs
fn parse_env(&self, input: &str) -> Result<HashMap<String, String>> {
let mut secrets = HashMap::new();
for line in input.lines() {
let line = line.trim();
// Skip empty lines and comments
if line.is_empty() || line.starts_with('#') {
continue;
}
// Parse export statements and simple KEY=VALUE
if let Some(export_key_value) = line.strip_prefix("export ") {
self.parse_key_value(export_key_value, &mut secrets)?;
} else {
self.parse_key_value(line, &mut secrets)?;
}
}
Ok(secrets)
}page 1 / 8next ›