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
119
use super::LIBRARY_NAME;
use crate::{
    code::{self, CrateType, ModsMap, StaticFiles},
    linking,
};
use std::{
    fs,
    io::{self, Write},
    path::Path,
};

/// Constructs the compile directory.
/// Takes a list of source files and writes the contents to file.
/// Builds `Cargo.toml` using crates found in `SourceFile`.
pub fn build_compile_dir<P>(
    compile_dir: P,
    mods_map: &ModsMap,
    linking_config: &linking::LinkingConfiguration,
    static_files: &StaticFiles,
) -> io::Result<()>
where
    P: AsRef<Path>,
{
    let compile_dir = compile_dir.as_ref();

    let crates = mods_map
        .iter()
        .flat_map(|kvp| kvp.1.crates.iter())
        .chain(static_files.iter().flat_map(|x| x.crates.iter()));
    let crates = dedup_crates(crates);

    // write cargo toml contents
    create_file_and_dir(compile_dir.join("Cargo.toml"))?
        .write_all(cargotoml_contents(LIBRARY_NAME, crates.into_iter()).as_bytes())?;

    let (src_code, _map) = code::construct_source_code(mods_map, linking_config, static_files);

    create_file_and_dir(compile_dir.join("src/lib.rs"))?.write_all(src_code.as_bytes())?;

    Ok(())
}

fn dedup_crates<'a>(crates: impl Iterator<Item = &'a CrateType>) -> Vec<&'a CrateType> {
    let mut crates: Vec<&CrateType> = crates.collect();
    crates.sort_by_key(|x| &x.cargo_name);
    crates.dedup_by_key(|x| &x.cargo_name);
    crates
}

/// Creates the specified file along with the directory to it if it doesn't exist.
fn create_file_and_dir<P: AsRef<Path>>(file: P) -> io::Result<fs::File> {
    let file = file.as_ref();
    debug!("trying to create file: {}", file.display());
    if let Some(parent) = file.parent() {
        fs::create_dir_all(parent)?;
    }
    fs::File::create(file)
}

fn cargotoml_contents<'a, I: Iterator<Item = &'a CrateType>>(lib_name: &str, crates: I) -> String {
    format!(
        r#"[package]
name = "{lib_name}"
version = "0.1.0"
edition = "2018"

[lib]
name = "{lib_name}"
crate-type = [ "cdylib" ]
path = "src/lib.rs"

[dependencies]
kserd = {{ version = "0.3", default-features = false, features = [ "format" ] }}
{crates}
"#,
        lib_name = lib_name,
        crates = crates
            .map(|c| format!(r#"{} = "*""#, c.cargo_name))
            .collect::<Vec<_>>()
            .join("\n")
    )
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn create_file_and_dir_test() {
        use std::path::Path;

        let p = Path::new("foo.txt");
        assert!(!p.exists());
        create_file_and_dir(&"foo.txt").unwrap();
        assert!(p.exists());
        fs::remove_file(p).unwrap();
        assert!(!p.exists());

        let p = Path::new("target/testing/foo");
        assert!(!p.exists());
        create_file_and_dir(&p).unwrap();
        assert!(p.exists());
        fs::remove_file(p).unwrap();
        assert!(!p.exists());
    }

    #[test]
    fn test_dedup_crates() {
        let crates = vec![
            CrateType::parse_str("extern crate rand;").unwrap(),
            CrateType::parse_str("extern crate rand as rnd;").unwrap(),
            CrateType::parse_str("extern crate third;").unwrap(),
        ];
        let crates = dedup_crates(crates.iter());

        let v: Vec<_> = crates.iter().map(|x| &x.cargo_name).collect();
        assert_eq!(&v, &["rand", "third"]);
    }
}