<commit_before>use structure::common::DocumentHeader;
use structure::model::{ModelDocument, ModelConfigDocument};
use structure::xflow::XFlowDocument;
use structure::page::PageDocument;
use structure::domain::DomainDocument;
use structure::translation::TranslationDocument;

use generation;

use glob::glob_with;
use glob::MatchOptions;

use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use std;

fn read_json_file(path: &Path) -> String {
    let display = path.display();

    let mut file = match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", display, Error::description(&why)),
        Ok(file) => file,
    };

    let mut s = String::new();
    if let Err(why) = file.read_to_string(&mut s) {
        panic!("couldn't read {}: {}", display, Error::description(&why));
    };

    s
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum ModelLoadError {
    NoPath,
    Unhandled,
}

fn write_file(filename: &str, data: &str) -> () {
    let path = Path::new(filename);
    let display = path.display();

    let mut file = match File::create(&path) {
        Err(why) => {
            error!("couldn't create {}: {}", display, why.description());
            panic!("couldn't create {}: {}", display, why.description());
        }
        Ok(file) => file,
    };

    match file.write_all(data.as_bytes()) {
        Err(why) => {
            error!("couldn't write to {}: {}", display, why.description());
            panic!("couldn't write to {}: {}", display, why.description());
        }
        Ok(_) => debug!("successfully wrote to {}", display),
    }
}

fn create_dir(path: &str) -> () {
    debug!("Creating directory '{:?}'", path);
    if !Path::new(path).exists() {
        match std::fs::create_dir(&path) {
            Ok(_) => {
                debug!("Created directory '{:?}' : OK", path);
            }
            Err(_) => {
                error!("Error creating directory '{:?}'", path);
            }
        };
    } else {
        debug!("Directory '{:?}' exists, not creating", path);
    }
}

pub fn build_to_react_app(model: &ModelDocument, path: &str) -> Result<(), ModelLoadError> {
    // partof: #SPC-artifact-generation-model

    // XXX Error handling, assumption checking

    debug!(
        "Building id:'{}' assets, model version:'{}' in directory '{}'",
        model.id,
        model.version,
        path
    );

    create_dir(&path);
    let xflow_path = format!("{path}/xflows", path = path);
    create_dir(&xflow_path);
    let component_path = format!("{path}/components", path = path);
    create_dir(&component_path);

    for page in &model.doc.pages {
        let doc = generation::page_to_react_component::output_html(&page);

        let filename = format!("{path}/{id}.js", path = component_path, id = page.id);
        write_file(&filename, &doc);

    }

    for xflow in &model.doc.xflows {
        let doc = generation::xflow_to_es5::output(&xflow);

        let filename = format!("{path}/{id}.js", path = xflow_path, id = xflow.id);
        write_file(&filename, &doc);
    }

    Ok(())
}

pub fn model_to_fs(model: &ModelDocument, path: &str) -> Result<(), ModelLoadError> {
    // partof: #SPC-serialization-fs

    // XXX Error handling, assumption checking

    debug!(
        "Writing model id:'{}', version:'{}' to directory '{}'",
        model.id,
        model.version,
        path
    );

    let model_header_doc_filename = format!("{}/model.json", path);
    write_file(&model_header_doc_filename, &model.get_header().to_json());

    let model_config_doc_filename = format!("{}/config.json", path);
    write_file(&model_config_doc_filename, &model.doc.config.to_json());

    let doc_filename = format!("{}/domain.json", path);
    write_file(&doc_filename, &model.doc.domain.to_json());

    let xflows_path_name = format!("{}/xflows", path);
    create_dir(&xflows_path_name);

    for doc in &model.doc.xflows {
        let doc_filename = format!("{}/{}.json", xflows_path_name, doc.id);
        write_file(&doc_filename, &doc.to_json());
    }

    let pages_path_name = format!("{}/pages", path);
    create_dir(&pages_path_name);

    for doc in &model.doc.pages {
        let doc_filename = format!("{}/{}.json", pages_path_name, doc.id);
        write_file(&doc_filename, &doc.to_json());
    }

    let translations_path_name = format!("{}/translations", path);
    create_dir(&translations_path_name);

    for doc in &model.doc.translations {
        let doc_filename = format!("{}/{}.json", translations_path_name, doc.doc.locale);
        write_file(&doc_filename, &doc.to_json());
    }

    Ok(())
}

pub fn model_from_fs(path: &str) -> Result<ModelDocument, ModelLoadError> {
    // partof: SPC-serialization-fs

    // XXX Error handling, assumption checking

    debug!("Reading model from directory '{}'", path);
    let model_header_filename = format!("{}/model.json", path);
    let model_header_path = Path::new(&model_header_filename);
    let model_header_json = read_json_file(model_header_path);
    let model_header: DocumentHeader = DocumentHeader::from_json(&model_header_json);

    let mut modeldoc = ModelDocument::new_from_header(&model_header);

    let model_config_filename = format!("{}/config.json", path);
    let model_config_path = Path::new(&model_config_filename);
    let model_config_json = read_json_file(model_config_path);
    let model_config: ModelConfigDocument = ModelConfigDocument::from_json(&model_config_json);

    modeldoc.doc.config = model_config;

    let domain_filename = format!("{}/domain.json", path);
    let domain_path = Path::new(&domain_filename);
    let json = read_json_file(domain_path);
    let domain: DomainDocument = DomainDocument::from_json(&json);

    modeldoc.doc.domain = domain;

    let glob_options = MatchOptions {
        case_sensitive: true,
        require_literal_separator: false,
        require_literal_leading_dot: false,
    };

    let xflow_files_path = format!("{}/xflows/*", path);
    for item in glob_with(&xflow_files_path, &glob_options).unwrap() {
        if let Ok(path) = item {
            let json = read_json_file(&path);
            let xflow_doc: XFlowDocument = XFlowDocument::from_json(&json);
            modeldoc.doc.xflows.push(xflow_doc);
        } else {
            warn!("Unable to load doc from '{:?}'", item);
        }
    }

    let page_files_path = format!("{}/pages/*", path);
    for item in glob_with(&page_files_path, &glob_options).unwrap() {
        if let Ok(path) = item {
            let json = read_json_file(&path);
            let page_doc: PageDocument = PageDocument::from_json(&json);
            modeldoc.doc.pages.push(page_doc);
        } else {
            warn!("Unable to load doc from '{:?}'", item);
        }
    }

    let translation_files_path = format!("{}/translations/*", path);
    for item in glob_with(&translation_files_path, &glob_options).unwrap() {
        if let Ok(path) = item {
            let json = read_json_file(&path);
            let translation_doc: TranslationDocument = TranslationDocument::from_json(&json);
            modeldoc.doc.translations.push(translation_doc);
        } else {
            warn!("Unable to load doc from '{:?}'", item);
        }
    }

    Ok(modeldoc)
}

pub fn init_new_model_dir(path: &str) -> Result<(), ModelLoadError> {
    create_dir(path);
    let mut model = ModelDocument::default();
    let default_locale = model.doc.config.doc.default_locale.clone();
    model.add_locale(&default_locale);
    model.pad_all_translations();
    model_to_fs(&model, &path)
}

pub fn is_model_dir(path: &str) -> bool {
    match model_from_fs(path) {
        Ok(_) => true,
        Err(_) => false,
    }
}
<commit_msg>feat: util for building dotfiles<commit_after>use structure::common::DocumentHeader;
use structure::model::{ModelDocument, ModelConfigDocument};
use structure::xflow::XFlowDocument;
use structure::page::PageDocument;
use structure::domain::DomainDocument;
use structure::translation::TranslationDocument;

use generation;

use glob::glob_with;
use glob::MatchOptions;

use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use std;

fn read_json_file(path: &Path) -> String {
    let display = path.display();

    let mut file = match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", display, Error::description(&why)),
        Ok(file) => file,
    };

    let mut s = String::new();
    if let Err(why) = file.read_to_string(&mut s) {
        panic!("couldn't read {}: {}", display, Error::description(&why));
    };

    s
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum ModelLoadError {
    NoPath,
    Unhandled,
}

fn write_file(filename: &str, data: &str) -> () {
    let path = Path::new(filename);
    let display = path.display();

    let mut file = match File::create(&path) {
        Err(why) => {
            error!("couldn't create {}: {}", display, why.description());
            panic!("couldn't create {}: {}", display, why.description());
        }
        Ok(file) => file,
    };

    match file.write_all(data.as_bytes()) {
        Err(why) => {
            error!("couldn't write to {}: {}", display, why.description());
            panic!("couldn't write to {}: {}", display, why.description());
        }
        Ok(_) => debug!("successfully wrote to {}", display),
    }
}

fn create_dir(path: &str) -> () {
    debug!("Creating directory '{:?}'", path);
    if !Path::new(path).exists() {
        match std::fs::create_dir(&path) {
            Ok(_) => {
                debug!("Created directory '{:?}' : OK", path);
            }
            Err(_) => {
                error!("Error creating directory '{:?}'", path);
            }
        };
    } else {
        debug!("Directory '{:?}' exists, not creating", path);
    }
}

pub fn build_dotfiles(model: &ModelDocument, path: &str) -> Result<(), ModelLoadError> {
    // partof: #SPC-artifact-generation-model

    // XXX Error handling, assumption checking

    debug!(
        "Building dotfiles id:'{}' assets, model version:'{}' in directory '{}'",
        model.id,
        model.version,
        path
    );

    create_dir(&path);
    let xflow_path = format!("{path}/xflows", path = path);
    create_dir(&xflow_path);

    for xflow in &model.doc.xflows {
        let doc = generation::xflow_to_dot::output(&xflow);

        let filename = format!("{path}/{id}.dot", path = xflow_path, id = xflow.id);
        write_file(&filename, &doc);
    }

    let doc = generation::domain_to_dot::output(&model.doc.domain);

    let filename = format!("{path}/domain.dot", path = path);
    write_file(&filename, &doc);

    Ok(())
}

pub fn build_to_react_app(model: &ModelDocument, path: &str) -> Result<(), ModelLoadError> {
    // partof: #SPC-artifact-generation-model

    // XXX Error handling, assumption checking

    debug!(
        "Building id:'{}' assets, model version:'{}' in directory '{}'",
        model.id,
        model.version,
        path
    );

    create_dir(&path);
    let xflow_path = format!("{path}/xflows", path = path);
    create_dir(&xflow_path);
    let component_path = format!("{path}/components", path = path);
    create_dir(&component_path);

    for page in &model.doc.pages {
        let doc = generation::page_to_react_component::output_html(&page);

        let filename = format!("{path}/{id}.js", path = component_path, id = page.id);
        write_file(&filename, &doc);

    }

    for xflow in &model.doc.xflows {
        let doc = generation::xflow_to_es5::output(&xflow);

        let filename = format!("{path}/{id}.js", path = xflow_path, id = xflow.id);
        write_file(&filename, &doc);
    }

    Ok(())
}

pub fn model_to_fs(model: &ModelDocument, path: &str) -> Result<(), ModelLoadError> {
    // partof: #SPC-serialization-fs

    // XXX Error handling, assumption checking

    debug!(
        "Writing model id:'{}', version:'{}' to directory '{}'",
        model.id,
        model.version,
        path
    );

    let model_header_doc_filename = format!("{}/model.json", path);
    write_file(&model_header_doc_filename, &model.get_header().to_json());

    let model_config_doc_filename = format!("{}/config.json", path);
    write_file(&model_config_doc_filename, &model.doc.config.to_json());

    let doc_filename = format!("{}/domain.json", path);
    write_file(&doc_filename, &model.doc.domain.to_json());

    let xflows_path_name = format!("{}/xflows", path);
    create_dir(&xflows_path_name);

    for doc in &model.doc.xflows {
        let doc_filename = format!("{}/{}.json", xflows_path_name, doc.id);
        write_file(&doc_filename, &doc.to_json());
    }

    let pages_path_name = format!("{}/pages", path);
    create_dir(&pages_path_name);

    for doc in &model.doc.pages {
        let doc_filename = format!("{}/{}.json", pages_path_name, doc.id);
        write_file(&doc_filename, &doc.to_json());
    }

    let translations_path_name = format!("{}/translations", path);
    create_dir(&translations_path_name);

    for doc in &model.doc.translations {
        let doc_filename = format!("{}/{}.json", translations_path_name, doc.doc.locale);
        write_file(&doc_filename, &doc.to_json());
    }

    Ok(())
}

