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
use model::Mcu;
use pack;

use std::path::{Path, PathBuf};
use std::{fs, io};

/// The extension on the pack files.
const PACK_FILE_EXT: &'static str = "atdf";

/// All pack collections inside the 'packs' folder
/// of this repository.
const PACK_COLLECTIONS: &'static [&'static str] = &[
    "atmega", "tiny", "xmegaa", "xmegab",
    "xmegac", "xmegad", "xmegae", "automotive",
];

/// The on-disk path of the crate root.
const CRATE_ROOT: &'static str = env!("CARGO_MANIFEST_DIR");

lazy_static! {
    static ref MCUS: Vec<Mcu> = self::load_microcontrollers().expect("failed to load microcontrollers");

    static ref MCU_NAMES: Vec<String> = pack_informations()
        .expect("could not find packfiles")
        .into_iter()
        .map(|pack| pack.mcu_name)
        .collect();
}

struct PackInfo {
    pub mcu_name: String,
    pub path: PathBuf,
}

/// Retrieves a list of `Mcu` objects for all microcontrollers.
pub fn microcontrollers() -> &'static [Mcu] {
    &MCUS[..]
}

/// Retrieves a list of all microcontroller names.
///
/// # Examples
///
/// * `atmega328p`
/// * `attiny85`
pub fn microcontroller_names() -> &'static [String] {
    &MCU_NAMES[..]
}

/// Retrieves information for a specific microcontroller.
pub fn microcontroller(name: &str) -> Mcu {
    let pack_info = pack_informations().unwrap()
                            .into_iter()
                            .find(|pack_info| pack_info.mcu_name == name)
                            .expect(&format!("no microcontroller with the name '{}' found", name));
    pack::load(&pack_info.path).expect("could not parse microcontroller pack")
}

/// Retrieves a list of `Mcu` objects in a directory containg `PACK_COLLECTIONS`.
fn load_microcontrollers() -> Result<Vec<Mcu>, io::Error> {
    let microcontrollers = pack_informations()?.into_iter()
        .map(|pack_info| pack::load(&pack_info.path).unwrap())
        .collect();

    Ok(microcontrollers)
}

fn pack_informations() -> Result<Vec<PackInfo>, io::Error> {
    let path = Path::new(CRATE_ROOT).join("packs");
    pack_informations_from(&path)
}

fn pack_informations_from(path: &Path) -> Result<Vec<PackInfo>, io::Error> {
    let mut pack_paths = Vec::new();

    for pack_name in PACK_COLLECTIONS {
        pack_paths.extend(find_packs(&path.join(pack_name)).unwrap());
    }

    Ok(pack_paths.into_iter().map(|path| PackInfo {
        mcu_name: path.file_stem().unwrap().to_str().unwrap().to_lowercase().to_owned(),
        path: path.to_owned(),
    }).collect())
}

/// Finds all pack files in a directory.
fn find_packs(in_dir: &Path) -> Result<Vec<PathBuf>, io::Error> {
    let mut paths = Vec::new();

    for entry in fs::read_dir(in_dir)? {
        let entry = entry?;
        if let Some(PACK_FILE_EXT) = entry.path().extension().map(|s| s.to_str().unwrap()) {
            paths.push(entry.path());
        }
    }
    Ok(paths)
}

#[cfg(test)]
mod test {
    #[test]
    fn there_are_at_least_100_microcontrollers() {
        let mcus = super::microcontrollers();
        assert!(mcus.len() > 100, "there should be at least 100 microcontrollers");
    }

    #[test]
    fn can_get_atmega328p_by_name() {
        let mcu = super::microcontroller("atmega328p");
        assert_eq!("ATmega328P", mcu.device.name);
    }
}