1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use super::*;

pub fn crate_winmds() -> &'static [File] {
    use std::{mem::MaybeUninit, sync::Once};
    static ONCE: Once = Once::new();
    static mut VALUE: MaybeUninit<Vec<File>> = MaybeUninit::uninit();

    ONCE.call_once(|| {
        // This is safe because `Once` provides thread-safe one-time initialization
        unsafe { VALUE = MaybeUninit::new(get_crate_winmds()) }
    });

    // This is safe because `call_once` has already been called.
    unsafe { &*VALUE.as_ptr() }
}

fn cargo_metadata() -> &'static str {
    use std::{mem::MaybeUninit, sync::Once};
    static ONCE: Once = Once::new();
    static mut VALUE: MaybeUninit<String> = MaybeUninit::uninit();

    ONCE.call_once(|| {
        let output = std::process::Command::new(env!("CARGO"))
            .arg("metadata")
            .arg("--format-version=1")
            .arg("--no-deps")
            .arg("--offline")
            .output()
            .expect("Failed to run `cargo metadata`");

        unsafe {
            VALUE = MaybeUninit::new(
                String::from_utf8(output.stdout).expect("Cargo metadata is not utf-8"),
            )
        }
    });

    // This is safe because `call_once` has already been called.
    unsafe { &*VALUE.as_ptr() }
}

pub fn workspace_dir() -> String {
    const JSON_KEY: &str = r#""workspace_root":"#;
    let json = cargo_metadata();

    let beginning_index = json
        .rfind(JSON_KEY)
        .expect("Cargo metadata did not contain `workspace_root` key.")
        + JSON_KEY.len()
        + 1;

    let ending_index = json[beginning_index..]
        .find('"')
        .expect("Cargo metadata ended before closing '\"' in `workspace_root` value");

    json[beginning_index..beginning_index + ending_index].replace("\\\\", "\\")
}

pub fn target_dir() -> String {
    const JSON_KEY: &str = r#""target_directory":"#;
    let json = cargo_metadata();

    let beginning_index = json
        .rfind(JSON_KEY)
        .expect("Cargo metadata did not contain `target_directory` key.")
        + JSON_KEY.len()
        + 1;

    let ending_index = json[beginning_index..]
        .find('"')
        .expect("Cargo metadata ended before closing '\"' in `target_directory` value");

    json[beginning_index..beginning_index + ending_index].replace("\\\\", "\\")
}

fn get_crate_winmds() -> Vec<File> {
    fn push_dir(result: &mut Vec<File>, dir: &std::path::Path) {
        if let Ok(files) = std::fs::read_dir(&dir) {
            for file in files.filter_map(|file| file.ok()) {
                if let Ok(file_type) = file.file_type() {
                    if file_type.is_file() {
                        let path = file.path();
                        if let Some("winmd") =
                            path.extension().and_then(|extension| extension.to_str())
                        {
                            result.push(File::new(path));
                        }
                    }
                }
            }
        }
    }

    let mut result = vec![];

    if let Ok(dir) = std::env::var("CARGO_MANIFEST_DIR") {
        let mut dir: std::path::PathBuf = dir.into();
        dir.push(".windows");
        dir.push("winmd");
        push_dir(&mut result, &dir);
    }

    let dir = std::env::var("PATH").expect("No `PATH` env variable set");
    let end = dir.find(';').expect("Path not ending in `;`");
    let mut dir: std::path::PathBuf = dir[..end].into();
    dir.pop();
    dir.pop();
    dir.push(".windows");
    dir.push("winmd");
    push_dir(&mut result, &dir);

    let mut dir: std::path::PathBuf = target_dir().into();
    dir.push(".windows");
    dir.push("winmd");
    push_dir(&mut result, &dir);

    result
}