pub fn model_from_fs(path: &str) -> Result<ModelDocument, ModelLoadError> {
    // partof: SPC-serialization-fs

    // XXX Error handling, assumption checking

    debug!("Reading model from directory '{}'", path);
    let model_header_filename = format!("{}/model.json", path);
    let model_header_path = Path::new(&model_header_filename);
    let model_header_json = read_json_file(model_header_path);
    let model_header: DocumentHeader = DocumentHeader::from_json(&model_header_json);

    let mut modeldoc = ModelDocument::new_from_header(&model_header);

    let model_config_filename = format!("{}/config.json", path);
    let model_config_path = Path::new(&model_config_filename);
    let model_config_json = read_json_file(model_config_path);
    let model_config: ModelConfigDocument = ModelConfigDocument::from_json(&model_config_json);

    modeldoc.doc.config = model_config;

    let domain_filename = format!("{}/domain.json", path);
    let domain_path = Path::new(&domain_filename);
    let json = read_json_file(domain_path);
    let domain: DomainDocument = DomainDocument::from_json(&json);

    modeldoc.doc.domain = domain;

    let glob_options = MatchOptions {
        case_sensitive: true,
        require_literal_separator: false,
        require_literal_leading_dot: false,
    };

    let xflow_files_path = format!("{}/xflows/*", path);
    for item in glob_with(&xflow_files_path, &glob_options).unwrap() {
        if let Ok(path) = item {
            let json = read_json_file(&path);
            let xflow_doc: XFlowDocument = XFlowDocument::from_json(&json);
            modeldoc.doc.xflows.push(xflow_doc);
        } else {
            warn!("Unable to load doc from '{:?}'", item);
        }
    }

    let page_files_path = format!("{}/pages/*", path);
    for item in glob_with(&page_files_path, &glob_options).unwrap() {
        if let Ok(path) = item {
            let json = read_json_file(&path);
            let page_doc: PageDocument = PageDocument::from_json(&json);
            modeldoc.doc.pages.push(page_doc);
        } else {
            warn!("Unable to load doc from '{:?}'", item);
        }
    }

    let translation_files_path = format!("{}/translations/*", path);
    for item in glob_with(&translation_files_path, &glob_options).unwrap() {
        if let Ok(path) = item {
            let json = read_json_file(&path);
            let translation_doc: TranslationDocument = TranslationDocument::from_json(&json);
            modeldoc.doc.translations.push(translation_doc);
        } else {
            warn!("Unable to load doc from '{:?}'", item);
        }
    }

    Ok(modeldoc)
}

pub fn init_new_model_dir(path: &str) -> Result<(), ModelLoadError> {
    create_dir(path);
    let mut model = ModelDocument::default();
    let default_locale = model.doc.config.doc.default_locale.clone();
    model.add_locale(&default_locale);
    model.pad_all_translations();
    model_to_fs(&model, &path)
}

pub fn is_model_dir(path: &str) -> bool {
    match model_from_fs(path) {
        Ok(_) => true,
        Err(_) => false,
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
use utils;
pub struct Memory
{
    ram: [u8;0x10000],
    rom: [u8;0x10000],
    // bank switching flags
    basic_on: bool,
    chargen_on: bool,
    io_on: bool,
    kernal_on: bool,
    cart_lo_on: bool, // cart flag - unused for now
    cart_hi_on: bool  // cart flag - unused for now
}

impl Memory
{
    pub fn new() -> Memory
    {
        let mut memory = Memory
        {
            ram: [0;0x10000],   // 64k
            rom: [0;0x10000],   // store ROM data in 64k array so it's easier to address it with bank switching
            basic_on: false,
            chargen_on: false,
            io_on: false,
            kernal_on: false,
            cart_lo_on: false, // unused for now
            cart_hi_on: false, // unused for now
        };

        // load basic
        let mut start_addr: u32 = 0xA000;
        let basic = utils::open_file("rom/basic.rom", 0);

        for (i,addr) in (start_addr..0xC000).enumerate()
        {
            memory.rom[addr as usize] = basic[i as usize];
        }

        // load chargen
        start_addr = 0xD000;
        let chargen = utils::open_file("rom/chargen.rom", 0);

        for (i,addr) in (start_addr..0xE000).enumerate()
        {
            memory.rom[addr as usize] = chargen[i as usize];
        }

        // load kernal
        start_addr = 0xE000;
        let kernal = utils::open_file("rom/kernal.rom", 0);

        for (i,addr) in (start_addr..0x10000).enumerate()
        {
            memory.rom[addr as usize] = kernal[i as usize];
        }

        memory
    }

    // returns memory bank for current address + latch setting and an indicator whether it's read-only to detect ROM writes
    pub fn get_bank(&mut self, addr: u16) -> (&mut [u8], bool)
    {
        match addr
        {
            0x0000...0x9FFF => (&mut self.ram[0..0x10000], false),
            0xA000...0xCFFF => if self.basic_on   { (&mut self.rom[0..0x10000], true) } else { (&mut self.ram[0..0x10000], false) },
            0xD000...0xDFFF => if self.chargen_on { (&mut self.rom[0..0x10000], true) } else { (&mut self.ram[0..0x10000], false) },
            0xE000...0xFFFF => if self.kernal_on  { (&mut self.rom[0..0x10000], true) } else { (&mut self.ram[0..0x10000], false) },
            _ => panic!("Address out of memory range")
        }
    }

    pub fn reset(&mut self)
    {
        self.write_byte(0x0000, 0xFF);
        self.write_byte(0x0001, 0x07); // enable kernal, chargen and basic ROMs
    }

    fn update_bank_flags(&mut self)
    {
        // latch state is determined by 3 least significant bits from this location
        let latch = self.ram[0x0001] & 0x07;

        self.chargen_on = ((latch & 0x04) == 0) && ((latch & 0x03) != 0); // %0xx except %000
        self.io_on      = ((latch & 0x04) != 0) && ((latch & 0x03) != 0); // %1xx except %100
        self.basic_on   = (latch & 0x03) == 0x03;
        self.kernal_on  = self.basic_on || ((latch & 0x03) == 0x02);
    }

    // Write a byte to memory - returns whether RAM was written (true) or RAM under ROM (false)
    pub fn write_byte(&mut self, addr: u16, value: u8) -> bool
    {
        // parentheses to avoid borrowing issues with changing the flags
        {
            self.ram[addr as usize] = value;

            let (_, read_only) = self.get_bank(addr);

            // RAM under ROM written? Return false to let us know about it
            if read_only
            {
                return false;
            }
        }

        // update the bank switching flags here, since they can only change on memory write
        // latch byte changed - update bank switching flags
        if addr == 0x0001 { self.update_bank_flags(); }

        return true;
    }

    // Read a byte from memory
    pub fn read_byte(&mut self, addr: u16) -> u8
    {
        let bank = self.get_bank(addr);
        bank.0[addr as usize]
    }

    // Read a word from memory (stored in little endian)
    pub fn read_word_le(&mut self, addr: u16) -> u16
    {
        let bank = self.get_bank(addr);
        let value_be: u16 = ((bank.0[addr as usize] as u16) << 8 & 0xFF00) |
                            ((bank.0[(addr + 0x0001) as usize] as u16) & 0x00FF);

        let value_le: u16 = ((value_be << 8) & 0xFF00) | ((value_be >> 8) & 0x00FF);
        value_le
    }

    // Read a word from memory (stored in big endian: swap low<->high)
    pub fn read_word_be(&mut self, addr: u16) -> u16
    {
        let bank = self.get_bank(addr);
        let value_le: u16 = ((bank.0[addr as usize] as u16) << 8 & 0xFF00) |
                            ((bank.0[(addr + 0x0001) as usize] as u16) & 0x00FF);
        value_le
    }

    // Write word in little endian format (low/high)
    pub fn write_word_le(&mut self, addr: u16, value: u16) -> bool
    {
        let value_le_lo: u8 = (((value << 8) & 0xFF00) >> 8 & 0xFF) as u8;
        let value_le_hi: u8 = ((value >> 8) & 0x00FF) as u8;

        let hi = self.write_byte(addr, value_le_lo);
        let lo = self.write_byte(addr + 0x0001, value_le_hi);

        return hi && lo;
    }

    // Write word in big endian format (high/low)
    pub fn write_word_be(&mut self, addr: u16, value: u16) -> bool
    {
        let value_le_lo: u8 = (((value << 8) & 0xFF00) >> 8 & 0xFF) as u8;
        let value_le_hi: u8 = ((value >> 8) & 0x00FF) as u8;

        let hi = self.write_byte(addr, value_le_hi);
        let lo = self.write_byte(addr + 0x0001, value_le_lo);

        return hi && lo;
    }

    // Debug: overwrite a byte in ROM
    pub fn debug_write_rom(&mut self, addr: u16, value: u8)
    {
        self.rom[addr as usize] = value;
    }

}

<commit_msg>separate memory banks for ROM and IO<commit_after>#![allow(dead_code)]
use utils;
pub struct Memory
{
    ram: [u8;0x10000],
    basic: [u8;0x2000],
    chargen: [u8;0x1000],
    io: [u8;0x1000],
    kernal: [u8;0x2000],
    // bank switching flags
    basic_on: bool,
    chargen_on: bool,
    io_on: bool,
    kernal_on: bool,
    cart_lo_on: bool, // cart flag - unused for now
    cart_hi_on: bool  // cart flag - unused for now
}

impl Memory
{
    pub fn new() -> Memory
    {
        let mut memory = Memory
        {
            ram: [0;0x10000],   // 64k
            basic: [0;0x2000],  // 8k
            chargen: [0;0x1000], // 4k
            io: [0;0x1000],      // 4k dedicated IO memory for VIC, SID, CIA registers [0xD000 - 0xDFFF]
            kernal: [0;0x2000],  // 8k
            basic_on: false,
            chargen_on: false,
            io_on: false,
            kernal_on: false,
            cart_lo_on: false, // unused for now
            cart_hi_on: false, // unused for now
        };

        // load basic
        let basic = utils::open_file("rom/basic.rom", 0);

        for i in (0..0x2000)
        {
            memory.basic[i as usize] = basic[i as usize];
        }

        // load chargen
        let chargen = utils::open_file("rom/chargen.rom", 0);

        for i in (0..0x1000)
        {
            memory.chargen[i as usize] = chargen[i as usize];
        }

        // load kernal
        let kernal = utils::open_file("rom/kernal.rom", 0);

        for i in (0..0x2000)
        {
            memory.kernal[i as usize] = kernal[i as usize];
        }

        memory
    }

    // returns memory bank for current address + latch setting and an indicator whether it's read-only to detect ROM writes
    pub fn get_bank(&mut self, addr: u16) -> (&mut [u8], bool, u16)
    {
        match addr
        {
            0x0000...0x9FFF => (&mut self.ram[0..0x10000], false, 0),
            0xA000...0xCFFF => if self.basic_on   { (&mut self.basic[0..0x2000], true, 0xA000) } else { (&mut self.ram[0..0x10000], false, 0) },
            0xD000...0xDFFF => {
                if self.chargen_on { return (&mut self.chargen[0..0x1000], true, 0xD000); }
                if self.io_on      { return (&mut self.io[0..0x1000], false, 0xD000); }
                return (&mut self.ram[0..0x10000], false, 0);
            },
            0xE000...0xFFFF => if self.kernal_on  { (&mut self.kernal[0..0x2000], true, 0xE000) } else { (&mut self.ram[0..0x10000], false, 0) },
            _ => panic!("Address out of memory range")
        }
    }

    pub fn reset(&mut self)
    {
        self.write_byte(0x0000, 0xFF);
        self.write_byte(0x0001, 0x07); // enable kernal, chargen and basic ROMs
    }

    fn update_bank_flags(&mut self)
    {
        // latch state is determined by 3 least significant bits from this location
        let latch = self.ram[0x0001] & 0x07;

        self.chargen_on = ((latch & 0x04) == 0) && ((latch & 0x03) != 0); // %0xx except %000
        self.io_on      = ((latch & 0x04) != 0) && ((latch & 0x03) != 0); // %1xx except %100
        self.basic_on   = (latch & 0x03) == 0x03;
        self.kernal_on  = self.basic_on || ((latch & 0x03) == 0x02);
    }

    // Write a byte to memory - returns whether RAM was written (true) or RAM under ROM (false)
    pub fn write_byte(&mut self, addr: u16, value: u8) -> bool
    {
        // parentheses to avoid borrowing issues with changing the flags
        {
            let (_, read_only, offset) = self.get_bank(addr);

            // RAM under ROM written? Return false to let us know about it
            if read_only
            {
                self.ram[(addr - offset) as usize] = value;
                return false;
            }
            else
            {
                let (bank, _, offset) = self.get_bank(addr);
                bank[(addr - offset) as usize] = value;
            }
        }

        // update the bank switching flags here, since they can only change on memory write
        // latch byte changed - update bank switching flags
        if addr == 0x0001 { self.update_bank_flags(); }

        return true;
    }

    // Read a byte from memory
    pub fn read_byte(&mut self, addr: u16) -> u8
    {
        let (bank, _, offset) = self.get_bank(addr);
        bank[(addr - offset) as usize]
    }

    // Read a word from memory (stored in little endian)
    pub fn read_word_le(&mut self, addr: u16) -> u16
    {
        let (bank, _, offset) = self.get_bank(addr);
        let value_be: u16 = ((bank[(addr - offset) as usize] as u16) << 8 & 0xFF00) |
                            ((bank[(addr - offset + 0x0001) as usize] as u16) & 0x00FF);

        let value_le: u16 = ((value_be << 8) & 0xFF00) | ((value_be >> 8) & 0x00FF);
        value_le
    }

    // Read a word from memory (stored in big endian: swap low<->high)
    pub fn read_word_be(&mut self, addr: u16) -> u16
    {
        let (bank, _, offset) = self.get_bank(addr);
        let value_le: u16 = ((bank[(addr - offset) as usize] as u16) << 8 & 0xFF00) |
                            ((bank[(addr - offset + 0x0001) as usize] as u16) & 0x00FF);
        value_le
    }

    // Write word in little endian format (low/high)
    pub fn write_word_le(&mut self, addr: u16, value: u16) -> bool
    {
        let value_le_lo: u8 = (((value << 8) & 0xFF00) >> 8 & 0xFF) as u8;
        let value_le_hi: u8 = ((value >> 8) & 0x00FF) as u8;

        let hi = self.write_byte(addr, value_le_lo);
        let lo = self.write_byte(addr + 0x0001, value_le_hi);

        return hi && lo;
    }

    // Write word in big endian format (high/low)
    pub fn write_word_be(&mut self, addr: u16, value: u16) -> bool
    {
        let value_le_lo: u8 = (((value << 8) & 0xFF00) >> 8 & 0xFF) as u8;
        let value_le_hi: u8 = ((value >> 8) & 0x00FF) as u8;

        let hi = self.write_byte(addr, value_le_hi);
        let lo = self.write_byte(addr + 0x0001, value_le_lo);

        return hi && lo;
    }
}

<|endoftext|>


<commit_before>//! Miscellaneous helper traits, enums, and structs for models.

use super::prelude::*;
use internal::RwLockExt;

#[cfg(all(feature = "model", feature = "utils"))]
use std::error::Error as StdError;
#[cfg(all(feature = "model", feature = "utils"))]
use std::result::Result as StdResult;
#[cfg(all(feature = "model", feature = "utils"))]
use std::str::FromStr;
#[cfg(all(feature = "model", feature = "utils"))]
use std::fmt;
#[cfg(all(feature = "model", any(feature = "cache", feature = "utils")))]
use utils;

/// Allows something - such as a channel or role - to be mentioned in a message.
pub trait Mentionable {
    /// Creates a mentionable string, that will be able to notify and/or create
    /// a link to the item.
    fn mention(&self) -> String;
}

impl Mentionable for ChannelId {
    fn mention(&self) -> String { format!("<#{}>", self.0) }
}

impl Mentionable for Channel {
    fn mention(&self) -> String {
        match *self {
            Channel::Guild(ref x) => x.with(Mentionable::mention),
            Channel::Private(ref x) => x.with(Mentionable::mention),
            Channel::Group(ref x) => x.with(Mentionable::mention),
            Channel::Category(ref x) => x.with(Mentionable::mention),
        }
    }
}

impl Mentionable for ChannelCategory {
    fn mention(&self) -> String {
        format!("#{}", self.name)
    }
}

impl Mentionable for CurrentUser {
    fn mention(&self) -> String {
        format!("<@{}>", self.id.0)
    }
}

impl Mentionable for Emoji {
    fn mention(&self) -> String { format!("<:{}:{}>", self.name, self.id.0) }
}

impl Mentionable for Group {
    fn mention(&self) -> String {
        format!("<#{}", self.channel_id.0)
    }
}

impl Mentionable for Member {
    fn mention(&self) -> String { format!("<@{}>", self.user.with(|u| u.id.0)) }
}

impl Mentionable for PrivateChannel {
    fn mention(&self) -> String {
        format!("<#{}>", self.id.0)
    }
}

impl Mentionable for RoleId {
    fn mention(&self) -> String { format!("<@&{}>", self.0) }
}

impl Mentionable for Role {
    fn mention(&self) -> String { format!("<@&{}>", self.id.0) }
}

impl Mentionable for UserId {
    fn mention(&self) -> String { format!("<@{}>", self.0) }
}

impl Mentionable for User {
    fn mention(&self) -> String { format!("<@{}>", self.id.0) }
}

impl Mentionable for GuildChannel {
    fn mention(&self) -> String { format!("<#{}>", self.id.0) }
}

#[cfg(all(feature = "model", feature = "utils"))]
#[derive(Debug)]
pub enum UserParseError {
    InvalidUsername,
    Rest(Box<Error>),
}

#[cfg(all(feature = "model", feature = "utils"))]
impl fmt::Display for UserParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description()) }
}

