rust-apk 1.0.0

Build, align, sign, verify, and inspect Android APKs in pure Rust
Documentation
//! Atomic file output: write to a temporary file beside the target, then
//! rename it into place, so readers never observe a partial APK.

use std::fs::File;
use std::path::Path;

use tempfile::NamedTempFile;

use crate::error::{Error, FileOperation};

pub(crate) struct Staged {
    temp: NamedTempFile,
}

impl Staged {
    /// A temporary file in the directory that will contain `target`.
    pub fn beside(target: &Path) -> Result<Self, Error> {
        let dir = target.parent().filter(|p| !p.as_os_str().is_empty()).unwrap_or(Path::new("."));
        let temp = tempfile::Builder::new()
            .prefix(".apk-rs-")
            .suffix(".tmp")
            .tempfile_in(dir)
            .map_err(Error::file(FileOperation::Create, dir))?;
        Ok(Self { temp })
    }

    pub fn file(&mut self) -> &mut File {
        self.temp.as_file_mut()
    }

    pub fn path(&self) -> &Path {
        self.temp.path()
    }

    /// Flushes the file to disk and moves it to `target` with mode `0644`.
    pub fn persist(self, target: &Path) -> Result<(), Error> {
        let file = self.temp.as_file();
        #[cfg(unix)]
        {
            use std::os::unix::fs::PermissionsExt;
            file.set_permissions(std::fs::Permissions::from_mode(0o644))
                .map_err(Error::file(FileOperation::Write, self.temp.path()))?;
        }
        file.sync_all().map_err(Error::file(FileOperation::Write, self.temp.path()))?;
        self.temp.persist(target).map_err(|error| Error::File {
            operation: FileOperation::Persist,
            path: target.to_owned(),
            source: error.error,
        })?;
        Ok(())
    }
}