use std::env;
use std::ffi::{OsStr, OsString};
use std::mem;
use std::path::Path;
use std::sync::{Arc, Mutex, Once, ONCE_INIT};
struct Outer {
inner: Mutex<Inner>,
}
struct Inner {
name: Arc<Option<OsString>>,
}
fn get_instance() -> &'static Mutex<Inner> {
static mut INSTANCE: *const Outer = 0 as *const Outer;
static ONCE: Once = ONCE_INIT;
ONCE.call_once(|| {
let instance = Box::new(Outer {
inner: Mutex::new(Inner { name: Arc::new(get_exec_name()) }),
});
unsafe {
INSTANCE = mem::transmute(instance);
}
});
unsafe { &(*INSTANCE).inner }
}
pub fn getprogname() -> String {
(*getprogname_arc()).as_ref()
.and_then(|s| s.to_str()).unwrap_or("").to_string()
}
pub fn getprogname_arc() -> Arc<Option<OsString>> {
get_instance().lock().unwrap().name.clone()
}
pub fn setprogname<S>(path: &S) where S: AsRef<OsStr> + ?Sized {
let name = Path::new(path).file_name().unwrap_or(path.as_ref()).to_owned();
let new = Arc::new(Some(name));
get_instance().lock().unwrap().name = new;
}
fn get_exec_name() -> Option<OsString> {
env::current_exe().ok().and_then(
|pb| pb.file_name().map(|s| s.to_owned()))
}
#[cfg(test)]
mod tests {
use std::thread;
use super::*;
#[test]
fn all() {
initial();
basic();
old_in_use();
components();
threads();
}
fn initial() {
assert!(getprogname().starts_with("unixcli"));
}
fn basic() {
setprogname("test");
assert_eq!(getprogname(), "test");
assert_eq!(getprogname(), "test");
setprogname("test2");
assert_eq!(getprogname(), "test2");
assert_eq!(getprogname(), "test2");
}
fn old_in_use() {
setprogname("test3");
let arc = getprogname_arc();
setprogname("test4");
assert_eq!(getprogname(), "test4");
assert_eq!(*arc, Some(OsString::from("test3")));
}
fn components() {
setprogname("/path/to/a/command");
assert_eq!(getprogname(), "command");
#[cfg(windows)]
{
setprogname("c:\\path\\to\\a\\command2");
assert_eq!(getprogname(), "command2");
setprogname("c:command3");
assert_eq!(getprogname(), "command3");
}
}
fn threads() {
let child1 = thread::spawn(move || {
for i in 0..10000 {
setprogname(&format!("child1: {}", i));
}
});
let child2 = thread::spawn(move || {
for i in 0..10000 {
setprogname(&format!("child2: {}", i));
}
});
for _ in 0..10000 {
println!("{}", getprogname());
}
child1.join().unwrap();
child2.join().unwrap();
}
}