#[cfg(all(feature = "model", feature = "utils"))]
impl StdError for UserParseError {
    fn description(&self) -> &str {
        use self::UserParseError::*;

        match *self {
            InvalidUsername => "invalid username",
            Rest(_) => "could not fetch",
        }
    }
}

#[cfg(all(feature = "model", feature = "utils"))]
impl FromStr for User {
    type Err = UserParseError;

    fn from_str(s: &str) -> StdResult<Self, Self::Err> {
        match utils::parse_username(s) {
            Some(x) => UserId(x as u64)
                .get()
                .map_err(|e| UserParseError::Rest(Box::new(e))),
            _ => Err(UserParseError::InvalidUsername),
        }
    }
}

macro_rules! impl_from_str {
    (id: $($id:tt, $err:ident;)*) => {
        $(
            #[cfg(all(feature = "model", feature = "utils"))]
            #[derive(Debug)]
            pub enum $err {
                InvalidFormat,
            }

            #[cfg(all(feature = "model", feature = "utils"))]
            impl fmt::Display for $err {
                fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description()) }
            }

            #[cfg(all(feature = "model", feature = "utils"))]
            impl StdError for $err {
                fn description(&self) -> &str {
                    use self::$err::*;

                    match *self {
                        InvalidFormat => "invalid id format",
                    }
                }
            }

            #[cfg(all(feature = "model", feature = "utils"))]
            impl FromStr for $id {
                type Err = $err;

                fn from_str(s: &str) -> StdResult<Self, Self::Err> {
                    Ok(match utils::parse_username(s) {
                        Some(id) => $id(id),
                        None => s.parse::<u64>().map($id).map_err(|_| $err::InvalidFormat)?,
                    })
                }
            }
        )*
    };

    (struct: $($struct:ty, $id:tt, $err:ident, $invalid_variant:tt, $parse_fn:ident, $desc:expr;)*) => {
        $(
            #[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
            #[derive(Debug)]
            pub enum $err {
                NotPresentInCache,
                $invalid_variant,
            }

            #[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
            impl fmt::Display for $err {
                fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description()) }
            }

            #[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
            impl StdError for $err {
                fn description(&self) -> &str {
                    use self::$err::*;

                    match *self {
                        NotPresentInCache => "not present in cache",
                        $invalid_variant => $desc,
                    }
                }
            }

            #[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
            impl FromStr for $struct {
                type Err = $err;

                fn from_str(s: &str) -> StdResult<Self, Self::Err> {
                    match utils::$parse_fn(s) {
                        Some(x) => match $id(x).find() {
                            Some(user) => Ok(user),
                            _ => Err($err::NotPresentInCache),
                        },
                        _ => Err($err::$invalid_variant),
                    }
                }
            }
        )*
    };
}

impl_from_str! { id:
    UserId, UserIdParseError;
    RoleId, RoleIdParseError;
    ChannelId, ChannelIdParseError;
}

impl_from_str! { struct:
    Channel, ChannelId, ChannelParseError, InvalidChannel, parse_channel, "invalid channel";
    Role, RoleId, RoleParseError, InvalidRole, parse_role, "invalid role";
}

/// A version of an emoji used only when solely the Id and name are known.
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct EmojiIdentifier {
    /// The Id of the emoji.
    pub id: EmojiId,
    /// The name of the emoji. It must be at least 2 characters long and can
    /// only contain alphanumeric characters and underscores.
    pub name: String,
}

#[cfg(all(feature = "model", feature = "utils"))]
impl EmojiIdentifier {
    /// Generates a URL to the emoji's image.
    #[inline]
    pub fn url(&self) -> String { format!(cdn!("/emojis/{}.png"), self.id) }
}

#[cfg(all(feature = "model", feature = "utils"))]
impl FromStr for EmojiIdentifier {
    type Err = ();

    fn from_str(s: &str) -> StdResult<Self, ()> { utils::parse_emoji(s).ok_or_else(|| ()) }
}

/// A component that was affected during a service incident.
///
/// This is pulled from the Discord status page.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct AffectedComponent {
    pub name: String,
}

/// An incident retrieved from the Discord status page.
///
/// This is not necessarily a representation of an ongoing incident.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Incident {
    pub created_at: String,
    pub id: String,
    pub impact: String,
    pub incident_updates: Vec<IncidentUpdate>,
    pub monitoring_at: Option<String>,
    pub name: String,
    pub page_id: String,
    pub resolved_at: Option<String>,
    pub short_link: String,
    pub status: String,
    pub updated_at: String,
}

/// An update to an incident from the Discord status page.
///
/// This will typically state what new information has been discovered about an
/// incident.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct IncidentUpdate {
    pub affected_components: Vec<AffectedComponent>,
    pub body: String,
    pub created_at: String,
    pub display_at: String,
    pub id: String,
    pub incident_id: String,
    pub status: IncidentStatus,
    pub updated_at: String,
}

/// The type of status update during a service incident.
#[derive(Copy, Clone, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum IncidentStatus {
    Identified,
    Investigating,
    Monitoring,
    Postmortem,
    Resolved,
}

/// A Discord status maintenance message. This can be either for active
/// maintenances or for scheduled maintenances.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Maintenance {
    pub description: String,
    pub id: String,
    pub name: String,
    pub start: String,
    pub stop: String,
}
<commit_msg>typos<commit_after>//! Miscellaneous helper traits, enums, and structs for models.

use super::prelude::*;
use internal::RwLockExt;

#[cfg(all(feature = "model", feature = "utils"))]
use std::error::Error as StdError;
#[cfg(all(feature = "model", feature = "utils"))]
use std::result::Result as StdResult;
#[cfg(all(feature = "model", feature = "utils"))]
use std::str::FromStr;
#[cfg(all(feature = "model", feature = "utils"))]
use std::fmt;
#[cfg(all(feature = "model", any(feature = "cache", feature = "utils")))]
use utils;

/// Allows something - such as a channel or role - to be mentioned in a message.
pub trait Mentionable {
    /// Creates a mentionable string, that will be able to notify and/or create
    /// a link to the item.
    fn mention(&self) -> String;
}

