-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move Unix-specific features to Unix module (#3)
- Extract POSIX features to POSIX module - Encapsulate Unix CString and PathBuf conversions - Improve error propagation of TemporaryDirectory
- Loading branch information
Showing
5 changed files
with
170 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,5 @@ | |
//! | ||
//! A robot built on Raspberry Pi. | ||
pub mod temporary_directory; | ||
#[cfg(unix)] | ||
pub mod unix; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
//! Features available on Unix-like operating systems. | ||
mod convert; | ||
mod posix; | ||
pub mod temporary_directory; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
//! Conversions that are only supported on Unix-like operating systems. | ||
use std::ffi::{CString, NulError, OsString}; | ||
use std::os::unix::ffi::OsStringExt; | ||
use std::path::PathBuf; | ||
|
||
/// Converts a [`CString`] into a [`PathBuf`]. | ||
pub fn c_string_to_path_buf(c_string: CString) -> PathBuf { | ||
PathBuf::from(OsString::from_vec(c_string.into_bytes())) | ||
} | ||
|
||
/// Converts a [`PathBuf`] into a [`CString`]. | ||
/// | ||
/// # Errors | ||
/// | ||
/// This function will return an error if `path_buf` contains a nul byte. | ||
pub fn path_buf_to_c_string(path_buf: PathBuf) -> Result<CString, NulError> { | ||
CString::new(path_buf.into_os_string().into_vec()) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
mod c_string_to_path_buf { | ||
use super::*; | ||
|
||
#[test] | ||
fn it_should_return_a_path_buf_representation_of_c_string() { | ||
let c_string = CString::new("/foo").expect("should not contain any nul bytes"); | ||
assert!(c_string_to_path_buf(c_string) == PathBuf::from("/foo")); | ||
} | ||
} | ||
|
||
mod path_buf_to_c_string { | ||
use super::*; | ||
|
||
#[test] | ||
fn it_should_return_ok_when_path_buf_does_not_contain_a_null_byte() { | ||
let path_buf = PathBuf::from("/foo"); | ||
assert!(path_buf_to_c_string(path_buf).is_ok()); | ||
} | ||
|
||
#[test] | ||
fn it_should_return_err_when_path_buf_contains_a_null_byte() { | ||
let path_buf = PathBuf::from("/f\0o"); | ||
assert!(path_buf_to_c_string(path_buf).is_err()); | ||
} | ||
|
||
#[test] | ||
fn it_should_return_a_c_string_representation_of_path_buf() { | ||
let path_buf = PathBuf::from("/foo"); | ||
assert!(path_buf_to_c_string(path_buf).is_ok_and(|c_string| c_string | ||
== CString::new("/foo").expect("should not contain any nul bytes"))); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
//! Features that are dependent on system conformance to POSIX standards. | ||
use std::ffi::{c_char, CString, NulError}; | ||
use std::path::PathBuf; | ||
use std::{env, io}; | ||
|
||
use super::convert; | ||
|
||
extern "C" { | ||
/// Securely creates a directory with a unique name derived from `template`. | ||
/// | ||
/// `template` must be a string representing the desired path ending with at | ||
/// least six trailing "X" characters. Six or more of the trailing "X" | ||
/// characters will be modified in place to create a unique name for the | ||
/// temporary directory. The directory is created with read, write, and execute | ||
/// permissions for the user only. | ||
/// | ||
/// Returns a pointer to `template` on success, or a null pointer on failure and | ||
/// sets `errno` to indicate the error. | ||
fn mkdtemp(template: *mut c_char) -> *mut c_char; | ||
} | ||
|
||
/// Securely creates a uniquely-named temporary directory. | ||
/// | ||
/// The path to the underlying temporary directory is based on the system’s | ||
/// temporary directory path composed with a random string. | ||
/// | ||
/// # Errors | ||
/// | ||
/// This function will return an error if it fails to create a temporary | ||
/// directory. | ||
pub fn create_temp_dir() -> Result<PathBuf, io::Error> { | ||
let template = get_temp_dir_template()?.into_raw(); | ||
let result = unsafe { mkdtemp(template) }; | ||
let error = io::Error::last_os_error(); | ||
let path = unsafe { CString::from_raw(template) }; | ||
|
||
if result.is_null() { | ||
Err(error) | ||
} else { | ||
Ok(convert::c_string_to_path_buf(path)) | ||
} | ||
} | ||
|
||
/// Returns a template for use with `mkdtemp`. | ||
/// | ||
/// # Errors | ||
/// | ||
/// This function will return an error if the system’s temporary directory path | ||
/// contains a nul byte. | ||
fn get_temp_dir_template() -> Result<CString, NulError> { | ||
let mut template = env::temp_dir(); | ||
template.push("XXXXXX"); | ||
convert::path_buf_to_c_string(template) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
mod create_temp_dir { | ||
use std::fs; | ||
|
||
use super::*; | ||
|
||
#[test] | ||
fn it_should_return_a_path_that_begins_with_the_system_temporary_directory() { | ||
let temp_dir = create_temp_dir().expect("`create_temp_dir()` should succeed"); | ||
assert!(temp_dir.starts_with(env::temp_dir())); | ||
let _ = fs::remove_dir_all(temp_dir); | ||
} | ||
|
||
#[test] | ||
fn it_should_return_a_path_to_an_accessible_directory() { | ||
let temp_dir = create_temp_dir().expect("`create_temp_dir()` should succeed"); | ||
assert!(temp_dir.is_dir()); | ||
let _ = fs::remove_dir_all(temp_dir); | ||
} | ||
|
||
#[test] | ||
fn it_should_return_a_unique_path_for_each_call() { | ||
let temp_dir_a = create_temp_dir().expect("`create_temp_dir()` should succeed"); | ||
let temp_dir_b = create_temp_dir().expect("`create_temp_dir()` should succeed"); | ||
assert_ne!(temp_dir_a, temp_dir_b); | ||
|
||
for temp_dir in &[temp_dir_a, temp_dir_b] { | ||
let _ = fs::remove_dir_all(temp_dir); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters