use super::*;
pub(crate) fn record_run_selection(state: &mut AppState, profile_name: &str) {
state
.last_run_selected_at
.retain(|name, _| state.profiles.contains_key(name));
state
.last_run_selected_at
.insert(profile_name.to_string(), Local::now().timestamp());
}
pub(crate) fn resolve_profile_name(state: &AppState, requested: Option<&str>) -> Result<String> {
if let Some(name) = requested {
if state.profiles.contains_key(name) {
return Ok(name.to_string());
}
bail!("profile '{}' does not exist", name);
}
if let Some(active) = state.active_profile.as_deref() {
if state.profiles.contains_key(active) {
return Ok(active.to_string());
}
bail!("active profile '{}' no longer exists", active);
}
if state.profiles.len() == 1 {
let (name, _) = state
.profiles
.iter()
.next()
.context("single profile lookup failed unexpectedly")?;
return Ok(name.clone());
}
bail!("no active profile selected; use `prodex use --profile <name>` or pass --profile")
}
pub(crate) fn ensure_path_is_unique(state: &AppState, candidate: &Path) -> Result<()> {
for (name, profile) in &state.profiles {
if same_path(&profile.codex_home, candidate) {
bail!(
"path {} is already used by profile '{}'",
candidate.display(),
name
);
}
}
Ok(())
}
pub(crate) fn validate_profile_name(name: &str) -> Result<()> {
if name.is_empty() {
bail!("profile name cannot be empty");
}
if name.contains(std::path::MAIN_SEPARATOR) {
bail!("profile name cannot contain path separators");
}
if name == "." || name == ".." {
bail!("profile name cannot be '.' or '..'");
}
if !name
.chars()
.all(|ch| ch.is_ascii_alphanumeric() || matches!(ch, '-' | '_' | '.'))
{
bail!("profile name may only contain letters, numbers, '.', '_' or '-'");
}
Ok(())
}
pub(crate) fn should_enable_runtime_rotation_proxy(
state: &AppState,
selected_profile_name: &str,
allow_auto_rotate: bool,
) -> bool {
if !allow_auto_rotate || state.profiles.len() <= 1 {
return false;
}
let Some(selected_profile) = state.profiles.get(selected_profile_name) else {
return false;
};
if !selected_profile.codex_home.exists() {
return false;
}
state.profiles.values().any(|profile| {
profile
.provider
.auth_summary(&profile.codex_home)
.quota_compatible
})
}