impl Mentionable for ChannelId {
    fn mention(&self) -> String { format!("<#{}>", self.0) }
}

impl Mentionable for Channel {
    fn mention(&self) -> String {
        match *self {
            Channel::Guild(ref x) => x.with(Mentionable::mention),
            Channel::Private(ref x) => x.with(Mentionable::mention),
            Channel::Group(ref x) => x.with(Mentionable::mention),
            Channel::Category(ref x) => x.with(Mentionable::mention),
        }
    }
}

impl Mentionable for ChannelCategory {
    fn mention(&self) -> String {
        format!("<#{}>", self.name)
    }
}

impl Mentionable for CurrentUser {
    fn mention(&self) -> String {
        format!("<@{}>", self.id.0)
    }
}

impl Mentionable for Emoji {
    fn mention(&self) -> String { format!("<:{}:{}>", self.name, self.id.0) }
}

impl Mentionable for Group {
    fn mention(&self) -> String {
        format!("<#{}>", self.channel_id.0)
    }
}

impl Mentionable for Member {
    fn mention(&self) -> String { format!("<@{}>", self.user.with(|u| u.id.0)) }
}

impl Mentionable for PrivateChannel {
    fn mention(&self) -> String {
        format!("<#{}>", self.id.0)
    }
}

impl Mentionable for RoleId {
    fn mention(&self) -> String { format!("<@&{}>", self.0) }
}

impl Mentionable for Role {
    fn mention(&self) -> String { format!("<@&{}>", self.id.0) }
}

impl Mentionable for UserId {
    fn mention(&self) -> String { format!("<@{}>", self.0) }
}

impl Mentionable for User {
    fn mention(&self) -> String { format!("<@{}>", self.id.0) }
}

impl Mentionable for GuildChannel {
    fn mention(&self) -> String { format!("<#{}>", self.id.0) }
}

#[cfg(all(feature = "model", feature = "utils"))]
#[derive(Debug)]
pub enum UserParseError {
    InvalidUsername,
    Rest(Box<Error>),
}

#[cfg(all(feature = "model", feature = "utils"))]
impl fmt::Display for UserParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description()) }
}

#[cfg(all(feature = "model", feature = "utils"))]
impl StdError for UserParseError {
    fn description(&self) -> &str {
        use self::UserParseError::*;

        match *self {
            InvalidUsername => "invalid username",
            Rest(_) => "could not fetch",
        }
    }
}

#[cfg(all(feature = "model", feature = "utils"))]
impl FromStr for User {
    type Err = UserParseError;

    fn from_str(s: &str) -> StdResult<Self, Self::Err> {
        match utils::parse_username(s) {
            Some(x) => UserId(x as u64)
                .get()
                .map_err(|e| UserParseError::Rest(Box::new(e))),
            _ => Err(UserParseError::InvalidUsername),
        }
    }
}

macro_rules! impl_from_str {
    (id: $($id:tt, $err:ident;)*) => {
        $(
            #[cfg(all(feature = "model", feature = "utils"))]
            #[derive(Debug)]
            pub enum $err {
                InvalidFormat,
            }

            #[cfg(all(feature = "model", feature = "utils"))]
            impl fmt::Display for $err {
                fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description()) }
            }

            #[cfg(all(feature = "model", feature = "utils"))]
            impl StdError for $err {
                fn description(&self) -> &str {
                    use self::$err::*;

                    match *self {
                        InvalidFormat => "invalid id format",
                    }
                }
            }

            #[cfg(all(feature = "model", feature = "utils"))]
            impl FromStr for $id {
                type Err = $err;

                fn from_str(s: &str) -> StdResult<Self, Self::Err> {
                    Ok(match utils::parse_username(s) {
                        Some(id) => $id(id),
                        None => s.parse::<u64>().map($id).map_err(|_| $err::InvalidFormat)?,
                    })
                }
            }
        )*
    };

    (struct: $($struct:ty, $id:tt, $err:ident, $invalid_variant:tt, $parse_fn:ident, $desc:expr;)*) => {
        $(
            #[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
            #[derive(Debug)]
            pub enum $err {
                NotPresentInCache,
                $invalid_variant,
            }

            #[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
            impl fmt::Display for $err {
                fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description()) }
            }

            #[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
            impl StdError for $err {
                fn description(&self) -> &str {
                    use self::$err::*;

                    match *self {
                        NotPresentInCache => "not present in cache",
                        $invalid_variant => $desc,
                    }
                }
            }

            #[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
            impl FromStr for $struct {
                type Err = $err;

                fn from_str(s: &str) -> StdResult<Self, Self::Err> {
                    match utils::$parse_fn(s) {
                        Some(x) => match $id(x).find() {
                            Some(user) => Ok(user),
                            _ => Err($err::NotPresentInCache),
                        },
                        _ => Err($err::$invalid_variant),
                    }
                }
            }
        )*
    };
}

impl_from_str! { id:
    UserId, UserIdParseError;
    RoleId, RoleIdParseError;
    ChannelId, ChannelIdParseError;
}

impl_from_str! { struct:
    Channel, ChannelId, ChannelParseError, InvalidChannel, parse_channel, "invalid channel";
    Role, RoleId, RoleParseError, InvalidRole, parse_role, "invalid role";
}

/// A version of an emoji used only when solely the Id and name are known.
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct EmojiIdentifier {
    /// The Id of the emoji.
    pub id: EmojiId,
    /// The name of the emoji. It must be at least 2 characters long and can
    /// only contain alphanumeric characters and underscores.
    pub name: String,
}

#[cfg(all(feature = "model", feature = "utils"))]
impl EmojiIdentifier {
    /// Generates a URL to the emoji's image.
    #[inline]
    pub fn url(&self) -> String { format!(cdn!("/emojis/{}.png"), self.id) }
}

#[cfg(all(feature = "model", feature = "utils"))]
impl FromStr for EmojiIdentifier {
    type Err = ();

    fn from_str(s: &str) -> StdResult<Self, ()> { utils::parse_emoji(s).ok_or_else(|| ()) }
}

/// A component that was affected during a service incident.
///
/// This is pulled from the Discord status page.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct AffectedComponent {
    pub name: String,
}

/// An incident retrieved from the Discord status page.
///
/// This is not necessarily a representation of an ongoing incident.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Incident {
    pub created_at: String,
    pub id: String,
    pub impact: String,
    pub incident_updates: Vec<IncidentUpdate>,
    pub monitoring_at: Option<String>,
    pub name: String,
    pub page_id: String,
    pub resolved_at: Option<String>,
    pub short_link: String,
    pub status: String,
    pub updated_at: String,
}

/// An update to an incident from the Discord status page.
///
/// This will typically state what new information has been discovered about an
/// incident.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct IncidentUpdate {
    pub affected_components: Vec<AffectedComponent>,
    pub body: String,
    pub created_at: String,
    pub display_at: String,
    pub id: String,
    pub incident_id: String,
    pub status: IncidentStatus,
    pub updated_at: String,
}

/// The type of status update during a service incident.
#[derive(Copy, Clone, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum IncidentStatus {
    Identified,
    Investigating,
    Monitoring,
    Postmortem,
    Resolved,
}

/// A Discord status maintenance message. This can be either for active
/// maintenances or for scheduled maintenances.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Maintenance {
    pub description: String,
    pub id: String,
    pub name: String,
    pub start: String,
    pub stop: String,
}
<|endoftext|>


<commit_before>use std::str::Chars;
use std::io::Reader;
use std::str::SendStr;
use span::Span;
use std::str::Owned;

#[deriving(PartialEq, Show)]
pub enum Token {
    Ident(String),
    Str(String),
    Keyword(SendStr),
    Colon,
    Caret,
    LParen,
    RParen,
    Noop,
    Done
}

/// The lexer is the system that has any notion of an input stream. The
/// stream can come from many sources, thus, we use the Reader trait
/// that is implemented for strings, files, tcp, etc...
///
/// The lexer should also not consume the whole stream at once, as
/// that will be a bottleneck for larger input sources.
pub struct Lexer<'a> {
    source: &'a str,
    iter: Chars<'a>,
    token: Token,
    span: Span
}

