reactive_graph_plugin_service_api/
plugin_paths.rs

1use std::env::consts::DLL_EXTENSION;
2use std::ffi::OsStr;
3use std::path::Path;
4use std::path::PathBuf;
5use std::time::SystemTime;
6use std::time::UNIX_EPOCH;
7
8fn get_timestamp() -> u64 {
9    SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs()
10}
11
12pub fn get_deploy_folder(path: &Path) -> Option<PathBuf> {
13    path.parent().and_then(|path| path.parent()).map(|path| path.join("deploy"))
14}
15
16// TODO: replace relative with absolute path replacement
17pub fn get_deploy_path(path: &Path) -> Option<PathBuf> {
18    file_prefix(path).and_then(|file_prefix| {
19        path.parent()
20            .and_then(|path| path.parent())
21            .map(|path| path.join("deploy").join(file_prefix).with_extension(DLL_EXTENSION))
22    })
23}
24
25// TODO: replace relative with absolute path replacement
26pub fn get_install_path(path: &Path) -> Option<PathBuf> {
27    file_prefix(path).and_then(|file_prefix| {
28        path.parent().and_then(|path| path.parent()).map(|path| {
29            path.join("installed")
30                .join(file_prefix)
31                .with_extension(format!("{}.{}", get_timestamp(), DLL_EXTENSION))
32        })
33    })
34}
35
36pub fn get_stem(path: &Path) -> Option<String> {
37    file_prefix(path).and_then(|stem| Some(stem.to_str()?.to_string()))
38}
39
40// Use this workaround until #![feature(path_file_prefix)] is stabilized
41pub fn file_prefix(path: &Path) -> Option<&OsStr> {
42    path.file_name().map(split_file_at_dot).map(|(before, _after)| before)
43}
44
45// Use this workaround until #![feature(path_file_prefix)] is stabilized
46fn split_file_at_dot(file: &OsStr) -> (&OsStr, Option<&OsStr>) {
47    let slice = os_str_as_u8_slice(file);
48    if slice == b".." {
49        return (file, None);
50    }
51
52    // The unsafety here stems from converting between &OsStr and &[u8]
53    // and back. This is safe to do because (1) we only look at ASCII
54    // contents of the encoding and (2) new &OsStr values are produced
55    // only from ASCII-bounded slices of existing &OsStr values.
56    let i = match slice[1..].iter().position(|b| *b == b'.') {
57        Some(i) => i + 1,
58        None => return (file, None),
59    };
60    let before = &slice[..i];
61    let after = &slice[i + 1..];
62    unsafe { (u8_slice_as_os_str(before), Some(u8_slice_as_os_str(after))) }
63}
64
65// Use this workaround until #![feature(path_file_prefix)] is stabilized
66fn os_str_as_u8_slice(s: &OsStr) -> &[u8] {
67    unsafe { &*(s as *const OsStr as *const [u8]) }
68}
69
70// Use this workaround until #![feature(path_file_prefix)] is stabilized
71unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr {
72    // SAFETY: see the comment of `os_str_as_u8_slice`
73    unsafe { &*(s as *const [u8] as *const OsStr) }
74}