prodex 0.32.0

OpenAI profile pooling and safe auto-rotate for Codex CLI and Claude Code
Documentation
use super::*;

pub(super) fn ensure_shared_codex_parent_dir(path: &Path) -> Result<()> {
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)
            .with_context(|| format!("failed to create {}", parent.display()))?;
    }
    Ok(())
}

pub(super) fn load_shared_codex_entry_metadata(path: &Path) -> Result<Option<fs::Metadata>> {
    match fs::symlink_metadata(path) {
        Ok(metadata) => Ok(Some(metadata)),
        Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
        Err(err) => Err(err).with_context(|| format!("failed to inspect {}", path.display())),
    }
}

fn ensure_shared_codex_directory(path: &Path, metadata: &fs::Metadata) -> Result<()> {
    if metadata.is_dir() {
        return Ok(());
    }
    bail!(
        "expected {} to be a directory for shared Codex state",
        path.display()
    );
}

fn ensure_shared_codex_file(path: &Path, metadata: &fs::Metadata) -> Result<()> {
    if metadata.is_file() {
        return Ok(());
    }
    bail!(
        "expected {} to be a file for shared Codex state",
        path.display()
    );
}

fn ensure_shared_codex_path_is_directory(path: &Path) -> Result<()> {
    if path.is_dir() {
        return Ok(());
    }
    bail!(
        "expected {} to be a directory for shared Codex state",
        path.display()
    );
}

fn ensure_shared_codex_path_is_file(path: &Path) -> Result<()> {
    if path.is_file() {
        return Ok(());
    }
    bail!(
        "expected {} to be a file for shared Codex state",
        path.display()
    );
}

fn ensure_shared_codex_target_is_directory(path: &Path) -> Result<()> {
    if path.is_dir() {
        return Ok(());
    }
    bail!(
        "expected {} to be a directory for shared Codex state",
        path.display()
    );
}

fn ensure_shared_codex_target_is_file(path: &Path) -> Result<()> {
    if path.is_file() {
        return Ok(());
    }
    bail!(
        "expected {} to be a file for shared Codex state",
        path.display()
    );
}

pub(super) fn copy_shared_codex_file(source: &Path, destination: &Path) -> Result<()> {
    fs::copy(source, destination).with_context(|| {
        format!(
            "failed to copy {} to {}",
            source.display(),
            destination.display()
        )
    })?;
    Ok(())
}

pub(super) fn copy_legacy_shared_codex_file(source: &Path, destination: &Path) -> Result<()> {
    fs::copy(source, destination).with_context(|| {
        format!(
            "failed to copy legacy shared Codex file {} to {}",
            source.display(),
            destination.display()
        )
    })?;
    Ok(())
}

pub(super) fn move_directory(source: &Path, destination: &Path) -> Result<()> {
    if let Some(parent) = destination.parent() {
        fs::create_dir_all(parent)
            .with_context(|| format!("failed to create {}", parent.display()))?;
    }

    match fs::rename(source, destination) {
        Ok(()) => Ok(()),
        Err(_) => {
            create_codex_home_if_missing(destination)?;
            copy_directory_contents(source, destination)?;
            fs::remove_dir_all(source)
                .with_context(|| format!("failed to remove {}", source.display()))
        }
    }
}

pub(super) fn move_file(source: &Path, destination: &Path) -> Result<()> {
    if let Some(parent) = destination.parent() {
        fs::create_dir_all(parent)
            .with_context(|| format!("failed to create {}", parent.display()))?;
    }

    match fs::rename(source, destination) {
        Ok(()) => Ok(()),
        Err(_) => {
            fs::copy(source, destination).with_context(|| {
                format!(
                    "failed to copy {} to {}",
                    source.display(),
                    destination.display()
                )
            })?;
            fs::remove_file(source)
                .with_context(|| format!("failed to remove {}", source.display()))
        }
    }
}

pub(super) fn ensure_symlink_to_shared(
    local_path: &Path,
    shared_path: &Path,
    kind: SharedCodexEntryKind,
) -> Result<()> {
    if let Some(parent) = local_path.parent() {
        create_codex_home_if_missing(parent)?;
    }
    if local_path.exists() || fs::symlink_metadata(local_path).is_ok() {
        remove_path(local_path)?;
    }

    create_symlink(shared_path, local_path, kind)
}

fn create_symlink(target: &Path, link: &Path, kind: SharedCodexEntryKind) -> Result<()> {
    #[cfg(unix)]
    {
        let _ = kind;
        std::os::unix::fs::symlink(target, link).with_context(|| {
            format!(
                "failed to link shared Codex state {} -> {}",
                link.display(),
                target.display()
            )
        })?;
    }

    #[cfg(windows)]
    {
        match kind {
            SharedCodexEntryKind::Directory => std::os::windows::fs::symlink_dir(target, link),
            SharedCodexEntryKind::File => std::os::windows::fs::symlink_file(target, link),
        }
        .with_context(|| {
            format!(
                "failed to link shared Codex state {} -> {}",
                link.display(),
                target.display()
            )
        })?;
    }

    #[cfg(not(any(unix, windows)))]
    {
        let _ = kind;
        bail!("shared Codex session links are not supported on this platform");
    }

    Ok(())
}

pub(super) fn remove_path(path: &Path) -> Result<()> {
    let metadata = fs::symlink_metadata(path)
        .with_context(|| format!("failed to inspect {}", path.display()))?;
    let file_type = metadata.file_type();

    if file_type.is_symlink() {
        fs::remove_file(path)
            .or_else(|_| fs::remove_dir(path))
            .with_context(|| format!("failed to remove symbolic link {}", path.display()))?;
        return Ok(());
    }

    if metadata.is_dir() {
        fs::remove_dir_all(path).with_context(|| format!("failed to remove {}", path.display()))?;
    } else {
        fs::remove_file(path).with_context(|| format!("failed to remove {}", path.display()))?;
    }

    Ok(())
}

pub(crate) fn create_codex_home_if_missing(path: &Path) -> Result<()> {
    fs::create_dir_all(path).with_context(|| format!("failed to create {}", path.display()))?;
    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        let permissions = fs::Permissions::from_mode(0o700);
        let _ = fs::set_permissions(path, permissions);
    }
    Ok(())
}

pub(super) fn dir_is_empty(path: &Path) -> Result<bool> {
    if !path.exists() {
        return Ok(true);
    }
    let mut entries =
        fs::read_dir(path).with_context(|| format!("failed to read {}", path.display()))?;
    Ok(entries.next().is_none())
}

pub(super) fn ensure_shared_codex_directory_public(
    path: &Path,
    metadata: &fs::Metadata,
) -> Result<()> {
    ensure_shared_codex_directory(path, metadata)
}

pub(super) fn ensure_shared_codex_file_public(path: &Path, metadata: &fs::Metadata) -> Result<()> {
    ensure_shared_codex_file(path, metadata)
}

pub(super) fn ensure_shared_codex_path_is_directory_public(path: &Path) -> Result<()> {
    ensure_shared_codex_path_is_directory(path)
}

pub(super) fn ensure_shared_codex_path_is_file_public(path: &Path) -> Result<()> {
    ensure_shared_codex_path_is_file(path)
}

pub(super) fn ensure_shared_codex_target_is_directory_public(path: &Path) -> Result<()> {
    ensure_shared_codex_target_is_directory(path)
}

pub(super) fn ensure_shared_codex_target_is_file_public(path: &Path) -> Result<()> {
    ensure_shared_codex_target_is_file(path)
}