impl<'a> Lexer<'a> {
    pub fn new(source: &'a str) -> Lexer<'a> {
        Lexer {
            source: source,
            iter: source.chars(),
            token: Noop,
            span: Span::new(0, 0)
        }
    }

    /// Parse and fetch the next token in the text stream.
    /// This doesn't return the token, it stores it
    /// in the lexer.
    pub fn bump(&mut self) {
        // Fetch the next char.
        let c = match self.iter.next() {
            Some(c) => c,
            None => {
                self.token = Done;
                return;
            }
        };

        match c {
            '(' => { self.token = LParen },
            ')' => { self.token = RParen },
            '^' => { self.token = Caret },
            ':' => {
                let mut keyword = String::new();
                self.iter.next().while_some(|a| {
                    if self.is_keyword(a) {
                        keyword.push_char(a);
                        self.iter.next()
                    } else {
                        None
                    }
                });

                self.token = Keyword(Owned(keyword));
            },
            '"' => {
                let mut concat = String::new();
                self.iter.next().while_some(|a| {
                    if a == '"' {
                        None
                    } else {
                        concat.push_char(a);
                        self.iter.next()
                    }
                });

                self.token = Str(concat);
            },
            // An identifier is composed of any
            // characters that contain no whitespace
            // and doesn't start with the designated
            // characters above.
            ch => {
                let mut ident = String::new();
                ident.push_char(ch);
                self.iter.next().while_some(|a| {
                    if self.is_ident(a) {
                        ident.push_char(a);
                        self.iter.next()
                    } else {
                        None
                    }
                });

                self.token = Ident(ident);
            }
        }
    }

    /// Determine if a char is allowed within an identifier.
    pub fn is_ident(&self, ch: char) -> bool {
        if ch.is_whitespace() || ch == ':' || ch == '(' || ch == ')' || ch == '"'
            || ch == '@' || ch == '!' {
            false
        } else {
            true
        }
    }

    /// Verify if the char is within the allowed character set for keywords. This
    /// only checks a single character at a time.
    #[inline]
    pub fn is_keyword(&self, a: char) -> bool {
        if a.is_whitespace() || a == '(' || a == ')' || a == '^' || a == ':' {
            false
        } else {
            true
        }
    }

    /// Expect a given token to come next, otherwise
    /// fail. Failing will result in a formatted
    /// error message that uses the Span and lets
    /// the user know what went wrong.
    pub fn expect(&mut self, tok: &Token) {
        if self.token == *tok {
            self.bump();
        } else {
            fail!("Expected {} but found {}", *tok, self.token);
        }
    }

    /// Optionally eat a given token. If the token is not
    /// found, this will simply return false and
    /// won't fail.
    pub fn eat(&mut self, tok: &Token) -> bool {
        if self.token == *tok {
            self.bump();
            true
        } else {
            false
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use span::Span;
    use std::str::{Slice, Owned};

    #[test]
    fn noop() {
        let lexer = Lexer::new("foo");
        assert_eq!(lexer.token, Noop);
    }

    #[test]
    fn zeroed_span() {
        let lexer = Lexer::new("foo");
        assert_eq!(lexer.span, Span::new(0, 0));
    }

    #[test]
    fn bump_empty() {
        let mut lexer = Lexer::new("");
        lexer.bump();
        assert_eq!(lexer.token, Done);
    }

    #[test]
    fn bump_lparen() {
        let mut lexer = Lexer::new("(");
        lexer.bump();
        assert_eq!(lexer.token, LParen);
    }

    #[test]
    fn bump_rparen() {
        let mut lexer = Lexer::new(")");
        lexer.bump();
        assert_eq!(lexer.token, RParen);
    }

    #[test]
    fn bump_simple_keyword() {
        let mut lexer = Lexer::new(":foo");
        lexer.bump();
        assert_eq!(lexer.token, Keyword(Slice("foo")));
    }

    #[test]
    fn bump_keyword_dash() {
        let mut lexer = Lexer::new(":foo-");
        lexer.bump();
        assert_eq!(lexer.token, Keyword(Slice("foo-")));
    }

    #[test]
    fn bump_keyword_dash_middle() {
        let mut lexer = Lexer::new(":foo-bar");
        lexer.bump();
        assert_eq!(lexer.token, Keyword(Slice("foo-bar")));
    }

    #[test]
    fn bump_string() {
        let mut lexer = Lexer::new("\"hah-w091:--:\"");
        lexer.bump();
        assert_eq!(lexer.token, Str("hah-w091:--:".to_string()));
    }

    #[test]
    fn bump_ident() {
        let mut lexer = Lexer::new("foo-bar-&#^");
        lexer.bump();
        assert_eq!(lexer.token, Ident("foo-bar-&#^".to_string()));
    }

    #[test]
    fn should_eat_lparen() {
        let mut lexer = Lexer::new("(");
        lexer.bump();
        assert_eq!(lexer.eat(&LParen), true);
    }

    #[test]
    #[should_fail]
    fn should_expect_lparen() {
        let mut lexer = Lexer::new(")");
        lexer.bump();
        lexer.expect(&LParen);
    }
}
<commit_msg>New test<commit_after>use std::str::Chars;
use std::io::Reader;
use std::str::SendStr;
use span::Span;
use std::str::Owned;

#[deriving(PartialEq, Show)]
pub enum Token {
    Ident(String),
    Str(String),
    Keyword(SendStr),
    Colon,
    Caret,
    LParen,
    RParen,
    Noop,
    Done
}

/// The lexer is the system that has any notion of an input stream. The
/// stream can come from many sources, thus, we use the Reader trait
/// that is implemented for strings, files, tcp, etc...
///
/// The lexer should also not consume the whole stream at once, as
/// that will be a bottleneck for larger input sources.
pub struct Lexer<'a> {
    source: &'a str,
    iter: Chars<'a>,
    token: Token,
    span: Span
}

impl<'a> Lexer<'a> {
    pub fn new(source: &'a str) -> Lexer<'a> {
        Lexer {
            source: source,
            iter: source.chars(),
            token: Noop,
            span: Span::new(0, 0)
        }
    }

    /// Parse and fetch the next token in the text stream.
    /// This doesn't return the token, it stores it
    /// in the lexer.
    pub fn bump(&mut self) {
        // Fetch the next char.
        let c = match self.iter.next() {
            Some(c) => c,
            None => {
                self.token = Done;
                return;
            }
        };

        match c {
            '(' => { self.token = LParen },
            ')' => { self.token = RParen },
            '^' => { self.token = Caret },
            ':' => {
                let mut keyword = String::new();
                self.iter.next().while_some(|a| {
                    if self.is_keyword(a) {
                        keyword.push_char(a);
                        self.iter.next()
                    } else {
                        None
                    }
                });

                self.token = Keyword(Owned(keyword));
            },
            '"' => {
                let mut concat = String::new();
                self.iter.next().while_some(|a| {
                    if a == '"' {
                        None
                    } else {
                        concat.push_char(a);
                        self.iter.next()
                    }
                });

                self.token = Str(concat);
            },
            // An identifier is composed of any
            // characters that contain no whitespace
            // and doesn't start with the designated
            // characters above.
            ch => {
                let mut ident = String::new();
                ident.push_char(ch);
                self.iter.next().while_some(|a| {
                    if self.is_ident(a) {
                        ident.push_char(a);
                        self.iter.next()
                    } else {
                        None
                    }
                });

                self.token = Ident(ident);
            }
        }
    }

    /// Determine if a char is allowed within an identifier.
    pub fn is_ident(&self, ch: char) -> bool {
        if ch.is_whitespace() || ch == ':' || ch == '(' || ch == ')' || ch == '"'
            || ch == '@' || ch == '!' {
            false
        } else {
            true
        }
    }

    /// Verify if the char is within the allowed character set for keywords. This
    /// only checks a single character at a time.
    #[inline]
    pub fn is_keyword(&self, a: char) -> bool {
        if a.is_whitespace() || a == '(' || a == ')' || a == '^' || a == ':' {
            false
        } else {
            true
        }
    }

    /// Expect a given token to come next, otherwise
    /// fail. Failing will result in a formatted
    /// error message that uses the Span and lets
    /// the user know what went wrong.
    pub fn expect(&mut self, tok: &Token) {
        if self.token == *tok {
            self.bump();
        } else {
            fail!("Expected {} but found {}", *tok, self.token);
        }
    }

    /// Optionally eat a given token. If the token is not
    /// found, this will simply return false and
    /// won't fail.
    pub fn eat(&mut self, tok: &Token) -> bool {
        if self.token == *tok {
            self.bump();
            true
        } else {
            false
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use span::Span;
    use std::str::{Slice, Owned};

    #[test]
    fn noop() {
        let lexer = Lexer::new("foo");
        assert_eq!(lexer.token, Noop);
    }

    #[test]
    fn zeroed_span() {
        let lexer = Lexer::new("foo");
        assert_eq!(lexer.span, Span::new(0, 0));
    }

    #[test]
    fn bump_empty() {
        let mut lexer = Lexer::new("");
        lexer.bump();
        assert_eq!(lexer.token, Done);
    }

    #[test]
    fn bump_lparen() {
        let mut lexer = Lexer::new("(");
        lexer.bump();
        assert_eq!(lexer.token, LParen);
    }

    #[test]
    fn bump_rparen() {
        let mut lexer = Lexer::new(")");
        lexer.bump();
        assert_eq!(lexer.token, RParen);
    }

    #[test]
    fn bump_simple_keyword() {
        let mut lexer = Lexer::new(":foo");
        lexer.bump();
        assert_eq!(lexer.token, Keyword(Slice("foo")));
    }

    #[test]
    fn bump_keyword_dash() {
        let mut lexer = Lexer::new(":foo-");
        lexer.bump();
        assert_eq!(lexer.token, Keyword(Slice("foo-")));
    }

    #[test]
    fn bump_keyword_dash_middle() {
        let mut lexer = Lexer::new(":foo-bar");
        lexer.bump();
        assert_eq!(lexer.token, Keyword(Slice("foo-bar")));
    }

    #[test]
    fn bump_string() {
        let mut lexer = Lexer::new("\"hah-w091:--:\"");
        lexer.bump();
        assert_eq!(lexer.token, Str("hah-w091:--:".to_string()));
    }

    #[test]
    fn bump_ident() {
        let mut lexer = Lexer::new("foo-bar-&#^");
        lexer.bump();
        assert_eq!(lexer.token, Ident("foo-bar-&#^".to_string()));
    }

    #[test]
    fn should_eat_lparen() {
        let mut lexer = Lexer::new("(");
        lexer.bump();
        assert_eq!(lexer.eat(&LParen), true);
    }

    #[test]
    #[should_fail]
    fn should_expect_lparen() {
        let mut lexer = Lexer::new(")");
        lexer.bump();
        lexer.expect(&LParen);
    }

    #[test]
    fn should_expect() {
        let mut lexer = Lexer::new("(");
        lexer.bump();
        lexer.expect(&LParen);
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2014 The Rust Project Developers.
// Copyright 2018 The Uuid Project Developers.
//
// See the COPYRIGHT file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! [`Uuid`] parsing constructs and utilities.
//!
//! [`Uuid`]: ../struct.Uuid.html

mod core_support;
#[cfg(feature = "std")]
mod std_support;

/// The expected value.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Expected {
    /// Expected any one of the given values.
    Any(&'static [usize]),
    /// Expected the given value.
    Exact(usize),
    /// Expected any values in the given range.
    Range {
        /// The minimum expected value.
        min: usize,
        /// The maximum expected value.
        max: usize,
    },
}

/// An error that can occur while parsing a [`Uuid`] string.
///
/// [`Uuid`]: ../struct.Uuid.html
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum UuidParseError {
    /// Invalid character in the [`Uuid`] string.
    ///
    /// [`Uuid`]: ../struct.Uuid.html
    InvalidCharacter {
        /// The expected characters.
        expected: &'static str,
        /// The invalid character found.
        found: char,
        /// The invalid character position.
        index: usize,
    },
    /// Invalid number of segments in the [`Uuid`] string.
    ///
    /// [`Uuid`]: ../struct.Uuid.html
    InvalidGroupCount {
        /// The expected number of segments.
        // TODO: explain multiple segment count.
        // BODY: Parsers can expect a range of Uuid segment count.
        //       This needs to be expanded on.
        expected: Expected,
        /// The number of segments found.
        found: usize,
    },
    /// Invalid length of a segment in a [`Uuid`] string.
    ///
    /// [`Uuid`]: ../struct.Uuid.html
    InvalidGroupLength {
        /// The expected length of the segment.
        expected: Expected,
        /// The length of segment found.
        found: usize,
        /// The segment with invalid length.
        group: usize,
    },
    /// Invalid length of the [`Uuid`] string.
    ///
    /// [`Uuid`]: ../struct.Uuid.html
    InvalidLength {
        /// The expected length(s).
        // TODO: explain multiple lengths.
        // BODY: Parsers can expect a range of Uuid lenghts.
        //       This needs to be expanded on.
        expected: Expected,
        /// The invalid length found.
        found: usize,
    },
}

impl UuidParseError {
    fn _description(&self) -> &str {
        match *self {
            UuidParseError::InvalidCharacter { .. } => "invalid character",
            UuidParseError::InvalidGroupCount { .. } => {
                "invalid number of groups"
            }
            UuidParseError::InvalidGroupLength { .. } => "invalid group length",
            UuidParseError::InvalidLength { .. } => "invalid length",
        }
    }
}

/// Check if the length matches any of the given criteria lengths.
pub fn len_matches_any(len: usize, crits: &[usize]) -> bool {
    for crit in crits {
        if len == *crit {
            return true;
        }
    }

    false
}

/// Check if the length matches any criteria lengths in the given range
/// (inclusive).
pub fn len_matches_range(len: usize, min: usize, max: usize) -> bool {
    for crit in min..(max + 1) {
        if len == crit {
            return true;
        }
    }

    false
}

// Accumulated length of each hyphenated group in hex digits.
pub(crate) const ACC_GROUP_LENS: [usize; 5] = [8, 12, 16, 20, 32];

// Length of each hyphenated group in hex digits.
pub(crate) const GROUP_LENS: [usize; 5] = [8, 4, 4, 4, 12];
<commit_msg>make the current 2 parser functions crate public<commit_after>// Copyright 2013-2014 The Rust Project Developers.
// Copyright 2018 The Uuid Project Developers.
//
// See the COPYRIGHT file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! [`Uuid`] parsing constructs and utilities.
//!
//! [`Uuid`]: ../struct.Uuid.html

mod core_support;
#[cfg(feature = "std")]
mod std_support;

/// The expected value.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Expected {
    /// Expected any one of the given values.
    Any(&'static [usize]),
    /// Expected the given value.
    Exact(usize),
    /// Expected any values in the given range.
    Range {
        /// The minimum expected value.
        min: usize,
        /// The maximum expected value.
        max: usize,
    },
}

/// An error that can occur while parsing a [`Uuid`] string.
///
/// [`Uuid`]: ../struct.Uuid.html
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum UuidParseError {
    /// Invalid character in the [`Uuid`] string.
    ///
    /// [`Uuid`]: ../struct.Uuid.html
    InvalidCharacter {
        /// The expected characters.
        expected: &'static str,
        /// The invalid character found.
        found: char,
        /// The invalid character position.
        index: usize,
    },
    /// Invalid number of segments in the [`Uuid`] string.
    ///
    /// [`Uuid`]: ../struct.Uuid.html
    InvalidGroupCount {
        /// The expected number of segments.
        // TODO: explain multiple segment count.
        // BODY: Parsers can expect a range of Uuid segment count.
        //       This needs to be expanded on.
        expected: Expected,
        /// The number of segments found.
        found: usize,
    },
    /// Invalid length of a segment in a [`Uuid`] string.
    ///
    /// [`Uuid`]: ../struct.Uuid.html
    InvalidGroupLength {
        /// The expected length of the segment.
        expected: Expected,
        /// The length of segment found.
        found: usize,
        /// The segment with invalid length.
        group: usize,
    },
    /// Invalid length of the [`Uuid`] string.
    ///
    /// [`Uuid`]: ../struct.Uuid.html
    InvalidLength {
        /// The expected length(s).
        // TODO: explain multiple lengths.
        // BODY: Parsers can expect a range of Uuid lenghts.
        //       This needs to be expanded on.
        expected: Expected,
        /// The invalid length found.
        found: usize,
    },
}

impl UuidParseError {
    fn _description(&self) -> &str {
        match *self {
            UuidParseError::InvalidCharacter { .. } => "invalid character",
            UuidParseError::InvalidGroupCount { .. } => {
                "invalid number of groups"
            }
            UuidParseError::InvalidGroupLength { .. } => "invalid group length",
            UuidParseError::InvalidLength { .. } => "invalid length",
        }
    }
}

/// Check if the length matches any of the given criteria lengths.
pub(crate) fn len_matches_any(len: usize, crits: &[usize]) -> bool {
    for crit in crits {
        if len == *crit {
            return true;
        }
    }

    false
}

/// Check if the length matches any criteria lengths in the given range
/// (inclusive).
pub(crate) fn len_matches_range(len: usize, min: usize, max: usize) -> bool {
    for crit in min..(max + 1) {
        if len == crit {
            return true;
        }
    }

    false
}

// Accumulated length of each hyphenated group in hex digits.
pub(crate) const ACC_GROUP_LENS: [usize; 5] = [8, 12, 16, 20, 32];

// Length of each hyphenated group in hex digits.
pub(crate) const GROUP_LENS: [usize; 5] = [8, 4, 4, 4, 12];
<|endoftext|>


<commit_before>use cast::transmute;
use cmp::Eq;
use num::from_int;
use ptr::to_unsafe_ptr;
use vec::raw::buf_as_slice;
use std::cmp::FuzzyEq;

use funs::exp::Exp;
use math::*;
use matrix::{Mat3, Mat4};
use ncast::*;
use vector::Vec3;

// These quaternion type aliases are not actually specified in the GLSL spec
// but they follow the same nomenclature

pub type quat4  = Quat<f32>;         /// a single-precision floating-point quaternion
pub type dquat4 = Quat<f64>;         /// a double-precision floating-point quaternion

//
//  Quaternion
//
pub trait Quaternion<T> {
    pure fn dim() -> uint;

    pure fn mul_t(value: T) -> self;
    pure fn div_t(value: T) -> self;

    // pure fn mul_v(other: &Vec3) -> Vec3;

    pure fn add_q(other: &self) -> self;
    pure fn sub_q(other: &self) -> self;
    pure fn mul_q(other: &self) -> self;

    pure fn conjugate() -> self;
    pure fn inverse() -> self;
    pure fn length2() -> T;
    pure fn length() -> T;

    pure fn to_Mat3() -> Mat3<T>;
    pure fn to_Mat4() -> Mat4<T>;
}

pub trait ToQuat<T> {
    pure fn to_Quat() -> Quat<T>;
}

pub struct Quat<T> { w: T, x: T, y: T, z: T }

pub mod Quat {

    #[inline(always)]
    pub pure fn new<T>(w: T, x: T, y: T, z: T) -> Quat<T> {
        Quat { w: move w, x: move x, y: move y, z: move z }
    }

    #[inline(always)]
    pub pure fn from_sv<T:Copy>(s: T, v: Vec3<T>) -> Quat<T> {
        Quat::new(s, v.x, v.y, v.z)
    }

    #[inline(always)]
    pub pure fn zero<T:Copy NumCast>() -> Quat<T> {
        let _0 = cast(0);
        Quat::new(_0, _0, _0, _0)
    }

    #[inline(always)]
    pub pure fn identity<T:Copy NumCast>() -> Quat<T> {
        let _0 = cast(0);
        Quat::new(cast(1), _0, _0, _0)
    }
}

pub impl<T:Copy Num NumCast Exp FuzzyEq> Quat<T>: Quaternion<T> {
    #[inline(always)]
    pure fn dim() -> uint { 4 }

    #[inline(always)]
    pure fn mul_t(value: T) -> Quat<T> {
        Quat::new(self[0] * value,
                  self[1] * value,
                  self[2] * value,
                  self[3] * value)
    }

    #[inline(always)]
    pure fn div_t(value: T) -> Quat<T> {
        Quat::new(self[0] / value,
                  self[1] / value,
                  self[2] / value,
                  self[3] / value)
    }

    #[inline(always)]
    pure fn add_q(other: &Quat<T>) -> Quat<T> {
        Quat::new(self[0] + other[0],
                  self[1] + other[1],
                  self[2] + other[2],
                  self[3] + other[3])
    }

    #[inline(always)]
    pure fn sub_q(other: &Quat<T>) -> Quat<T> {
        Quat::new(self[0] - other[0],
                  self[1] - other[1],
                  self[2] - other[2],
                  self[3] - other[3])
    }

    #[inline(always)]
    pure fn mul_q(other: &Quat<T>) -> Quat<T> {
        Quat::new(self.w * other.w - self.x * other.x - self.y * other.y - self.z * other.z,
                  self.w * other.x + self.x * other.w + self.y * other.z - self.z * other.y,
                  self.w * other.y + self.y * other.w + self.z * other.x - self.x * other.z,
                  self.w * other.z + self.z * other.w + self.x * other.y - self.y * other.x)
    }

    #[inline(always)]
    pure fn conjugate() -> Quat<T> {
        Quat::new(self.w, -self.x, -self.y, -self.z)
    }

    #[inline(always)]
    pure fn inverse() -> Quat<T> {
        let mut n: T = from_int(1);
        n /= self.length2();
        self.conjugate().mul_t(n)
    }

    #[inline(always)]
    pure fn length2() -> T {
        self.w * self.w +
        self.x * self.x +
        self.y * self.y +
        self.z * self.z
    }

    #[inline(always)]
    pure fn length() -> T {
        self.length2().sqrt()
    }

    #[inline(always)]
    pure fn to_Mat3() -> Mat3<T> {
        let x2 = self.x + self.x;
        let y2 = self.y + self.y;
        let z2 = self.z + self.z;

        let xx2 = x2 * self.x;
        let xy2 = x2 * self.y;
        let xz2 = x2 * self.z;

        let yy2 = y2 * self.y;
        let yz2 = y2 * self.z;
        let zz2 = z2 * self.z;

        let wy2 = y2 * self.w;
        let wz2 = z2 * self.w;
        let wx2 = x2 * self.w;

        let _1: T = from_int(1);

        Mat3::new(_1 - yy2 - zz2,      xy2 - wz2,      xz2 + wy2,
                       xy2 + wz2, _1 - xx2 - zz2,      yz2 - wx2,
                       xz2 - wy2,      yz2 + wx2, _1 - xx2 - yy2)
    }

    #[inline(always)]
    pure fn to_Mat4() -> Mat4<T> {
        self.to_Mat3().to_Mat4()
    }
}

pub impl<T:Copy> Quat<T>: Index<uint, T> {
    #[inline(always)]
    pure fn index(i: uint) -> T {
        unsafe { do buf_as_slice(
            transmute::<*Quat<T>, *T>(
                to_unsafe_ptr(&self)), 4) |slice| { slice[i] }
        }
    }
}

pub impl<T:Copy Neg<T>> Quat<T>: Neg<Quat<T>> {
    #[inline(always)]
    pure fn neg() -> Quat<T> {
        Quat::new(-self[0], -self[1], -self[2], -self[3])
    }
}

// TODO: make work for T:Integer
pub impl<T:Copy FuzzyEq> Quat<T>: Eq {
    #[inline(always)]
    pure fn eq(other: &Quat<T>) -> bool {
        self.fuzzy_eq(other)
    }

    #[inline(always)]
    pure fn ne(other: &Quat<T>) -> bool {
        !(self == *other)
    }
}

pub impl<T:Copy Eq> Quat<T>: ExactEq {
    #[inline(always)]
    pure fn exact_eq(other: &Quat<T>) -> bool {
        self[0] == other[0] &&
        self[1] == other[1] &&
        self[2] == other[2] &&
        self[3] == other[3]
    }
}

pub impl<T:Copy FuzzyEq> Quat<T>: FuzzyEq {
    #[inline(always)]
    pure fn fuzzy_eq(other: &Quat<T>) -> bool {
        self[0].fuzzy_eq(&other[0]) &&
        self[1].fuzzy_eq(&other[1]) &&
        self[2].fuzzy_eq(&other[2]) &&
        self[3].fuzzy_eq(&other[3])
    }
}

pub impl<T:Copy> Quat<T>: ToPtr<T> {
    #[inline(always)]
    pure fn to_ptr() -> *T {
        to_unsafe_ptr(&self[0])
    }
}<commit_msg>Add dot product<commit_after>use cast::transmute;
use cmp::Eq;
use num::from_int;
use ptr::to_unsafe_ptr;
use vec::raw::buf_as_slice;
use std::cmp::FuzzyEq;

use funs::exp::Exp;
use math::*;
use matrix::{Mat3, Mat4};
use ncast::*;
use vector::Vec3;

// These quaternion type aliases are not actually specified in the GLSL spec
// but they follow the same nomenclature

pub type quat4  = Quat<f32>;         /// a single-precision floating-point quaternion
pub type dquat4 = Quat<f64>;         /// a double-precision floating-point quaternion

//
//  Quaternion
//
pub trait Quaternion<T> {
    pure fn dim() -> uint;

    pure fn mul_t(value: T) -> self;
    pure fn div_t(value: T) -> self;

    // pure fn mul_v(other: &Vec3) -> Vec3;

    pure fn add_q(other: &self) -> self;
    pure fn sub_q(other: &self) -> self;
    pure fn mul_q(other: &self) -> self;

    pure fn dot(other: &self) -> T;

    pure fn conjugate() -> self;
    pure fn inverse() -> self;
    pure fn length2() -> T;
    pure fn length() -> T;

    pure fn to_Mat3() -> Mat3<T>;
    pure fn to_Mat4() -> Mat4<T>;
}

pub trait ToQuat<T> {
    pure fn to_Quat() -> Quat<T>;
}

pub struct Quat<T> { w: T, x: T, y: T, z: T }

pub mod Quat {

    #[inline(always)]
    pub pure fn new<T>(w: T, x: T, y: T, z: T) -> Quat<T> {
        Quat { w: move w, x: move x, y: move y, z: move z }
    }

    #[inline(always)]
    pub pure fn from_sv<T:Copy>(s: T, v: Vec3<T>) -> Quat<T> {
        Quat::new(s, v.x, v.y, v.z)
    }

    #[inline(always)]
    pub pure fn zero<T:Copy NumCast>() -> Quat<T> {
        let _0 = cast(0);
        Quat::new(_0, _0, _0, _0)
    }

    #[inline(always)]
    pub pure fn identity<T:Copy NumCast>() -> Quat<T> {
        let _0 = cast(0);
        Quat::new(cast(1), _0, _0, _0)
    }
}

pub impl<T:Copy Num NumCast Exp FuzzyEq> Quat<T>: Quaternion<T> {
    #[inline(always)]
    pure fn dim() -> uint { 4 }

    #[inline(always)]
    pure fn mul_t(value: T) -> Quat<T> {
        Quat::new(self[0] * value,
                  self[1] * value,
                  self[2] * value,
                  self[3] * value)
    }

    #[inline(always)]
    pure fn div_t(value: T) -> Quat<T> {
        Quat::new(self[0] / value,
                  self[1] / value,
                  self[2] / value,
                  self[3] / value)
    }

    #[inline(always)]
    pure fn add_q(other: &Quat<T>) -> Quat<T> {
        Quat::new(self[0] + other[0],
                  self[1] + other[1],
                  self[2] + other[2],
                  self[3] + other[3])
    }

    #[inline(always)]
    pure fn sub_q(other: &Quat<T>) -> Quat<T> {
        Quat::new(self[0] - other[0],
                  self[1] - other[1],
                  self[2] - other[2],
                  self[3] - other[3])
    }

    #[inline(always)]
    pure fn mul_q(other: &Quat<T>) -> Quat<T> {
        Quat::new(self.w * other.w - self.x * other.x - self.y * other.y - self.z * other.z,
                  self.w * other.x + self.x * other.w + self.y * other.z - self.z * other.y,
                  self.w * other.y + self.y * other.w + self.z * other.x - self.x * other.z,
                  self.w * other.z + self.z * other.w + self.x * other.y - self.y * other.x)
    }

    #[inline(always)]
    pure fn dot(other: &Quat<T>) -> T {
        self.w * other.w +
        self.x * other.x +
        self.y * other.y +
        self.z * other.z
    }

    #[inline(always)]
    pure fn conjugate() -> Quat<T> {
        Quat::new(self.w, -self.x, -self.y, -self.z)
    }

    #[inline(always)]
    pure fn inverse() -> Quat<T> {
        let mut n: T = from_int(1);
        n /= self.length2();
        self.conjugate().mul_t(n)
    }

    #[inline(always)]
    pure fn length2() -> T {
        self.w * self.w +
        self.x * self.x +
        self.y * self.y +
        self.z * self.z
    }

    #[inline(always)]
    pure fn length() -> T {
        self.length2().sqrt()
    }

    #[inline(always)]
    pure fn to_Mat3() -> Mat3<T> {
        let x2 = self.x + self.x;
        let y2 = self.y + self.y;
        let z2 = self.z + self.z;

        let xx2 = x2 * self.x;
        let xy2 = x2 * self.y;
        let xz2 = x2 * self.z;

        let yy2 = y2 * self.y;
        let yz2 = y2 * self.z;
        let zz2 = z2 * self.z;

        let wy2 = y2 * self.w;
        let wz2 = z2 * self.w;
        let wx2 = x2 * self.w;

        let _1: T = from_int(1);

        Mat3::new(_1 - yy2 - zz2,      xy2 - wz2,      xz2 + wy2,
                       xy2 + wz2, _1 - xx2 - zz2,      yz2 - wx2,
                       xz2 - wy2,      yz2 + wx2, _1 - xx2 - yy2)
    }

    #[inline(always)]
    pure fn to_Mat4() -> Mat4<T> {
        self.to_Mat3().to_Mat4()
    }
}

pub impl<T:Copy> Quat<T>: Index<uint, T> {
    #[inline(always)]
    pure fn index(i: uint) -> T {
        unsafe { do buf_as_slice(
            transmute::<*Quat<T>, *T>(
                to_unsafe_ptr(&self)), 4) |slice| { slice[i] }
        }
    }
}

pub impl<T:Copy Neg<T>> Quat<T>: Neg<Quat<T>> {
    #[inline(always)]
    pure fn neg() -> Quat<T> {
        Quat::new(-self[0], -self[1], -self[2], -self[3])
    }
}

// TODO: make work for T:Integer
pub impl<T:Copy FuzzyEq> Quat<T>: Eq {
    #[inline(always)]
    pure fn eq(other: &Quat<T>) -> bool {
        self.fuzzy_eq(other)
    }

    #[inline(always)]
    pure fn ne(other: &Quat<T>) -> bool {
        !(self == *other)
    }
}

pub impl<T:Copy Eq> Quat<T>: ExactEq {
    #[inline(always)]
    pure fn exact_eq(other: &Quat<T>) -> bool {
        self[0] == other[0] &&
        self[1] == other[1] &&
        self[2] == other[2] &&
        self[3] == other[3]
    }
}

pub impl<T:Copy FuzzyEq> Quat<T>: FuzzyEq {
    #[inline(always)]
    pure fn fuzzy_eq(other: &Quat<T>) -> bool {
        self[0].fuzzy_eq(&other[0]) &&
        self[1].fuzzy_eq(&other[1]) &&
        self[2].fuzzy_eq(&other[2]) &&
        self[3].fuzzy_eq(&other[3])
    }
}

pub impl<T:Copy> Quat<T>: ToPtr<T> {
    #[inline(always)]
    pure fn to_ptr() -> *T {
        to_unsafe_ptr(&self[0])
    }
}<|endoftext|>


<commit_before>//! # Schemes
//! A scheme is a primitive for handling filesystem syscalls in Redox.
//! Schemes accept paths from the kernel for `open`, and file descriptors that they generate
//! are then passed for operations like `close`, `read`, `write`, etc.
//!
//! The kernel validates paths and file descriptors before they are passed to schemes,
//! also stripping the scheme identifier of paths if necessary.

use alloc::sync::Arc;
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use core::sync::atomic::AtomicUsize;
use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};

use crate::syscall::error::*;
use crate::syscall::scheme::Scheme;

#[cfg(feature = "acpi")]
use self::acpi::AcpiScheme;

use self::debug::DebugScheme;
use self::event::EventScheme;
use self::initfs::InitFsScheme;
use self::irq::IrqScheme;
use self::itimer::ITimerScheme;
use self::memory::MemoryScheme;
use self::pipe::PipeScheme;
use self::proc::ProcScheme;
use self::root::RootScheme;
use self::serio::SerioScheme;
use self::sys::SysScheme;
use self::time::TimeScheme;

/// When compiled with the "acpi" feature - `acpi:` - allows drivers to read a limited set of ACPI tables.
#[cfg(feature = "acpi")]
pub mod acpi;

/// `debug:` - provides access to serial console
pub mod debug;

/// `event:` - allows reading of `Event`s which are registered using `fevent`
pub mod event;

/// `initfs:` - a readonly filesystem used for initializing the system
pub mod initfs;

/// `irq:` - allows userspace handling of IRQs
pub mod irq;

/// `itimer:` - support for getitimer and setitimer
pub mod itimer;

/// When compiled with "live" feature - `disk:` - embedded filesystem for live disk
#[cfg(feature="live")]
pub mod live;

/// `memory:` - a scheme for accessing physical memory
pub mod memory;

/// `pipe:` - used internally by the kernel to implement `pipe`
pub mod pipe;

/// `proc:` - allows tracing processes and reading/writing their memory
pub mod proc;

/// `:` - allows the creation of userspace schemes, tightly dependent on `user`
pub mod root;

/// `serio:` - provides access to ps/2 devices
pub mod serio;

/// `sys:` - system information, such as the context list and scheme list
pub mod sys;

/// `time:` - allows reading time, setting timeouts and getting events when they are met
pub mod time;

/// A wrapper around userspace schemes, tightly dependent on `root`
pub mod user;

/// Limit on number of schemes
pub const SCHEME_MAX_SCHEMES: usize = 65_536;

// Unique identifier for a scheme namespace.
int_like!(SchemeNamespace, AtomicSchemeNamespace, usize, AtomicUsize);

// Unique identifier for a scheme.
int_like!(SchemeId, AtomicSchemeId, usize, AtomicUsize);

// Unique identifier for a file descriptor.
int_like!(FileHandle, AtomicFileHandle, usize, AtomicUsize);

pub struct SchemeIter<'a> {
    inner: Option<::alloc::collections::btree_map::Iter<'a, Box<[u8]>, SchemeId>>
}

impl<'a> Iterator for SchemeIter<'a> {
    type Item = (&'a Box<[u8]>, &'a SchemeId);

    fn next(&mut self) -> Option<Self::Item> {
        self.inner.as_mut().and_then(|iter| iter.next())
    }
}

/// Scheme list type
pub struct SchemeList {
    map: BTreeMap<SchemeId, Arc<dyn Scheme + Send + Sync>>,
    names: BTreeMap<SchemeNamespace, BTreeMap<Box<[u8]>, SchemeId>>,
    next_ns: usize,
    next_id: usize
}

impl SchemeList {
    /// Create a new scheme list.
    pub fn new() -> Self {
        let mut list = SchemeList {
            map: BTreeMap::new(),
            names: BTreeMap::new(),
            // Scheme namespaces always start at 1. 0 is a reserved namespace, the null namespace
            next_ns: 1,
            next_id: 1
        };
        list.new_root();
        list
    }

    /// Initialize a new namespace
    fn new_ns(&mut self) -> SchemeNamespace {
        let ns = SchemeNamespace(self.next_ns);
        self.next_ns += 1;
        self.names.insert(ns, BTreeMap::new());

        self.insert(ns, Box::new(*b""), |scheme_id| Arc::new(RootScheme::new(ns, scheme_id))).unwrap();
        self.insert(ns, Box::new(*b"event"), |_| Arc::new(EventScheme)).unwrap();
        self.insert(ns, Box::new(*b"itimer"), |_| Arc::new(ITimerScheme::new())).unwrap();
        self.insert(ns, Box::new(*b"memory"), |_| Arc::new(MemoryScheme::new())).unwrap();
        self.insert(ns, Box::new(*b"sys"), |_| Arc::new(SysScheme::new())).unwrap();
        self.insert(ns, Box::new(*b"time"), |scheme_id| Arc::new(TimeScheme::new(scheme_id))).unwrap();

        ns
    }

    /// Initialize the root namespace
    fn new_root(&mut self) {
        // Do common namespace initialization
        let ns = self.new_ns();

        // These schemes should only be available on the root
        #[cfg(feature = "acpi")] {
            self.insert(ns, Box::new(*b"acpi"), |_| Arc::new(AcpiScheme::new())).unwrap();
        }
        self.insert(ns, Box::new(*b"debug"), |scheme_id| Arc::new(DebugScheme::new(scheme_id))).unwrap();
        self.insert(ns, Box::new(*b"initfs"), |_| Arc::new(InitFsScheme::new())).unwrap();
        self.insert(ns, Box::new(*b"irq"), |scheme_id| Arc::new(IrqScheme::new(scheme_id))).unwrap();
        self.insert(ns, Box::new(*b"proc"), |scheme_id| Arc::new(ProcScheme::new(scheme_id))).unwrap();
        self.insert(ns, Box::new(*b"serio"), |scheme_id| Arc::new(SerioScheme::new(scheme_id))).unwrap();

        #[cfg(feature = "live")] {
            self.insert(ns, Box::new(*b"disk/live"), |_| Arc::new(self::live::DiskScheme::new())).unwrap();
        }

        // Pipe is special and needs to be in the root namespace
        self.insert(ns, Box::new(*b"pipe"), |scheme_id| Arc::new(PipeScheme::new(scheme_id))).unwrap();
    }

    pub fn make_ns(&mut self, from: SchemeNamespace, names: &[&[u8]]) -> Result<SchemeNamespace> {
        // Create an empty namespace
        let to = self.new_ns();

        // Copy requested scheme IDs
        for name in names.iter() {
            let id = if let Some((id, _scheme)) = self.get_name(from, name) {
                id
            } else {
                return Err(Error::new(ENODEV));
            };

            if let Some(ref mut names) = self.names.get_mut(&to) {
                assert!(names.insert(name.to_vec().into_boxed_slice(), id).is_none());
            } else {
                panic!("scheme namespace not found");
            }
        }

        Ok(to)
    }

    pub fn iter(&self) -> ::alloc::collections::btree_map::Iter<SchemeId, Arc<dyn Scheme + Send + Sync>> {
        self.map.iter()
    }

    pub fn iter_name(&self, ns: SchemeNamespace) -> SchemeIter {
        SchemeIter {
            inner: self.names.get(&ns).map(|names| names.iter())
        }
    }

    /// Get the nth scheme.
    pub fn get(&self, id: SchemeId) -> Option<&Arc<dyn Scheme + Send + Sync>> {
        self.map.get(&id)
    }

    pub fn get_name(&self, ns: SchemeNamespace, name: &[u8]) -> Option<(SchemeId, &Arc<dyn Scheme + Send + Sync>)> {
        if let Some(names) = self.names.get(&ns) {
            if let Some(&id) = names.get(name) {
                return self.get(id).map(|scheme| (id, scheme));
            }
        }
        None
    }

    /// Create a new scheme.
    pub fn insert<F>(&mut self, ns: SchemeNamespace, name: Box<[u8]>, scheme_fn: F) -> Result<SchemeId>
        where F: Fn(SchemeId) -> Arc<dyn Scheme + Send + Sync>
    {
        if let Some(names) = self.names.get(&ns) {
            if names.contains_key(&name) {
                return Err(Error::new(EEXIST));
            }
        }

        if self.next_id >= SCHEME_MAX_SCHEMES {
            self.next_id = 1;
        }

        while self.map.contains_key(&SchemeId(self.next_id)) {
            self.next_id += 1;
        }

        /* Allow scheme list to grow if required
        if self.next_id >= SCHEME_MAX_SCHEMES {
            return Err(Error::new(EAGAIN));
        }
        */

        let id = SchemeId(self.next_id);
        self.next_id += 1;

        let scheme = scheme_fn(id);

        assert!(self.map.insert(id, scheme).is_none());
        if let Some(ref mut names) = self.names.get_mut(&ns) {
            assert!(names.insert(name, id).is_none());
        } else {
            // Nonexistent namespace, posssibly null namespace
            return Err(Error::new(ENODEV));
        }
        Ok(id)
    }

    /// Remove a scheme
    pub fn remove(&mut self, id: SchemeId) {
        assert!(self.map.remove(&id).is_some());
        for (_ns, names) in self.names.iter_mut() {
            let mut remove = Vec::with_capacity(1);
            for (name, name_id) in names.iter() {
                if name_id == &id {
                    remove.push(name.clone());
                }
            }
            for name in remove {
                assert!(names.remove(&name).is_some());
            }
        }
    }
}

/// Schemes list
static SCHEMES: Once<RwLock<SchemeList>> = Once::new();

/// Initialize schemes, called if needed
fn init_schemes() -> RwLock<SchemeList> {
    RwLock::new(SchemeList::new())
}

/// Get the global schemes list, const
pub fn schemes() -> RwLockReadGuard<'static, SchemeList> {
    SCHEMES.call_once(init_schemes).read()
}

/// Get the global schemes list, mutable
pub fn schemes_mut() -> RwLockWriteGuard<'static, SchemeList> {
    SCHEMES.call_once(init_schemes).write()
}
<commit_msg>Add memory: to null namespace, temporarily<commit_after>//! # Schemes
//! A scheme is a primitive for handling filesystem syscalls in Redox.
//! Schemes accept paths from the kernel for `open`, and file descriptors that they generate
//! are then passed for operations like `close`, `read`, `write`, etc.
//!
//! The kernel validates paths and file descriptors before they are passed to schemes,
//! also stripping the scheme identifier of paths if necessary.

use alloc::sync::Arc;
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use core::sync::atomic::AtomicUsize;
use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};

use crate::syscall::error::*;
use crate::syscall::scheme::Scheme;

#[cfg(feature = "acpi")]
use self::acpi::AcpiScheme;

use self::debug::DebugScheme;
use self::event::EventScheme;
use self::initfs::InitFsScheme;
use self::irq::IrqScheme;
use self::itimer::ITimerScheme;
use self::memory::MemoryScheme;
use self::pipe::PipeScheme;
use self::proc::ProcScheme;
use self::root::RootScheme;
use self::serio::SerioScheme;
use self::sys::SysScheme;
use self::time::TimeScheme;

/// When compiled with the "acpi" feature - `acpi:` - allows drivers to read a limited set of ACPI tables.
#[cfg(feature = "acpi")]
pub mod acpi;

/// `debug:` - provides access to serial console
pub mod debug;

/// `event:` - allows reading of `Event`s which are registered using `fevent`
pub mod event;

/// `initfs:` - a readonly filesystem used for initializing the system
pub mod initfs;

/// `irq:` - allows userspace handling of IRQs
pub mod irq;

/// `itimer:` - support for getitimer and setitimer
pub mod itimer;

/// When compiled with "live" feature - `disk:` - embedded filesystem for live disk
#[cfg(feature="live")]
pub mod live;

/// `memory:` - a scheme for accessing physical memory
pub mod memory;

/// `pipe:` - used internally by the kernel to implement `pipe`
pub mod pipe;

/// `proc:` - allows tracing processes and reading/writing their memory
pub mod proc;

/// `:` - allows the creation of userspace schemes, tightly dependent on `user`
pub mod root;

/// `serio:` - provides access to ps/2 devices
pub mod serio;

/// `sys:` - system information, such as the context list and scheme list
pub mod sys;

/// `time:` - allows reading time, setting timeouts and getting events when they are met
pub mod time;

/// A wrapper around userspace schemes, tightly dependent on `root`
pub mod user;

/// Limit on number of schemes
pub const SCHEME_MAX_SCHEMES: usize = 65_536;

// Unique identifier for a scheme namespace.
int_like!(SchemeNamespace, AtomicSchemeNamespace, usize, AtomicUsize);

// Unique identifier for a scheme.
int_like!(SchemeId, AtomicSchemeId, usize, AtomicUsize);

// Unique identifier for a file descriptor.
int_like!(FileHandle, AtomicFileHandle, usize, AtomicUsize);

pub struct SchemeIter<'a> {
    inner: Option<::alloc::collections::btree_map::Iter<'a, Box<[u8]>, SchemeId>>
}

impl<'a> Iterator for SchemeIter<'a> {
    type Item = (&'a Box<[u8]>, &'a SchemeId);

