syd 3.58.0

rock-solid application kernel
Documentation
//
// Syd: rock-solid application kernel
// src/t/mktemp.rs: Temporary file support
//
// Copyright (c) 2026 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: GPL-3.0

#![allow(dead_code)]
#![allow(clippy::disallowed_methods)]
#![allow(clippy::disallowed_types)]

use std::{
    fs::{self, File, Permissions},
    io::{self, Write},
    path::{Path, PathBuf},
};

use nix::unistd::{mkdtemp, mkstemp};

fn template(dir: &Path, prefix: &str) -> PathBuf {
    dir.join(format!("{prefix}XXXXXX"))
}

/// Create an anonymous temporary file, unlinked immediately.
pub fn tempfile() -> io::Result<File> {
    let (fd, path) = mkstemp(&template(&std::env::temp_dir(), ".tmp"))?;
    let _ = fs::remove_file(&path);
    Ok(File::from(fd))
}

/// Create a temporary directory in the system temporary directory.
pub fn tempdir() -> io::Result<TempDir> {
    Builder::new().tempdir()
}

/// A named temporary file, removed on drop.
pub struct NamedTempFile {
    file: File,
    path: PathBuf,
}

impl NamedTempFile {
    /// Create a named temporary file in the system temporary directory.
    pub fn new() -> io::Result<NamedTempFile> {
        let (fd, path) = mkstemp(&template(&std::env::temp_dir(), ".tmp"))?;
        Ok(NamedTempFile {
            file: File::from(fd),
            path,
        })
    }

    /// Path of the temporary file.
    pub fn path(&self) -> &Path {
        &self.path
    }

    /// Reference to the underlying file.
    pub fn as_file(&self) -> &File {
        &self.file
    }
}

impl Write for NamedTempFile {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.file.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.file.flush()
    }
}

impl Drop for NamedTempFile {
    fn drop(&mut self) {
        let _ = fs::remove_file(&self.path);
    }
}

/// A temporary directory, removed recursively on drop.
pub struct TempDir {
    path: PathBuf,
    keep: bool,
}

impl TempDir {
    /// Path of the temporary directory.
    pub fn path(&self) -> &Path {
        &self.path
    }

    /// Persist the directory (disable cleanup) and return its path.
    pub fn keep(mut self) -> PathBuf {
        self.keep = true;
        self.path.clone()
    }
}

impl Drop for TempDir {
    fn drop(&mut self) {
        if !self.keep {
            let _ = fs::remove_dir_all(&self.path);
        }
    }
}

/// Builder for temporary directories.
#[derive(Default)]
pub struct Builder {
    prefix: Option<String>,
    permissions: Option<Permissions>,
    keep: bool,
}

impl Builder {
    /// Create a new builder.
    pub fn new() -> Builder {
        Builder::default()
    }

    /// Set the name prefix.
    pub fn prefix(mut self, prefix: &str) -> Builder {
        self.prefix = Some(prefix.to_owned());
        self
    }

    /// Set the permissions of the created directory.
    pub fn permissions(mut self, permissions: Permissions) -> Builder {
        self.permissions = Some(permissions);
        self
    }

    /// Disable RAII cleanup of the created directory.
    pub fn disable_cleanup(mut self, disable: bool) -> Builder {
        self.keep = disable;
        self
    }

    /// Create a temporary directory in the system temporary directory.
    pub fn tempdir(self) -> io::Result<TempDir> {
        let dir = std::env::temp_dir();
        self.make(&dir)
    }

    /// Create a temporary directory inside `dir`.
    pub fn tempdir_in<P: AsRef<Path>>(self, dir: P) -> io::Result<TempDir> {
        self.make(dir.as_ref())
    }

    fn make(self, dir: &Path) -> io::Result<TempDir> {
        let prefix = self.prefix.as_deref().unwrap_or(".tmp");
        let path = mkdtemp(&template(dir, prefix))?;
        if let Some(permissions) = self.permissions {
            fs::set_permissions(&path, permissions)?;
        }
        Ok(TempDir {
            path,
            keep: self.keep,
        })
    }
}