Skip to main content

wowlab_fs/
executable.rs

1//! Durable creation of executable fixtures and helper programs.
2
3use std::io::Write as _;
4
5use crate::{
6    error::{Error, Operation, Result},
7    file,
8    path::Path,
9};
10
11/// Create a new executable file with fully synchronized contents.
12///
13/// Existing entries are never replaced.
14/// On Unix, owner, group, and other users receive executable permissions.
15/// On other platforms, executable selection is platform-defined.
16/// Those platforms typically use the filename extension.
17/// The file is still created and synchronized without permission changes.
18///
19/// # Errors
20///
21/// Returns a contextual error when the path already exists.
22/// Content, permission, and metadata persistence failures are also reported.
23pub fn create(path: &Path, contents: impl AsRef<[u8]>) -> Result<()> {
24    let mut executable = file::create_new(path)?;
25
26    executable
27        .write_all(contents.as_ref())
28        .map_err(|source| Error::new(Operation::WriteFile, path, source))?;
29    executable
30        .flush()
31        .map_err(|source| Error::new(Operation::WriteFile, path, source))?;
32    executable.make_executable()?;
33
34    executable.sync()
35}
36
37#[cfg(test)]
38mod tests {
39    use googletest::prelude::*;
40
41    use super::create;
42    use crate::{file, temporary::Directory};
43
44    #[gtest]
45    fn create_persists_exact_bytes_without_replacing_existing_file() -> Result<()> {
46        let directory = Directory::new().or_fail()?;
47        let path = directory.path().join("fixture");
48
49        create(&path, b"first").or_fail()?;
50
51        verify_that!(file::read_bytes(&path).or_fail()?, eq(b"first"))?;
52        verify_true!(create(&path, b"second").is_err())?;
53
54        verify_that!(file::read_bytes(&path).or_fail()?, eq(b"first"))
55    }
56
57    #[cfg(unix)]
58    #[gtest]
59    fn create_marks_unix_script_executable() -> Result<()> {
60        let directory = Directory::new().or_fail()?;
61        let path = directory.path().join("fixture");
62
63        create(&path, b"#!/bin/sh\nprintf executable\n").or_fail()?;
64        let output = std::process::Command::new(&path).output().or_fail()?;
65
66        verify_true!(output.status.success())?;
67
68        verify_that!(output.stdout, eq(b"executable"))
69    }
70}