    fn next(&mut self) -> Option<Self::Item> {
        self.inner.as_mut().and_then(|iter| iter.next())
    }
}

/// Scheme list type
pub struct SchemeList {
    map: BTreeMap<SchemeId, Arc<dyn Scheme + Send + Sync>>,
    names: BTreeMap<SchemeNamespace, BTreeMap<Box<[u8]>, SchemeId>>,
    next_ns: usize,
    next_id: usize
}

impl SchemeList {
    /// Create a new scheme list.
    pub fn new() -> Self {
        let mut list = SchemeList {
            map: BTreeMap::new(),
            names: BTreeMap::new(),
            // Scheme namespaces always start at 1. 0 is a reserved namespace, the null namespace
            next_ns: 1,
            next_id: 1
        };
        list.new_null();
        list.new_root();
        list
    }

    /// Initialize the null namespace
    fn new_null(&mut self) {
        let ns = SchemeNamespace(0);
        self.names.insert(ns, BTreeMap::new());

        //TODO: Only memory: is in the null namespace right now. It should be removed when
        //anonymous mmap's are implemented
        self.insert(ns, Box::new(*b"memory"), |_| Arc::new(MemoryScheme::new())).unwrap();
    }

    /// Initialize a new namespace
    fn new_ns(&mut self) -> SchemeNamespace {
        let ns = SchemeNamespace(self.next_ns);
        self.next_ns += 1;
        self.names.insert(ns, BTreeMap::new());

        self.insert(ns, Box::new(*b""), |scheme_id| Arc::new(RootScheme::new(ns, scheme_id))).unwrap();
        self.insert(ns, Box::new(*b"event"), |_| Arc::new(EventScheme)).unwrap();
        self.insert(ns, Box::new(*b"itimer"), |_| Arc::new(ITimerScheme::new())).unwrap();
        self.insert(ns, Box::new(*b"memory"), |_| Arc::new(MemoryScheme::new())).unwrap();
        self.insert(ns, Box::new(*b"sys"), |_| Arc::new(SysScheme::new())).unwrap();
        self.insert(ns, Box::new(*b"time"), |scheme_id| Arc::new(TimeScheme::new(scheme_id))).unwrap();

        ns
    }

    /// Initialize the root namespace
    fn new_root(&mut self) {
        // Do common namespace initialization
        let ns = self.new_ns();

        // These schemes should only be available on the root
        #[cfg(feature = "acpi")] {
            self.insert(ns, Box::new(*b"acpi"), |_| Arc::new(AcpiScheme::new())).unwrap();
        }
        self.insert(ns, Box::new(*b"debug"), |scheme_id| Arc::new(DebugScheme::new(scheme_id))).unwrap();
        self.insert(ns, Box::new(*b"initfs"), |_| Arc::new(InitFsScheme::new())).unwrap();
        self.insert(ns, Box::new(*b"irq"), |scheme_id| Arc::new(IrqScheme::new(scheme_id))).unwrap();
        self.insert(ns, Box::new(*b"proc"), |scheme_id| Arc::new(ProcScheme::new(scheme_id))).unwrap();
        self.insert(ns, Box::new(*b"serio"), |scheme_id| Arc::new(SerioScheme::new(scheme_id))).unwrap();

        #[cfg(feature = "live")] {
            self.insert(ns, Box::new(*b"disk/live"), |_| Arc::new(self::live::DiskScheme::new())).unwrap();
        }

        // Pipe is special and needs to be in the root namespace
        self.insert(ns, Box::new(*b"pipe"), |scheme_id| Arc::new(PipeScheme::new(scheme_id))).unwrap();
    }

    pub fn make_ns(&mut self, from: SchemeNamespace, names: &[&[u8]]) -> Result<SchemeNamespace> {
        // Create an empty namespace
        let to = self.new_ns();

        // Copy requested scheme IDs
        for name in names.iter() {
            let id = if let Some((id, _scheme)) = self.get_name(from, name) {
                id
            } else {
                return Err(Error::new(ENODEV));
            };

            if let Some(ref mut names) = self.names.get_mut(&to) {
                assert!(names.insert(name.to_vec().into_boxed_slice(), id).is_none());
            } else {
                panic!("scheme namespace not found");
            }
        }

        Ok(to)
    }

    pub fn iter(&self) -> ::alloc::collections::btree_map::Iter<SchemeId, Arc<dyn Scheme + Send + Sync>> {
        self.map.iter()
    }

    pub fn iter_name(&self, ns: SchemeNamespace) -> SchemeIter {
        SchemeIter {
            inner: self.names.get(&ns).map(|names| names.iter())
        }
    }

    /// Get the nth scheme.
    pub fn get(&self, id: SchemeId) -> Option<&Arc<dyn Scheme + Send + Sync>> {
        self.map.get(&id)
    }

    pub fn get_name(&self, ns: SchemeNamespace, name: &[u8]) -> Option<(SchemeId, &Arc<dyn Scheme + Send + Sync>)> {
        if let Some(names) = self.names.get(&ns) {
            if let Some(&id) = names.get(name) {
                return self.get(id).map(|scheme| (id, scheme));
            }
        }
        None
    }

    /// Create a new scheme.
    pub fn insert<F>(&mut self, ns: SchemeNamespace, name: Box<[u8]>, scheme_fn: F) -> Result<SchemeId>
        where F: Fn(SchemeId) -> Arc<dyn Scheme + Send + Sync>
    {
        if let Some(names) = self.names.get(&ns) {
            if names.contains_key(&name) {
                return Err(Error::new(EEXIST));
            }
        }

        if self.next_id >= SCHEME_MAX_SCHEMES {
            self.next_id = 1;
        }

        while self.map.contains_key(&SchemeId(self.next_id)) {
            self.next_id += 1;
        }

        /* Allow scheme list to grow if required
        if self.next_id >= SCHEME_MAX_SCHEMES {
            return Err(Error::new(EAGAIN));
        }
        */

        let id = SchemeId(self.next_id);
        self.next_id += 1;

        let scheme = scheme_fn(id);

        assert!(self.map.insert(id, scheme).is_none());
        if let Some(ref mut names) = self.names.get_mut(&ns) {
            assert!(names.insert(name, id).is_none());
        } else {
            // Nonexistent namespace, posssibly null namespace
            return Err(Error::new(ENODEV));
        }
        Ok(id)
    }

    /// Remove a scheme
    pub fn remove(&mut self, id: SchemeId) {
        assert!(self.map.remove(&id).is_some());
        for (_ns, names) in self.names.iter_mut() {
            let mut remove = Vec::with_capacity(1);
            for (name, name_id) in names.iter() {
                if name_id == &id {
                    remove.push(name.clone());
                }
            }
            for name in remove {
                assert!(names.remove(&name).is_some());
            }
        }
    }
}

/// Schemes list
static SCHEMES: Once<RwLock<SchemeList>> = Once::new();

/// Initialize schemes, called if needed
fn init_schemes() -> RwLock<SchemeList> {
    RwLock::new(SchemeList::new())
}

/// Get the global schemes list, const
pub fn schemes() -> RwLockReadGuard<'static, SchemeList> {
    SCHEMES.call_once(init_schemes).read()
}

/// Get the global schemes list, mutable
pub fn schemes_mut() -> RwLockWriteGuard<'static, SchemeList> {
    SCHEMES.call_once(init_schemes).write()
}
<|endoftext|>