<commit_before>#![crate_name = "platform"]
#![crate_type = "rlib"]
#![no_std]
#![feature(const_fn)]

extern crate common;
extern crate drivers;
extern crate hil;
extern crate sam4l;

use sam4l::pm;
use hil::Controller;
use hil::timer::*;
use hil::spi_master::*;

pub struct Firestorm {
    chip: sam4l::chip::Sam4l,
    console: &'static drivers::console::Console<'static, sam4l::usart::USART>,
    gpio: drivers::gpio::GPIO<[&'static hil::gpio::GPIOPin; 14]>,
    tmp006: &'static drivers::tmp006::TMP006<'static, sam4l::i2c::I2CDevice>,
}

#[allow(unused_variables,dead_code)]
pub struct DummyCB {
  val: u8
}
impl hil::spi_master::SpiCallback for DummyCB {
#[allow(unused_variables,dead_code)]
        fn read_write_done(&'static self) {}
}

pub static mut SPICB: DummyCB = DummyCB{val: 0x55 as u8};

impl Firestorm {
    pub unsafe fn service_pending_interrupts(&mut self) {
        self.chip.service_pending_interrupts()
    }

    pub unsafe fn has_pending_interrupts(&mut self) -> bool {
        self.chip.has_pending_interrupts()
    }

    #[inline(never)]
    pub fn with_driver<F, R>(&mut self, driver_num: usize, f: F) -> R where
            F: FnOnce(Option<&hil::Driver>) -> R {

        match driver_num {
            0 => f(Some(self.console)),
            1 => f(Some(&self.gpio)),
            2 => f(Some(self.tmp006)),
            _ => f(None)
        }
    }
}

pub unsafe fn init<'a>() -> &'a mut Firestorm {
    use core::mem;

    static mut FIRESTORM_BUF : [u8; 1024] = [0; 1024];
    static mut CONSOLE_BUF : [u8; 1024] = [0; 1024];
    static mut TIMER_BUF : [u8; 1024] = [0; 1024];
    static mut MUX_ALARM_BUF : [u8; 256] = [0; 256];
    static mut VIRT_ALARM_BUF : [u8; 256] = [0; 256];
    static mut TMP006_BUF : [u8; 1028] = [0; 1028];

    pm::enable_clock(pm::Clock::PBA(pm::PBAClock::SPI));
    /* TODO(alevy): replace above line with this. Currently, over allocating to make development
     * easier, but should be obviated when `size_of` at compile time hits.
    static mut FIRESTORM_BUF : [u8; 192] = [0; 192];
    // Just test that FIRESTORM_BUF is correct size
    // (will throw compiler error if too large or small)
    let _ : Firestorm = mem::transmute(FIRESTORM_BUF);
    let _ : Firestorm = mem::transmute(CONSOLE_BUF);
    */

    let ast = &sam4l::ast::AST;
    ast.select_clock(sam4l::ast::Clock::ClockRCSys);
    ast.set_prescalar(0);
    ast.clear_alarm();

    let console : &mut drivers::console::Console<sam4l::usart::USART> = mem::transmute(&mut CONSOLE_BUF);
    *console = drivers::console::Console::new(&mut sam4l::usart::USART3);

    let mut mux_alarm : &mut MuxAlarm<'static, sam4l::ast::Ast> = mem::transmute(&mut MUX_ALARM_BUF);
    *mux_alarm = MuxAlarm::new(ast);
    ast.configure(mux_alarm);

    let mut virtual_alarm : &mut VirtualMuxAlarm<'static, sam4l::ast::Ast> = mem::transmute(&mut VIRT_ALARM_BUF);
    *virtual_alarm = VirtualMuxAlarm::new(mux_alarm);

    let mut timer : &mut SingleTimer<'static, VirtualMuxAlarm<'static, sam4l::ast::Ast>> = mem::transmute(&mut TIMER_BUF);
    *timer = SingleTimer::new(virtual_alarm);
    virtual_alarm.set_client(timer);

    let tmp006 : &mut drivers::tmp006::TMP006<'static, sam4l::i2c::I2CDevice> = mem::transmute(&mut TMP006_BUF);
    *tmp006 = drivers::tmp006::TMP006::new(&sam4l::i2c::I2C2, timer);

    timer.set_client(tmp006);

    sam4l::usart::USART3.set_client(&*console);

    let firestorm : &'static mut Firestorm = mem::transmute(&mut FIRESTORM_BUF);
    *firestorm = Firestorm {
        chip: sam4l::chip::Sam4l::new(),
        console: &*console,
        gpio: drivers::gpio::GPIO::new(
            [ &mut sam4l::gpio::PC[10], &mut sam4l::gpio::PC[19]
            , &mut sam4l::gpio::PC[13], &mut sam4l::gpio::PA[9]
            , &mut sam4l::gpio::PA[17], &mut sam4l::gpio::PC[20]
            , &mut sam4l::gpio::PA[19], &mut sam4l::gpio::PA[14]
            , &mut sam4l::gpio::PA[16], &mut sam4l::gpio::PA[13]
            , &mut sam4l::gpio::PA[11], &mut sam4l::gpio::PA[10]
            , &mut sam4l::gpio::PA[12], &mut sam4l::gpio::PC[09]]),
        tmp006: &*tmp006
    };

    sam4l::usart::USART3.configure(sam4l::usart::USARTParams {
        //client: &console,
        baud_rate: 115200,
        data_bits: 8,
        parity: hil::uart::Parity::None
    });

    sam4l::gpio::PB[09].configure(Some(sam4l::gpio::PeripheralFunction::A));
    sam4l::gpio::PB[10].configure(Some(sam4l::gpio::PeripheralFunction::A));

    sam4l::gpio::PA[21].configure(Some(sam4l::gpio::PeripheralFunction::E));
    sam4l::gpio::PA[22].configure(Some(sam4l::gpio::PeripheralFunction::E));

    /*
    let pc6 = &sam4l::gpio::PC[6] as &hil::gpio::GPIOPin;
    pc6.enable_output();
    loop {
        pc6.toggle();
    }
    */

    // Configure SPI pins: CLK, MISO, MOSI, CS3
    sam4l::gpio::PC[ 6].configure(Some(sam4l::gpio::PeripheralFunction::A));
    sam4l::gpio::PC[ 4].configure(Some(sam4l::gpio::PeripheralFunction::A));
    sam4l::gpio::PC[ 5].configure(Some(sam4l::gpio::PeripheralFunction::A));
    sam4l::gpio::PC[ 1].configure(Some(sam4l::gpio::PeripheralFunction::A));
    sam4l::spi_dma::SPI.set_active_peripheral(sam4l::spi_dma::Peripheral::Peripheral1);
    sam4l::spi_dma::SPI.init(&SPICB);
    sam4l::spi_dma::SPI.enable();

    let mut flop: bool = false;
    loop {
        if flop {
            //sam4l::spi_dma::SPI.write_byte(0xad);
            sam4l::spi_dma::SPI.read_write_bytes(Some(&mut buf1), Some(&mut buf2));
            //sam4l::spi_dma::SPI.read_write_bytes(Some(&mut buf1), None);
            //sam4l::spi_dma::SPI.read_write_bytes(None, Some(&mut buf2));
        } else {
            //sam4l::spi_dma::SPI.write_byte(0xde);
            sam4l::spi_dma::SPI.read_write_bytes(Some(&mut buf2), Some(&mut buf1));
            //sam4l::spi_dma::SPI.read_write_bytes(Some(&mut buf2), None);
            //sam4l::spi_dma::SPI.read_write_bytes(None, Some(&mut buf1));
        }
        for x in 1..4000 {
            sam4l::spi_dma::SPI.enable();
        }
        flop = !flop;
    }
    // This is a simple byte-level test of SPI.
    /*let mut counter: u8 = 0;
    loop {
      let val1 = SPI.read_write_byte(0x55);
      let val2 = SPI.read_write_byte(counter);
      let val3 = SPI.read_write_byte(val1);
      let val4 = SPI.read_write_byte(val2);
      let val5 = SPI.read_write_byte(0);
      counter = counter + 1;
      for x in 1..2000 {
        SPI.disable();
        SPI.enable();
      }
    }*/

    firestorm.console.initialize();

    firestorm
}
pub static mut buf1: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
pub static mut buf2: [u8; 8] = [7, 6, 5, 4, 3, 2, 1, 0];

<commit_msg>Make blocking version match interfaces and be a drop-in replacement. Remove extra test invocations from lib.<commit_after>#![crate_name = "platform"]
#![crate_type = "rlib"]
#![no_std]
#![feature(const_fn)]

extern crate common;
extern crate drivers;
extern crate hil;
extern crate sam4l;

use sam4l::pm;
use hil::Controller;
use hil::timer::*;
use hil::spi_master::*;

pub struct Firestorm {
    chip: sam4l::chip::Sam4l,
    console: &'static drivers::console::Console<'static, sam4l::usart::USART>,
    gpio: drivers::gpio::GPIO<[&'static hil::gpio::GPIOPin; 14]>,
    tmp006: &'static drivers::tmp006::TMP006<'static, sam4l::i2c::I2CDevice>,
}

#[allow(unused_variables,dead_code)]
pub struct DummyCB {
  val: u8
}
impl hil::spi_master::SpiCallback for DummyCB {
#[allow(unused_variables,dead_code)]
        fn read_write_done(&'static self) {}
}

pub static mut SPICB: DummyCB = DummyCB{val: 0x55 as u8};

impl Firestorm {
    pub unsafe fn service_pending_interrupts(&mut self) {
        self.chip.service_pending_interrupts()
    }

    pub unsafe fn has_pending_interrupts(&mut self) -> bool {
        self.chip.has_pending_interrupts()
    }

    #[inline(never)]
    pub fn with_driver<F, R>(&mut self, driver_num: usize, f: F) -> R where
            F: FnOnce(Option<&hil::Driver>) -> R {

        match driver_num {
            0 => f(Some(self.console)),
            1 => f(Some(&self.gpio)),
            2 => f(Some(self.tmp006)),
            _ => f(None)
        }
    }
}

pub unsafe fn init<'a>() -> &'a mut Firestorm {
    use core::mem;

    static mut FIRESTORM_BUF : [u8; 1024] = [0; 1024];
    static mut CONSOLE_BUF : [u8; 1024] = [0; 1024];
    static mut TIMER_BUF : [u8; 1024] = [0; 1024];
    static mut MUX_ALARM_BUF : [u8; 256] = [0; 256];
    static mut VIRT_ALARM_BUF : [u8; 256] = [0; 256];
    static mut TMP006_BUF : [u8; 1028] = [0; 1028];

    pm::enable_clock(pm::Clock::PBA(pm::PBAClock::SPI));
    /* TODO(alevy): replace above line with this. Currently, over allocating to make development
     * easier, but should be obviated when `size_of` at compile time hits.
    static mut FIRESTORM_BUF : [u8; 192] = [0; 192];
    // Just test that FIRESTORM_BUF is correct size
    // (will throw compiler error if too large or small)
    let _ : Firestorm = mem::transmute(FIRESTORM_BUF);
    let _ : Firestorm = mem::transmute(CONSOLE_BUF);
    */

    let ast = &sam4l::ast::AST;
    ast.select_clock(sam4l::ast::Clock::ClockRCSys);
    ast.set_prescalar(0);
    ast.clear_alarm();

    let console : &mut drivers::console::Console<sam4l::usart::USART> = mem::transmute(&mut CONSOLE_BUF);
    *console = drivers::console::Console::new(&mut sam4l::usart::USART3);

    let mut mux_alarm : &mut MuxAlarm<'static, sam4l::ast::Ast> = mem::transmute(&mut MUX_ALARM_BUF);
    *mux_alarm = MuxAlarm::new(ast);
    ast.configure(mux_alarm);

    let mut virtual_alarm : &mut VirtualMuxAlarm<'static, sam4l::ast::Ast> = mem::transmute(&mut VIRT_ALARM_BUF);
    *virtual_alarm = VirtualMuxAlarm::new(mux_alarm);

    let mut timer : &mut SingleTimer<'static, VirtualMuxAlarm<'static, sam4l::ast::Ast>> = mem::transmute(&mut TIMER_BUF);
    *timer = SingleTimer::new(virtual_alarm);
    virtual_alarm.set_client(timer);

    let tmp006 : &mut drivers::tmp006::TMP006<'static, sam4l::i2c::I2CDevice> = mem::transmute(&mut TMP006_BUF);
    *tmp006 = drivers::tmp006::TMP006::new(&sam4l::i2c::I2C2, timer);

    timer.set_client(tmp006);

    sam4l::usart::USART3.set_client(&*console);

    let firestorm : &'static mut Firestorm = mem::transmute(&mut FIRESTORM_BUF);
    *firestorm = Firestorm {
        chip: sam4l::chip::Sam4l::new(),
        console: &*console,
        gpio: drivers::gpio::GPIO::new(
            [ &mut sam4l::gpio::PC[10], &mut sam4l::gpio::PC[19]
            , &mut sam4l::gpio::PC[13], &mut sam4l::gpio::PA[9]
            , &mut sam4l::gpio::PA[17], &mut sam4l::gpio::PC[20]
            , &mut sam4l::gpio::PA[19], &mut sam4l::gpio::PA[14]
            , &mut sam4l::gpio::PA[16], &mut sam4l::gpio::PA[13]
            , &mut sam4l::gpio::PA[11], &mut sam4l::gpio::PA[10]
            , &mut sam4l::gpio::PA[12], &mut sam4l::gpio::PC[09]]),
        tmp006: &*tmp006
    };

    sam4l::usart::USART3.configure(sam4l::usart::USARTParams {
        //client: &console,
        baud_rate: 115200,
        data_bits: 8,
        parity: hil::uart::Parity::None
    });

    sam4l::gpio::PB[09].configure(Some(sam4l::gpio::PeripheralFunction::A));
    sam4l::gpio::PB[10].configure(Some(sam4l::gpio::PeripheralFunction::A));

    sam4l::gpio::PA[21].configure(Some(sam4l::gpio::PeripheralFunction::E));
    sam4l::gpio::PA[22].configure(Some(sam4l::gpio::PeripheralFunction::E));

    /*
    let pc6 = &sam4l::gpio::PC[6] as &hil::gpio::GPIOPin;
    pc6.enable_output();
    loop {
        pc6.toggle();
    }
    */

    // Configure SPI pins: CLK, MISO, MOSI, CS3
    sam4l::gpio::PC[ 6].configure(Some(sam4l::gpio::PeripheralFunction::A));
    sam4l::gpio::PC[ 4].configure(Some(sam4l::gpio::PeripheralFunction::A));
    sam4l::gpio::PC[ 5].configure(Some(sam4l::gpio::PeripheralFunction::A));
    sam4l::gpio::PC[ 1].configure(Some(sam4l::gpio::PeripheralFunction::A));
    sam4l::spi_dma::SPI.set_active_peripheral(sam4l::spi_dma::Peripheral::Peripheral1);
    sam4l::spi_dma::SPI.init(&SPICB);
    sam4l::spi_dma::SPI.enable();

    let mut flop: bool = false;
    loop {
        if flop {
            sam4l::spi_dma::SPI.read_write_bytes(Some(&mut buf1), Some(&mut buf2));
        } else {
            sam4l::spi_dma::SPI.read_write_bytes(Some(&mut buf2), Some(&mut buf1));
        }
        for x in 1..4000 {
            sam4l::spi_dma::SPI.enable();
        }
        flop = !flop;
    }
    // This is a simple byte-level test of SPI.
    /*let mut counter: u8 = 0;
    loop {
      let val1 = SPI.read_write_byte(0x55);
      let val2 = SPI.read_write_byte(counter);
      let val3 = SPI.read_write_byte(val1);
      let val4 = SPI.read_write_byte(val2);
      let val5 = SPI.read_write_byte(0);
      counter = counter + 1;
      for x in 1..2000 {
        SPI.disable();
        SPI.enable();
      }
    }*/

    firestorm.console.initialize();

    firestorm
}
pub static mut buf1: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
pub static mut buf2: [u8; 8] = [7, 6, 5, 4, 3, 2, 1, 0];

<|endoftext|>


<commit_before>use regex::Regex;
use reqwest;
use serde_json;

use web::data::SolrTermSummary;
use web::config::Config;

pub struct Search {
    solr_url: String,
    close_synonym_boost: f32,
    distant_synonym_boost: f32,
}

#[derive(Deserialize, Debug)]
struct SolrResponse {
    pub docs: Vec<SolrTermSummary>,
}

#[derive(Deserialize, Debug)]
struct SolrResponseContainer {
    pub response: SolrResponse,
}

impl Search {
    pub fn new(config: &Config) -> Search {
        Search {
            solr_url: config.server.solr_url.clone(),
            close_synonym_boost: config.server.close_synonym_boost,
            distant_synonym_boost: config.server.distant_synonym_boost,
        }
    }

    fn get_query_part(&self, words: &Vec<String>) -> String {
        let mut ret = String::new();

        let words_length = words.len();

        for (i, word) in words.iter().enumerate() {
            if i == words_length - 1 {
                ret += &format!("{} {}~0.8 {}*", word, word, word);
            } else {
                ret += &format!("{} {}~0.8 ", word, word);
            }
        }

        ret
    }

    pub fn term_complete(&self, cv_name: &str, q: &str)
                         -> Result<Vec<SolrTermSummary>, reqwest::Error>
    {
        let mut terms_url =
            self.solr_url.to_owned() + "/terms/select?wt=json&q=";

        let termid_re_string = r"(?P<prefix>[\w_]+):(?P<accession>\d+)";
        let termid_re = Regex::new(termid_re_string).unwrap();

        let parent_re_string = r"^\[".to_owned() + termid_re_string + r"\]$";
        let parent_re = Regex::new(&parent_re_string).unwrap();

        let maybe_captures = parent_re.captures(cv_name);

        if let Some(captures) = maybe_captures {
            let prefix = captures.name("prefix").unwrap();
            let accession = captures.name("accession").unwrap();
            terms_url += &format!("(interesting_parents:{}\\:{} OR id:{}\\:{})",
                                  prefix, accession, prefix, accession);
        } else {
            terms_url += "cv_name:";
            terms_url += cv_name;
        }

        if let Some(captures) = termid_re.captures(q) {
            let prefix = captures.name("prefix").unwrap();
            let accession = captures.name("accession").unwrap();
            terms_url += " AND id:";
            terms_url += prefix;
            terms_url += r"\:";
            terms_url += accession;
        } else {
            terms_url += " AND (name:(";

            let clean_words: Vec<String> =
                Regex::new(r"([\w-]+)").unwrap().captures_iter(q)
                .map(|cap| cap.at(1).unwrap().to_owned()).collect();

            if clean_words.len() == 0 {
                return Ok(vec![]);
            }

            let clean_words_length = clean_words.len();

            for (i, word) in clean_words.iter().enumerate() {
                if i == clean_words_length - 1 {
                    terms_url += &format!("{} {}~0.8 {}*", word, word, word);
                } else {
                    terms_url += &format!("{} {}~0.8 ", word, word);
                }
            }

            let query_part = self.get_query_part(&clean_words);

            terms_url += &format!(") OR close_synonym_words:({})^{} OR distant_synonym_words:({})^{})",
                                  query_part, self.close_synonym_boost,
                                  query_part, self.distant_synonym_boost);
        }
        print!("{:?}\n", terms_url);

        let res = reqwest::get(&terms_url)?;

        println!("Status: {}", res.status());
        println!("Headers:\n{}", res.headers());

        match serde_json::from_reader(res) {
            Ok(solr_response_container) => {
                let container: SolrResponseContainer = solr_response_container;
                Ok(container.response.docs)
            },
            Err(err) => {
                panic!(format!("{:?}", err));
            }
        }
    }
}
<commit_msg>Improve error handling when querying Solr<commit_after>use regex::Regex;
use reqwest;
use serde_json;

use web::data::SolrTermSummary;
use web::config::Config;

pub struct Search {
    solr_url: String,
    close_synonym_boost: f32,
    distant_synonym_boost: f32,
}

#[derive(Deserialize, Debug)]
struct SolrResponse {
    pub docs: Vec<SolrTermSummary>,
}

#[derive(Deserialize, Debug)]
struct SolrResponseContainer {
    pub response: SolrResponse,
}

impl Search {
    pub fn new(config: &Config) -> Search {
        Search {
            solr_url: config.server.solr_url.clone(),
            close_synonym_boost: config.server.close_synonym_boost,
            distant_synonym_boost: config.server.distant_synonym_boost,
        }
    }

    fn get_query_part(&self, words: &Vec<String>) -> String {
        let mut ret = String::new();

        let words_length = words.len();

        for (i, word) in words.iter().enumerate() {
            if i == words_length - 1 {
                ret += &format!("{} {}~0.8 {}*", word, word, word);
            } else {
                ret += &format!("{} {}~0.8 ", word, word);
            }
        }

        ret
    }

    pub fn term_complete(&self, cv_name: &str, q: &str)
                         -> Result<Vec<SolrTermSummary>, String>
    {
        let mut terms_url =
            self.solr_url.to_owned() + "/terms/select?wt=json&q=";

        let termid_re_string = r"^(?P<prefix>[\w_]+):(?P<accession>\d+)$";
        let termid_re = Regex::new(termid_re_string).unwrap();

        let parent_re_string = r"^\[".to_owned() + termid_re_string + r"\]$";
        let parent_re = Regex::new(&parent_re_string).unwrap();

        let maybe_captures = parent_re.captures(cv_name);

        if let Some(captures) = maybe_captures {
            let prefix = captures.name("prefix").unwrap();
            let accession = captures.name("accession").unwrap();
            terms_url += &format!("(interesting_parents:{}\\:{} OR id:{}\\:{})",
                                  prefix, accession, prefix, accession);
        } else {
            terms_url += "cv_name:";
            terms_url += cv_name;
        }

        if let Some(captures) = termid_re.captures(q) {
            let prefix = captures.name("prefix").unwrap();
            let accession = captures.name("accession").unwrap();
            terms_url += " AND id:";
            terms_url += prefix;
            terms_url += r"\:";
            terms_url += accession;
        } else {
            let substring = |s: &str, len: usize| s.chars().take(len).collect::<String>();
            let lower_q = substring(&q.to_lowercase(), 200);

            terms_url += " AND (name:(";

            let clean_words: Vec<String> =
                Regex::new(r"([\w-]+)").unwrap().captures_iter(&lower_q)
                .map(|cap| cap.at(1).unwrap().to_owned()).collect();

            if clean_words.len() == 0 {
                return Ok(vec![]);
            }

            let clean_words_length = clean_words.len();

            for (i, word) in clean_words.iter().enumerate() {
                if i == clean_words_length - 1 {
                    terms_url += &format!("{} {}~0.8 {}*", word, word, word);
                } else {
                    terms_url += &format!("{} {}~0.8 ", word, word);
                }
            }

            let query_part = self.get_query_part(&clean_words);

            terms_url += &format!(") OR close_synonym_words:({})^{} OR distant_synonym_words:({})^{})",
                                  query_part, self.close_synonym_boost,
                                  query_part, self.distant_synonym_boost);
        }
        print!("Solr URL: {:?}\n", terms_url);

        return match reqwest::get(&terms_url) {
            Ok(res) => {
                println!("Status: {}", res.status());

                if res.status().is_success() {
                    match serde_json::from_reader(res) {
                        Ok(solr_response_container) => {
                            let container: SolrResponseContainer = solr_response_container;
                            Ok(container.response.docs)
                        },
                        Err(err) => {
                            Err(format!("Error parsing response from Solr: {:?}", err))
                        }
                    }
                } else {
                    if let Some(reason) = res.status().canonical_reason() {
                        Err(format!("HTTP request to Solr failed: {} - {}", res.status(), reason))
                    } else {
                        Err(format!("HTTP request to Solr failed with status code: {}",
                                    res.status()))
                    }
                }
            },
            Err(err) => {
                Err(format!("Error from Reqwest: {:?}", err))
            }
        }
    }
}
<|endoftext|>


<commit_before>use types::*;
use types::flags::*;
use types::generation::*;
use types::parsing::*;

use cookie_factory::GenError;

pub mod metadata {
    use super::*;

    pub const NAME:          &'static str   = "{{protocol.name}}";
    pub const MAJOR_VERSION: ShortShortUInt = {{protocol.major_version}};
    pub const MINOR_VERSION: ShortShortUInt = {{protocol.minor_version}};
    pub const REVISION:      ShortShortUInt = {{protocol.revision}};
    pub const PORT:          LongUInt       = {{protocol.port}};
    pub const COPYRIGHT:     &'static str   = r#"{{copyright}}"#;
}

pub mod constants {
    use super::*;

    {{#each protocol.constants as |constant| ~}}
    pub const {{sanitize_name constant.name}}: {{constant.type}} = {{constant.value}};
    {{/each ~}}
}

pub enum AMQPError {
    Soft(AMQPSoftError),
    Hard(AMQPHardError),
}

impl AMQPError {
    pub fn get_id(&self) -> ShortUInt {
        match *self {
            AMQPError::Soft(ref s) => s.get_id(),
            AMQPError::Hard(ref h) => h.get_id(),
        }
    }

    pub fn from_id(id: ShortUInt) -> Option<AMQPError> {
        AMQPSoftError::from_id(id).map(|e| AMQPError::Soft(e)).or_else(|| AMQPHardError::from_id(id).map(|e| AMQPError::Hard(e)))
    }
}

pub enum AMQPSoftError {
    {{#each protocol.soft_errors as |constant| ~}}
    {{camel constant.name}},
    {{/each ~}}
}

impl AMQPSoftError {
    pub fn get_id(&self) -> ShortUInt {
        match *self {
            {{#each protocol.soft_errors as |constant| ~}}
            AMQPSoftError::{{camel constant.name}} => {{constant.value}},
            {{/each ~}}
        }
    }

    pub fn from_id(id: ShortUInt) -> Option<AMQPSoftError> {
        match id {
            {{#each protocol.soft_errors as |constant| ~}}
            {{constant.value}} => Some(AMQPSoftError::{{camel constant.name}}),
            {{/each ~}}
            _                  => None,
        }
    }
}

pub enum AMQPHardError {
    {{#each protocol.hard_errors as |constant| ~}}
    {{camel constant.name}},
    {{/each ~}}
}

impl AMQPHardError {
    pub fn get_id(&self) -> ShortUInt {
        match *self {
            {{#each protocol.hard_errors as |constant| ~}}
            AMQPHardError::{{camel constant.name}} => {{constant.value}},
            {{/each ~}}
        }
    }

    pub fn from_id(id: ShortUInt) -> Option<AMQPHardError> {
        match id {
            {{#each protocol.hard_errors as |constant| ~}}
            {{constant.value}} => Some(AMQPHardError::{{camel constant.name}}),
            {{/each ~}}
            _                  => None,
        }
    }
}

{{#each protocol.classes as |class| ~}}
use self::{{snake class.name}}::parse_{{snake class.name}};
{{/each ~}}

named!(pub parse_class<AMQPClass>, switch!(parse_id,
    {{#each protocol.classes as |class| ~}}
    {{class.id}} => map!(call!(parse_{{snake class.name}}), AMQPClass::{{camel class.name}}) {{#unless @last ~}}|{{/unless ~}}
    {{/each ~}}
));

pub fn gen_class<'a>(input: (&'a mut [u8], usize), class: &AMQPClass) -> Result<(&'a mut [u8], usize), GenError> {
    match *class {
        {{#each protocol.classes as |class| ~}}
        AMQPClass::{{camel class.name}}(ref {{snake class.name}}) => {{snake class.name}}::gen_{{snake class.name}}(input, {{snake class.name}}),
        {{/each ~}}
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum AMQPClass {
    {{#each protocol.classes as |class| ~}}
    {{camel class.name}}({{snake class.name}}::AMQPMethod),
    {{/each ~}}
}

{{#each protocol.classes as |class|}}
pub mod {{snake class.name}} {
    use super::*;

    named!(pub parse_{{snake class.name}}<{{snake class.name}}::AMQPMethod>, switch!(parse_id,
        {{#each class.methods as |method| ~}}
        {{method.id}} => map!(call!(parse_{{snake method.name}}), AMQPMethod::{{camel method.name}}) {{#unless @last ~}}|{{/unless ~}}
        {{/each ~}}
    ));

    pub fn gen_{{snake class.name}}<'a>(input: (&'a mut [u8], usize), method: &AMQPMethod) -> Result<(&'a mut [u8], usize), GenError> {
        match *method {
            {{#each class.methods as |method| ~}}
            AMQPMethod::{{camel method.name}}(ref {{snake method.name}}) => {
                do_gen!(input,
                    gen_id(&{{class.id}}) >>
                    gen_{{snake method.name}}({{snake method.name}})
                )
            },
            {{/each ~}}
        }
    }

    #[derive(Clone, Debug, PartialEq)]
    pub enum AMQPMethod {
        {{#each class.methods as |method| ~}}
        {{camel method.name}}({{camel method.name}}),
        {{/each ~}}
    }

    {{#each class.methods as |method|}}
    #[derive(Clone, Debug, PartialEq)]
    pub struct {{camel method.name}} {
        {{#each_argument method.arguments as |argument| ~}}
        {{#if argument_is_value ~}}
        pub {{snake argument.name}}: {{argument.type}},
        {{else}}
        {{#each_flag argument as |flag| ~}}
        pub {{snake flag.name}}: Boolean,
        {{/each_flag ~}}
        {{/if ~}}
        {{/each_argument ~}}
    }

    named!(pub parse_{{snake method.name}}<{{camel method.name}}>, do_parse!(
        {{#each_argument method.arguments as |argument| ~}}
        {{#if argument_is_value ~}}
        {{snake argument.name}}: parse_{{snake_type argument.type}} >>
        {{else}}
        /* FIXME: support multiple flags structs? */
        flags: apply!(parse_flags, &vec![
            {{#each_flag argument as |flag| ~}}
            "{{flag.name}}",
            {{/each_flag ~}}
        ]) >>
        {{/if ~}}
        {{/each_argument ~}}
        ({{camel method.name}} {
            {{#each_argument method.arguments as |argument| ~}}
            {{#if argument_is_value ~}}
            {{snake argument.name}}: {{snake argument.name}},
            {{else}}
            {{#each_flag argument as |flag| ~}}
            {{snake flag.name}}: flags.get_flag("{{snake flag.name}}").unwrap_or({{flag.default_value}}),
            {{/each_flag ~}}
            {{/if ~}}
            {{/each_argument ~}}
        })
    ));

    pub fn gen_{{snake method.name}}<'a>(input: (&'a mut [u8], usize), {{#if method.has_arguments ~}}method{{else}}_{{/if ~}}: &{{camel method.name}}) -> Result<(&'a mut [u8],usize), GenError> {
        {{#if method.has_flags ~}}
        /* FIXME: support multiple flags structs? */
        let mut flags = AMQPFlags::new();
        {{#each_argument method.arguments as |argument| ~}}
        {{#unless argument_is_value ~}}
        {{#each_flag argument as |flag| ~}}
        flags.add_flag("{{snake flag.name}}".to_string(), method.{{snake flag.name}});
        {{/each_flag ~}}
        {{/unless ~}}
        {{/each_argument ~}}
        {{/if ~}}
        do_gen!(input,
            gen_id(&{{method.id}})
            {{#each_argument method.arguments as |argument| ~}}
            {{#if argument_is_value ~}}
            >> gen_{{snake_type argument.type}}(&method.{{snake argument.name}})
            {{else}}
            >> gen_flags(&flags)
            {{/if ~}}
            {{/each_argument ~}}
        )
    }
    {{/each ~}}
    {{#if class.has_properties ~}}
    pub enum AMQPProperty {
        {{#each class.properties as |property| ~}}
        {{camel property.name}}({{property.type}}),
        {{/each ~}}
    }

    impl AMQPProperty {
        pub fn get_bitmask(&self) -> ShortUInt {
            match *self {
                {{#each class.properties as |property| ~}}
                AMQPProperty::{{camel property.name}}(_) => (1 << (15 - {{@index}})),
                {{/each ~}}
            }
        }
    }
    {{/if ~}}
}
{{/each ~}}
<commit_msg>protocol: implement AMQPProperties<commit_after>use types::*;
use types::flags::*;
use types::generation::*;
use types::parsing::*;

use cookie_factory::GenError;

pub mod metadata {
    use super::*;

    pub const NAME:          &'static str   = "{{protocol.name}}";
    pub const MAJOR_VERSION: ShortShortUInt = {{protocol.major_version}};
    pub const MINOR_VERSION: ShortShortUInt = {{protocol.minor_version}};
    pub const REVISION:      ShortShortUInt = {{protocol.revision}};
    pub const PORT:          LongUInt       = {{protocol.port}};
    pub const COPYRIGHT:     &'static str   = r#"{{copyright}}"#;
}

pub mod constants {
    use super::*;

    {{#each protocol.constants as |constant| ~}}
    pub const {{sanitize_name constant.name}}: {{constant.type}} = {{constant.value}};
    {{/each ~}}
}

pub enum AMQPError {
    Soft(AMQPSoftError),
    Hard(AMQPHardError),
}

impl AMQPError {
    pub fn get_id(&self) -> ShortUInt {
        match *self {
            AMQPError::Soft(ref s) => s.get_id(),
            AMQPError::Hard(ref h) => h.get_id(),
        }
    }

    pub fn from_id(id: ShortUInt) -> Option<AMQPError> {
        AMQPSoftError::from_id(id).map(|e| AMQPError::Soft(e)).or_else(|| AMQPHardError::from_id(id).map(|e| AMQPError::Hard(e)))
    }
}

pub enum AMQPSoftError {
    {{#each protocol.soft_errors as |constant| ~}}
    {{camel constant.name}},
    {{/each ~}}
}

impl AMQPSoftError {
    pub fn get_id(&self) -> ShortUInt {
        match *self {
            {{#each protocol.soft_errors as |constant| ~}}
            AMQPSoftError::{{camel constant.name}} => {{constant.value}},
            {{/each ~}}
        }
    }

    pub fn from_id(id: ShortUInt) -> Option<AMQPSoftError> {
        match id {
            {{#each protocol.soft_errors as |constant| ~}}
            {{constant.value}} => Some(AMQPSoftError::{{camel constant.name}}),
            {{/each ~}}
            _                  => None,
        }
    }
}

pub enum AMQPHardError {
    {{#each protocol.hard_errors as |constant| ~}}
    {{camel constant.name}},
    {{/each ~}}
}

impl AMQPHardError {
    pub fn get_id(&self) -> ShortUInt {
        match *self {
            {{#each protocol.hard_errors as |constant| ~}}
            AMQPHardError::{{camel constant.name}} => {{constant.value}},
            {{/each ~}}
        }
    }

    pub fn from_id(id: ShortUInt) -> Option<AMQPHardError> {
        match id {
            {{#each protocol.hard_errors as |constant| ~}}
            {{constant.value}} => Some(AMQPHardError::{{camel constant.name}}),
            {{/each ~}}
            _                  => None,
        }
    }
}

{{#each protocol.classes as |class| ~}}
use self::{{snake class.name}}::parse_{{snake class.name}};
{{/each ~}}

named!(pub parse_class<AMQPClass>, switch!(parse_id,
    {{#each protocol.classes as |class| ~}}
    {{class.id}} => map!(call!(parse_{{snake class.name}}), AMQPClass::{{camel class.name}}) {{#unless @last ~}}|{{/unless ~}}
    {{/each ~}}
));

pub fn gen_class<'a>(input: (&'a mut [u8], usize), class: &AMQPClass) -> Result<(&'a mut [u8], usize), GenError> {
    match *class {
        {{#each protocol.classes as |class| ~}}
        AMQPClass::{{camel class.name}}(ref {{snake class.name}}) => {{snake class.name}}::gen_{{snake class.name}}(input, {{snake class.name}}),
        {{/each ~}}
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum AMQPClass {
    {{#each protocol.classes as |class| ~}}
    {{camel class.name}}({{snake class.name}}::AMQPMethod),
    {{/each ~}}
}

{{#each protocol.classes as |class|}}
pub mod {{snake class.name}} {
    use super::*;

    named!(pub parse_{{snake class.name}}<{{snake class.name}}::AMQPMethod>, switch!(parse_id,
        {{#each class.methods as |method| ~}}
        {{method.id}} => map!(call!(parse_{{snake method.name}}), AMQPMethod::{{camel method.name}}) {{#unless @last ~}}|{{/unless ~}}
        {{/each ~}}
    ));

    pub fn gen_{{snake class.name}}<'a>(input: (&'a mut [u8], usize), method: &AMQPMethod) -> Result<(&'a mut [u8], usize), GenError> {
        match *method {
            {{#each class.methods as |method| ~}}
            AMQPMethod::{{camel method.name}}(ref {{snake method.name}}) => {
                do_gen!(input,
                    gen_id(&{{class.id}}) >>
                    gen_{{snake method.name}}({{snake method.name}})
                )
            },
            {{/each ~}}
        }
    }

    #[derive(Clone, Debug, PartialEq)]
    pub enum AMQPMethod {
        {{#each class.methods as |method| ~}}
        {{camel method.name}}({{camel method.name}}),
        {{/each ~}}
    }

    {{#each class.methods as |method|}}
    #[derive(Clone, Debug, PartialEq)]
    pub struct {{camel method.name}} {
        {{#each_argument method.arguments as |argument| ~}}
        {{#if argument_is_value ~}}
        pub {{snake argument.name}}: {{argument.type}},
        {{else}}
        {{#each_flag argument as |flag| ~}}
        pub {{snake flag.name}}: Boolean,
        {{/each_flag ~}}
        {{/if ~}}
        {{/each_argument ~}}
    }

    named!(pub parse_{{snake method.name}}<{{camel method.name}}>, do_parse!(
        {{#each_argument method.arguments as |argument| ~}}
        {{#if argument_is_value ~}}
        {{snake argument.name}}: parse_{{snake_type argument.type}} >>
        {{else}}
        /* FIXME: support multiple flags structs? */
        flags: apply!(parse_flags, &vec![
            {{#each_flag argument as |flag| ~}}
            "{{flag.name}}",
            {{/each_flag ~}}
        ]) >>
        {{/if ~}}
        {{/each_argument ~}}
        ({{camel method.name}} {
            {{#each_argument method.arguments as |argument| ~}}
            {{#if argument_is_value ~}}
            {{snake argument.name}}: {{snake argument.name}},
            {{else}}
            {{#each_flag argument as |flag| ~}}
            {{snake flag.name}}: flags.get_flag("{{snake flag.name}}").unwrap_or({{flag.default_value}}),
            {{/each_flag ~}}
            {{/if ~}}
            {{/each_argument ~}}
        })
    ));

    pub fn gen_{{snake method.name}}<'a>(input: (&'a mut [u8], usize), {{#if method.has_arguments ~}}method{{else}}_{{/if ~}}: &{{camel method.name}}) -> Result<(&'a mut [u8],usize), GenError> {
        {{#if method.has_flags ~}}
        /* FIXME: support multiple flags structs? */
        let mut flags = AMQPFlags::new();
        {{#each_argument method.arguments as |argument| ~}}
        {{#unless argument_is_value ~}}
        {{#each_flag argument as |flag| ~}}
        flags.add_flag("{{snake flag.name}}".to_string(), method.{{snake flag.name}});
        {{/each_flag ~}}
        {{/unless ~}}
        {{/each_argument ~}}
        {{/if ~}}
        do_gen!(input,
            gen_id(&{{method.id}})
            {{#each_argument method.arguments as |argument| ~}}
            {{#if argument_is_value ~}}
            >> gen_{{snake_type argument.type}}(&method.{{snake argument.name}})
            {{else}}
            >> gen_flags(&flags)
            {{/if ~}}
            {{/each_argument ~}}
        )
    }
    {{/each ~}}
    {{#if class.has_properties ~}}
    #[derive(Clone,Debug,PartialEq)]
    pub struct AMQPProperties {
        {{#each class.properties as |property| ~}}
        {{snake property.name}}: Option<{{property.type}}>,
        {{/each ~}}
    }

    impl Default for AMQPProperties {
        fn default() -> AMQPProperties {
            AMQPProperties {
                {{#each class.properties as |property| ~}}
                {{snake property.name}}: None,
                {{/each ~}}
            }
        }
    }

    impl AMQPProperties {
        {{#each class.properties as |property| ~}}
        pub fn with_{{snake property.name}}(mut self, value: {{property.type}}) -> AMQPProperties {
            self.{{snake property.name}} = Some(value);
            self
        }
        {{/each ~}}

        pub fn bitmask(&self) -> ShortUInt {
            {{#each class.properties as |property| ~}}
            (if self.{{snake property.name}}.is_some() { 1 << (15 - {{@index}}) } else { 0 }) {{#unless @last ~}} + {{/unless ~}}
            {{/each ~}}
        }
    }

    named!(pub parse_properties<AMQPProperties>, do_parse!(
        flags: parse_short_uint >>
        {{#each class.properties as |property| ~}}
        {{snake property.name}}: cond!(flags & (1 << (15 - {{@index}})) != 0, parse_{{snake_type property.type}}) >>
        {{/each ~}}
        (AMQPProperties {
            {{#each class.properties as |property| ~}}
            {{snake property.name}}: {{snake property.name}},
            {{/each ~}}
        })
    ));

    pub fn gen_properties<'a>(input:(&'a mut [u8],usize), props: &AMQPProperties) -> Result<(&'a mut [u8],usize),GenError> {
        do_gen!(input,
            gen_short_uint(&props.bitmask())
            {{#each class.properties as |property| ~}}
            >> gen_cond!(props.{{snake property.name}}.is_some(), gen_call!(gen_{{snake_type property.type}}, &props.{{snake property.name}}.as_ref().unwrap()))
            {{/each ~}}
        )
    }
    {{/if ~}}
}
{{/each ~}}
<|endoftext|>


<commit_before>use std::io;
use std::str;

use crate::wire_format::WireType;

/// `Result` alias for `ProtobufError`
pub type ProtobufResult<T> = Result<T, ProtobufError>;

/// Enum values added here for diagnostic purposes.
/// Users should not depend on specific values.
#[derive(Debug, thiserror::Error)]
pub enum WireError {
    #[error("Unexpected EOF")]
    UnexpectedEof,
    #[error("Unexpected wire type")]
    UnexpectedWireType(WireType),
    #[error("Incorrect tag")]
    IncorrectTag(u32),
    // unused since https://github.com/stepancheg/rust-protobuf/issues/318
    #[error("Incomplete map")]
    IncompleteMap,
    #[error("Incorrect varint")]
    IncorrectVarint,
    #[error("Invalid UTF-8 sequence")]
    Utf8Error,
    #[error("Invalid enum value: {}", .0)]
    InvalidEnumValue(i32),
    #[error("Over recursion limit")]
    OverRecursionLimit,
    #[error("Truncated message")]
    TruncatedMessage,
    // not really possible
    #[error("Limit overflow")]
    LimitOverflow,
    #[error("New limit must not be greater than current limit")]
    LimitIncrease,
}

/// Generic protobuf error
#[derive(Debug, thiserror::Error)]
pub enum ProtobufError {
    /// I/O error when reading or writing
    #[error(transparent)]
    IoError(#[from] io::Error),
    /// Malformed input
    #[error(transparent)]
    WireError(#[from] WireError),
    /// Protocol contains a string which is not valid UTF-8 string
    #[error("UTF-8 decode error")]
    Utf8(#[from] str::Utf8Error),
    /// Not all required fields of message set.
    #[error("Message `{}` is missing required fields", .0)]
    MessageNotInitialized(String),
    /// Protobuf type and runtime types mismatch.
    #[error("Protobuf type and runtime types are not compatible")]
    IncompatibleProtobufTypeAndRuntimeType,
    /// Group field type not implemented.
    #[error("Group field is not supported")]
    GroupIsNotImplemented,
}

impl From<ProtobufError> for io::Error {
    fn from(err: ProtobufError) -> Self {
        match err {
            ProtobufError::IoError(e) => e,
            ProtobufError::WireError(e) => {
                io::Error::new(io::ErrorKind::InvalidData, ProtobufError::WireError(e))
            }
            ProtobufError::MessageNotInitialized(message) => io::Error::new(
                io::ErrorKind::InvalidInput,
                ProtobufError::MessageNotInitialized(message),
            ),
            e => io::Error::new(io::ErrorKind::Other, Box::new(e)),
        }
    }
}
<commit_msg>Use source, not from for ProtobufError::Utf8<commit_after>use std::io;
use std::str;

use crate::wire_format::WireType;

/// `Result` alias for `ProtobufError`
pub type ProtobufResult<T> = Result<T, ProtobufError>;

/// Enum values added here for diagnostic purposes.
/// Users should not depend on specific values.
#[derive(Debug, thiserror::Error)]
pub enum WireError {
    #[error("Unexpected EOF")]
    UnexpectedEof,
    #[error("Unexpected wire type")]
    UnexpectedWireType(WireType),
    #[error("Incorrect tag")]
    IncorrectTag(u32),
    // unused since https://github.com/stepancheg/rust-protobuf/issues/318
    #[error("Incomplete map")]
    IncompleteMap,
    #[error("Incorrect varint")]
    IncorrectVarint,
    #[error("Invalid UTF-8 sequence")]
    Utf8Error,
    #[error("Invalid enum value: {}", .0)]
    InvalidEnumValue(i32),
    #[error("Over recursion limit")]
    OverRecursionLimit,
    #[error("Truncated message")]
    TruncatedMessage,
    // not really possible
    #[error("Limit overflow")]
    LimitOverflow,
    #[error("New limit must not be greater than current limit")]
    LimitIncrease,
}

/// Generic protobuf error
#[derive(Debug, thiserror::Error)]
pub enum ProtobufError {
    /// I/O error when reading or writing
    #[error(transparent)]
    IoError(#[from] io::Error),
    /// Malformed input
    #[error(transparent)]
    WireError(#[from] WireError),
    /// Protocol contains a string which is not valid UTF-8 string
    #[error("UTF-8 decode error")]
    Utf8(#[source] str::Utf8Error),
    /// Not all required fields of message set.
    #[error("Message `{}` is missing required fields", .0)]
    MessageNotInitialized(String),
    /// Protobuf type and runtime types mismatch.
    #[error("Protobuf type and runtime types are not compatible")]
    IncompatibleProtobufTypeAndRuntimeType,
    /// Group field type not implemented.
    #[error("Group field is not supported")]
    GroupIsNotImplemented,
}

impl From<ProtobufError> for io::Error {
    fn from(err: ProtobufError) -> Self {
        match err {
            ProtobufError::IoError(e) => e,
            ProtobufError::WireError(e) => {
                io::Error::new(io::ErrorKind::InvalidData, ProtobufError::WireError(e))
            }
            ProtobufError::MessageNotInitialized(message) => io::Error::new(
                io::ErrorKind::InvalidInput,
                ProtobufError::MessageNotInitialized(message),
            ),
            e => io::Error::new(io::ErrorKind::Other, Box::new(e)),
        }
    }
}
<|endoftext|>


<commit_before>/*
 * Description: Update an internal view of a buffer's state.
 *
 * Copyright (C) 2022 Danny McClanahan <dmcC2@hypnicjerk.ai>
 * SPDX-License-Identifier: AGPL-3.0-or-later
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

//! Update an internal view of a buffer's state.
//!
//! # TODO
//! 1. maintain a list of all lines of text in the buffer, along with their checksums & covered
//!    regions
//! 1. every time a line is modified, update its checksum + region
//! 1. if a newline is deleted or inserted, calculate new adjacent lines, regions, & checksums
//! 1. when a sync process fails, obtain the minimum sequence of hashed line insertions/deletions to
//!    apply
//! 1. TODO: figure out how to broadcast synced lines. rebroadcast them all periodically? LRU?
//!    MRU?
//!
//! # sync negotiation
//! - A provides a list of "trial lines" with indices and checksums to B.
//! - B provides a response of which checksums weren't found.
//!   - If B found any such lines, then B pauses receiving any new operations for that buffer
//!     momentarily.
//! - A provides the N previous/successive lines from each mismatched line from B.
//! - B reports the *line number intervals* which didn't match, so A can provide more
//!   previous/successive lines necessary for B to obtain the whole extent of non-matching
//!   lines. Note that there needs to be a sentinel value for the final line from A being the end of
//!   the buffer.
//! - [A & B repeat the above process until the non-matching intervals have been closed in on.]
//! - A then sends all of the line contents according to all the checksums B doesn't have.
//! - B applies all the changes to the line contents as a single edit.
//! - B then unpauses receiving operations for the buffer.

use crate::{buffers::BufferId, transforms};

use unicode_buffer::{Buffer, BufferError, CodePointInsertionIndex, InsertionIndex};

use async_lock::RwLock;
use displaydoc::Display;
use indexmap::IndexMap;
use thiserror::Error;

use std::{num, sync::Arc};

#[derive(Debug, Display, Error)]
pub enum BufferMappingError {
  /// error handling a buffer: {0}
  Buf(#[from] BufferError),
  /// failed to find a buffer with id {0}
  BufNotFound(BufferId),
  /// numerical conversion error: {0}
  Num(#[from] num::TryFromIntError),
}

#[derive(Debug, Clone)]
pub struct BufferMapping {
  pub live_buffers: Arc<RwLock<IndexMap<BufferId, Arc<RwLock<Buffer>>>>>,
}

impl BufferMapping {
  pub fn new() -> Self {
    Self {
      live_buffers: Arc::new(RwLock::new(IndexMap::new())),
    }
  }

  pub async fn initialize_buffer(
    &self,
    id: BufferId,
    contents: &str,
  ) -> Result<(), BufferMappingError> {
    let buffer = self
      .live_buffers
      .write()
      .await
      .entry(id)
      .or_insert_with(|| Arc::new(RwLock::new(Buffer::new())))
      .clone();
    buffer.write().await.replace(contents)?;
    Ok(())
  }

  pub async fn apply_transform(
    &self,
    id: BufferId,
    transform: transforms::Transform,
  ) -> Result<(), BufferMappingError> {
    let transforms::Transform { r#type } = transform;
    let buffer = self
      .live_buffers
      .read()
      .await
      .get(&id)
      .ok_or(BufferMappingError::BufNotFound(id))?
      .clone();
    let mut buffer = buffer.write().await;
    match r#type {
      transforms::TransformType::edit(transforms::Edit { point, payload }) => {
        let point = CodePointInsertionIndex(point.code_point_index as usize);
        let insertion_index: InsertionIndex = buffer.locate_code_point(point)?;
        match payload {
          transforms::EditPayload::insert(transforms::Insert { contents }) => {
            buffer.insert_at(insertion_index, &contents)?;
          },
          transforms::EditPayload::delete(transforms::Delete { distance }) => {
            let deletion_range = insertion_index.delete_range(distance.try_into()?);
            buffer.delete_at(deletion_range)?;
          },
        }
      },
      transforms::TransformType::sync(sync) => todo!("sync: {:?}", sync),
    }
    Ok(())
  }
}
<commit_msg>insert an upgradable read guard for fun<commit_after>/*
 * Description: Update an internal view of each buffer's state.
 *
 * Copyright (C) 2022 Danny McClanahan <dmcC2@hypnicjerk.ai>
 * SPDX-License-Identifier: AGPL-3.0-or-later
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

//! Update an internal view of each buffer's state.
//!
//! # TODO
//! 1. maintain a list of all lines of text in the buffer, along with their checksums & covered
//!    regions
//! 1. every time a line is modified, update its checksum + region
//! 1. if a newline is deleted or inserted, calculate new adjacent lines, regions, & checksums
//! 1. when a sync process fails, obtain the minimum sequence of hashed line insertions/deletions to
//!    apply
//! 1. TODO: figure out how to broadcast synced lines. rebroadcast them all periodically? LRU?
//!    MRU?
//!
//! # sync negotiation
//! - A provides a list of "trial lines" with indices and checksums to B.
//! - B provides a response of which checksums weren't found.
//!   - If B found any such lines, then B pauses receiving any new operations for that buffer
//!     momentarily.
//! - A provides the N previous/successive lines from each mismatched line from B.
//! - B reports the *line number intervals* which didn't match, so A can provide more
//!   previous/successive lines necessary for B to obtain the whole extent of non-matching
//!   lines. Note that there needs to be a sentinel value for the final line from A being the end of
//!   the buffer.
//! - [A & B repeat the above process until the non-matching intervals have been closed in on.]
//! - A then sends all of the line contents according to all the checksums B doesn't have.
//! - B applies all the changes to the line contents as a single edit.
//! - B then unpauses receiving operations for the buffer.

use crate::{buffers::BufferId, transforms};

use unicode_buffer::{Buffer, BufferError, CodePointInsertionIndex, InsertionIndex};

use async_lock::{RwLock, RwLockUpgradableReadGuard};
use displaydoc::Display;
use indexmap::IndexMap;
use thiserror::Error;

use std::{num, sync::Arc};

#[derive(Debug, Display, Error)]
pub enum BufferMappingError {
  /// error handling a buffer: {0}
  Buf(#[from] BufferError),
  /// failed to find a buffer with id {0}
  BufNotFound(BufferId),
  /// numerical conversion error: {0}
  Num(#[from] num::TryFromIntError),
}

#[derive(Debug, Clone)]
pub struct BufferMapping {
  pub live_buffers: Arc<RwLock<IndexMap<BufferId, Arc<RwLock<Buffer>>>>>,
}

impl BufferMapping {
  pub fn new() -> Self {
    Self {
      live_buffers: Arc::new(RwLock::new(IndexMap::new())),
    }
  }

  pub async fn initialize_buffer(
    &self,
    id: BufferId,
    contents: &str,
  ) -> Result<(), BufferMappingError> {
    let buffer = self
      .live_buffers
      .write()
      .await
      .entry(id)
      .or_insert_with(|| Arc::new(RwLock::new(Buffer::new())))
      .clone();
    buffer.write().await.replace(contents)?;
    Ok(())
  }

  pub async fn apply_transform(
    &self,
    id: BufferId,
    transform: transforms::Transform,
  ) -> Result<(), BufferMappingError> {
    let transforms::Transform { r#type } = transform;
    let buffer = self
      .live_buffers
      .read()
      .await
      .get(&id)
      .ok_or(BufferMappingError::BufNotFound(id))?
      .clone();
    match r#type {
      transforms::TransformType::edit(transforms::Edit { point, payload }) => {
        let point = CodePointInsertionIndex(point.code_point_index as usize);
        let buffer = buffer.upgradable_read().await;
        let insertion_index: InsertionIndex = buffer.locate_code_point(point)?;
        let mut buffer = RwLockUpgradableReadGuard::upgrade(buffer).await;
        match payload {
          transforms::EditPayload::insert(transforms::Insert { contents }) => {
            buffer.insert_at(insertion_index, &contents)?;
          },
          transforms::EditPayload::delete(transforms::Delete { distance }) => {
            let deletion_range = insertion_index.delete_range(distance.try_into()?);
            buffer.delete_at(deletion_range)?;
          },
        }
      },
      transforms::TransformType::sync(sync) => todo!("sync: {:?}", sync),
    }
    Ok(())
  }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::path::PathBuf;
use std::io;
use std::io::Write;
use std::fmt::Debug;
use retry_conf::RetryConf;
use rustc_serialize::Encodable;
use record::FluentError;
use forwardable::forward::Forward;
use std::fs::OpenOptions;
use record::Record;

fn ensure_file_with_wca(path: PathBuf) -> Result<File, io::Error> {
    let file = try!(OpenOptions::new().write(true).create(true).append(true).open(path));
    Ok(file)
}

pub fn maybe_write_record<T>(conf: &RetryConf,
                             record: Record<T>,
                             err: FluentError)
                             -> Result<(), FluentError>
    where T: Encodable + Debug
{
    let store_needed = conf.clone().need_to_store();
    let store_path = conf.clone().store_path();
    if store_needed && store_path.is_some() {
        match ensure_file_with_wca(store_path.unwrap()) {
            Ok(mut f) => {
                let mut w = Vec::new();
                write!(&mut w, "{}", record.dump()).unwrap();
                try!(f.write(&w));
                try!(f.sync_data());
            }
            Err(e) => return Err(From::from(e)),
        }
        Ok(())
    } else {
        Err(err)
    }
}

pub fn maybe_write_records<T>(conf: &RetryConf,
                              forward: Forward<T>,
                              err: FluentError)
                              -> Result<(), FluentError>
    where T: Encodable + Debug
{
    let store_needed = conf.clone().need_to_store();
    let store_path = conf.clone().store_path();
    if store_needed && store_path.is_some() {
        match ensure_file_with_wca(store_path.unwrap()) {
            Ok(mut f) => {
                let mut w = Vec::new();
                write!(&mut w, "{}", forward.dump()).unwrap();
                try!(f.write(&w));
                try!(f.sync_data());
            }
            Err(e) => return Err(From::from(e)),
        }
        Ok(())
    } else {
        Err(err)
    }
}

#[cfg(test)]
mod tests {
    extern crate tempdir;
    use super::*;
    use time;
    use std::collections::HashMap;
    use self::tempdir::TempDir;
    use record::Record;
    use retry_conf::RetryConf;
    use record::FluentError;
    use forwardable::forward::Forward;

    #[test]
    fn test_write_record() {
        let tag = "fruently".to_string();
        let time = time::now();
        let mut obj: HashMap<String, String> = HashMap::new();
        obj.insert("name".to_string(), "fruently".to_string());
        let record = Record::new(tag.clone(), time, obj.clone());
        let tmp = TempDir::new("fruently").unwrap().into_path().join("buffer");
        let conf = RetryConf::new().store_file(tmp.clone());
        assert!(maybe_write_record(&conf, record, FluentError::Dummy("dummy".to_string())).is_ok());
        assert!(tmp.exists())
    }

    #[test]
    fn test_write_record_2_times() {
        let tag = "fruently".to_string();
        let time = time::now();
        let mut obj: HashMap<String, String> = HashMap::new();
        obj.insert("name".to_string(), "fruently".to_string());
        let record = Record::new(tag.clone(), time, obj.clone());
        let tmp = TempDir::new("fruently").unwrap().into_path().join("buffer");
        let conf = RetryConf::new().store_file(tmp.clone());
        assert!(maybe_write_record(&conf, record, FluentError::Dummy("dummy".to_string())).is_ok());
        assert!(tmp.exists());
        let mut obj2: HashMap<String, String> = HashMap::new();
        obj2.insert("name2".to_string(), "fruently2".to_string());
        let record2 = Record::new(tag.clone(), time, obj2.clone());
        let conf2 = RetryConf::new().store_file(tmp.clone());
        assert!(maybe_write_record(&conf2, record2, FluentError::Dummy("dummy".to_string()))
            .is_ok());
        assert!(tmp.exists())
    }

    #[test]
    fn test_write_records() {
        let tag = "fruently".to_string();
        let mut obj1: HashMap<String, String> = HashMap::new();
        obj1.insert("hey".to_string(), "Rust with forward mode!".to_string());
        let mut obj2: HashMap<String, String> = HashMap::new();
        obj2.insert("yeah".to_string(), "Also sent together!".to_string());
        let time = time::now().to_timespec().sec;
        let entry = (time, obj1);
        let entry2 = (time, obj2);
        let entries = vec![(entry), (entry2)];
        let forward = Forward::new(tag, entries);
        let tmp = TempDir::new("fruently").unwrap().into_path().join("buffer");
        let conf = RetryConf::new().store_file(tmp.clone());
        assert!(maybe_write_records(&conf, forward, FluentError::Dummy("dummy".to_string()))
            .is_ok());
        assert!(tmp.exists())
    }
}
<commit_msg>store_buffer: Remove needless clauses<commit_after>use std::fs::File;
use std::path::PathBuf;
use std::io;
use std::io::Write;
use std::fmt::Debug;
use retry_conf::RetryConf;
use rustc_serialize::Encodable;
use record::FluentError;
use forwardable::forward::Forward;
use std::fs::OpenOptions;
use record::Record;

fn ensure_file_with_wca(path: PathBuf) -> Result<File, io::Error> {
    let file = try!(OpenOptions::new().write(true).create(true).append(true).open(path));
    Ok(file)
}

pub fn maybe_write_record<T>(conf: &RetryConf,
                             record: Record<T>,
                             err: FluentError)
                             -> Result<(), FluentError>
    where T: Encodable + Debug
{
    let store_needed = conf.clone().need_to_store();
    let store_path = conf.clone().store_path();
    if store_needed {
        match ensure_file_with_wca(store_path.unwrap()) {
            Ok(mut f) => {
                let mut w = Vec::new();
                write!(&mut w, "{}", record.dump()).unwrap();
                try!(f.write(&w));
                try!(f.sync_data());
            }
            Err(e) => return Err(From::from(e)),
        }
        Ok(())
    } else {
        Err(err)
    }
}

pub fn maybe_write_records<T>(conf: &RetryConf,
                              forward: Forward<T>,
                              err: FluentError)
                              -> Result<(), FluentError>
    where T: Encodable + Debug
{
    let store_needed = conf.clone().need_to_store();
    let store_path = conf.clone().store_path();
    if store_needed {
        match ensure_file_with_wca(store_path.unwrap()) {
            Ok(mut f) => {
                let mut w = Vec::new();
                write!(&mut w, "{}", forward.dump()).unwrap();
                try!(f.write(&w));
                try!(f.sync_data());
            }
            Err(e) => return Err(From::from(e)),
        }
        Ok(())
    } else {
        Err(err)
    }
}

#[cfg(test)]
mod tests {
    extern crate tempdir;
    use super::*;
    use time;
    use std::collections::HashMap;
    use self::tempdir::TempDir;
    use record::Record;
    use retry_conf::RetryConf;
    use record::FluentError;
    use forwardable::forward::Forward;

    #[test]
    fn test_write_record() {
        let tag = "fruently".to_string();
        let time = time::now();
        let mut obj: HashMap<String, String> = HashMap::new();
        obj.insert("name".to_string(), "fruently".to_string());
        let record = Record::new(tag.clone(), time, obj.clone());
        let tmp = TempDir::new("fruently").unwrap().into_path().join("buffer");
        let conf = RetryConf::new().store_file(tmp.clone());
        assert!(maybe_write_record(&conf, record, FluentError::Dummy("dummy".to_string())).is_ok());
        assert!(tmp.exists())
    }

    #[test]
    fn test_write_record_2_times() {
        let tag = "fruently".to_string();
        let time = time::now();
        let mut obj: HashMap<String, String> = HashMap::new();
        obj.insert("name".to_string(), "fruently".to_string());
        let record = Record::new(tag.clone(), time, obj.clone());
        let tmp = TempDir::new("fruently").unwrap().into_path().join("buffer");
        let conf = RetryConf::new().store_file(tmp.clone());
        assert!(maybe_write_record(&conf, record, FluentError::Dummy("dummy".to_string())).is_ok());
        assert!(tmp.exists());
        let mut obj2: HashMap<String, String> = HashMap::new();
        obj2.insert("name2".to_string(), "fruently2".to_string());
        let record2 = Record::new(tag.clone(), time, obj2.clone());
        let conf2 = RetryConf::new().store_file(tmp.clone());
        assert!(maybe_write_record(&conf2, record2, FluentError::Dummy("dummy".to_string()))
            .is_ok());
        assert!(tmp.exists())
    }

    #[test]
    fn test_write_records() {
        let tag = "fruently".to_string();
        let mut obj1: HashMap<String, String> = HashMap::new();
        obj1.insert("hey".to_string(), "Rust with forward mode!".to_string());
        let mut obj2: HashMap<String, String> = HashMap::new();
        obj2.insert("yeah".to_string(), "Also sent together!".to_string());
        let time = time::now().to_timespec().sec;
        let entry = (time, obj1);
        let entry2 = (time, obj2);
        let entries = vec![(entry), (entry2)];
        let forward = Forward::new(tag, entries);
        let tmp = TempDir::new("fruently").unwrap().into_path().join("buffer");
        let conf = RetryConf::new().store_file(tmp.clone());
        assert!(maybe_write_records(&conf, forward, FluentError::Dummy("dummy".to_string()))
            .is_ok());
        assert!(tmp.exists())
    }
}
<|endoftext|>


<commit_before>enum merge_comp<T> {
    lt,
    gt,
    eq(T)
}

fn merge<T: copy>(vec1: [T], vec2: [T], comp: fn(T, T) -> merge_comp<T>) -> [T] {
    let result = [];
    let i1 = 0u, i2 = 0u;
    let len1 = vec::len(vec1), len2 = vec::len(vec2);
    while (i1 < len1 && i2 < len2) {
        alt comp(vec1[i1], vec2[i2]) {
          lt    { result += [ vec1[i1] ]; i1 += 1u; }
          gt    { result += [ vec2[i2] ]; i2 += 1u;}
          eq(x) { result += [ x ]; i1 += 1u; i2 += 1u; }
        }
    }
    if i1 < len1 {
        result += vec::slice(vec1, i1, len1);
    }
    if i2 < len2 {
        result += vec::slice(vec2, i2, len2);
    }
    ret result;
}

fn mergei<T: copy>(vecs: [[T]], comp: fn(T, T) -> merge_comp<T>) -> [T] {
    ret alt vec::len(vecs) {
      0u { [] }
      1u { vecs[0] }
      l  {
        let pre  = mergei(vec::slice(vecs, 0u64, l / 2u64), comp);
        let post = mergei(vec::slice(vecs, l / 2u64, l), comp);
        ret merge(pre, post, comp);
      }
    }
}

fn zip_default<T: copy, U: copy>(v1: [const T], v2: [const U], def: (T, U)) -> [(T, U)] {
    let result = [];
    let (l1, l2) = (vec::len(v1), vec::len(v2));
    let (d1, d2) = def;
    uint::range(0u, uint::max(l1, l2)) { |i|
        let e1 = if i < l1 { v1[i] } else { d1 };
        let e2 = if i < l2 { v2[i] } else { d2 };
        result += [(e1, e2)];
    }
    ret result;
}

fn div_mod(n: uint, d: uint) -> (uint, uint) {
    (n / d, n % d)
}

#[cfg(test)]
mod tests {
    fn merge_add_comp(e1: (int, int), e2: (int, int)) -> merge_comp<(int, int)> {
        let ((n1, e1), (n2, e2)) = (e1, e2);
        if n1 < n2 { ret lt; }
        if n1 > n2 { ret gt; }
        ret eq((n1, e1 + e2));
    }

    fn merge_max_comp(e1: (int, int), e2: (int, int)) -> merge_comp<(int, int)> {
        let ((n1, e1), (n2, e2)) = (e1, e2);
        if n1 < n2 { ret lt; }
        if n1 > n2 { ret gt; }
        ret eq((n1, int::max(e1, e2)));
    }

    #[test]
    fn test_merge() {
        assert merge([(1, 1), (3, 1), (5, 1)], [(1, 2), (2, 1), (4, 1)], merge_add_comp) ==
            [(1, 3), (2, 1), (3, 1), (4, 1), (5, 1)];
        assert merge([(1, 1), (3, 1), (5, 1)], [(1, 2), (2, 1), (4, 1)], merge_max_comp) ==
            [(1, 2), (2, 1), (3, 1), (4, 1), (5, 1)];
        assert merge([(1, 1), (3, 1), (5, 1)], [], merge_add_comp) == [(1, 1), (3, 1), (5, 1)];
        assert merge([], [], merge_add_comp) == [];
    }

    #[test]
    fn test_mergei() {
        assert mergei([[], [], []], merge_add_comp) == [];
        assert mergei([[(1, 1), (2, 1)], [(1, 2), (3, 1)], [(-1, 3)]], merge_add_comp) ==
            [(-1, 3), (1, 3), (2, 1), (3, 1)];
        assert mergei([[(1, 1)], [(1, 2)], [(1, 3)]], merge_add_comp) == [(1, 6)];
    }

    #[test]
    fn test_zip_default() {
        assert zip_default([1, 2, 3], [4u, 5u, 6u], (0, 0u)) == [(1, 4u), (2, 5u), (3, 6u)];
        assert zip_default([1, 2, 3], [4u], (0, 0u)) == [(1, 4u), (2, 0u), (3, 0u)];
        assert zip_default([], [], (0, 0u)) == [];
    }

    #[test]
    fn test_div_mod() {
        let (d, m) = div_mod(1234u, 56u);
        assert d * 56u + m == 1234u;
    }
}
<commit_msg>Rename euler::util::div_mod => euler::util::div_rem<commit_after>enum merge_comp<T> {
    lt,
    gt,
    eq(T)
}

fn merge<T: copy>(vec1: [T], vec2: [T], comp: fn(T, T) -> merge_comp<T>) -> [T] {
    let result = [];
    let i1 = 0u, i2 = 0u;
    let len1 = vec::len(vec1), len2 = vec::len(vec2);
    while (i1 < len1 && i2 < len2) {
        alt comp(vec1[i1], vec2[i2]) {
          lt    { result += [ vec1[i1] ]; i1 += 1u; }
          gt    { result += [ vec2[i2] ]; i2 += 1u;}
          eq(x) { result += [ x ]; i1 += 1u; i2 += 1u; }
        }
    }
    if i1 < len1 {
        result += vec::slice(vec1, i1, len1);
    }
    if i2 < len2 {
        result += vec::slice(vec2, i2, len2);
    }
    ret result;
}

fn mergei<T: copy>(vecs: [[T]], comp: fn(T, T) -> merge_comp<T>) -> [T] {
    ret alt vec::len(vecs) {
      0u { [] }
      1u { vecs[0] }
      l  {
        let pre  = mergei(vec::slice(vecs, 0u64, l / 2u64), comp);
        let post = mergei(vec::slice(vecs, l / 2u64, l), comp);
        ret merge(pre, post, comp);
      }
    }
}

fn zip_default<T: copy, U: copy>(v1: [const T], v2: [const U], def: (T, U)) -> [(T, U)] {
    let result = [];
    let (l1, l2) = (vec::len(v1), vec::len(v2));
    let (d1, d2) = def;
    uint::range(0u, uint::max(l1, l2)) { |i|
        let e1 = if i < l1 { v1[i] } else { d1 };
        let e2 = if i < l2 { v2[i] } else { d2 };
        result += [(e1, e2)];
    }
    ret result;
}

fn div_rem(n: uint, d: uint) -> (uint, uint) {
    (n / d, n % d)
}

#[cfg(test)]
mod tests {
    fn merge_add_comp(e1: (int, int), e2: (int, int)) -> merge_comp<(int, int)> {
        let ((n1, e1), (n2, e2)) = (e1, e2);
        if n1 < n2 { ret lt; }
        if n1 > n2 { ret gt; }
        ret eq((n1, e1 + e2));
    }

    fn merge_max_comp(e1: (int, int), e2: (int, int)) -> merge_comp<(int, int)> {
        let ((n1, e1), (n2, e2)) = (e1, e2);
        if n1 < n2 { ret lt; }
        if n1 > n2 { ret gt; }
        ret eq((n1, int::max(e1, e2)));
    }

    #[test]
    fn test_merge() {
        assert merge([(1, 1), (3, 1), (5, 1)], [(1, 2), (2, 1), (4, 1)], merge_add_comp) ==
            [(1, 3), (2, 1), (3, 1), (4, 1), (5, 1)];
        assert merge([(1, 1), (3, 1), (5, 1)], [(1, 2), (2, 1), (4, 1)], merge_max_comp) ==
            [(1, 2), (2, 1), (3, 1), (4, 1), (5, 1)];
        assert merge([(1, 1), (3, 1), (5, 1)], [], merge_add_comp) == [(1, 1), (3, 1), (5, 1)];
        assert merge([], [], merge_add_comp) == [];
    }

    #[test]
    fn test_mergei() {
        assert mergei([[], [], []], merge_add_comp) == [];
        assert mergei([[(1, 1), (2, 1)], [(1, 2), (3, 1)], [(-1, 3)]], merge_add_comp) ==
            [(-1, 3), (1, 3), (2, 1), (3, 1)];
        assert mergei([[(1, 1)], [(1, 2)], [(1, 3)]], merge_add_comp) == [(1, 6)];
    }

    #[test]
    fn test_zip_default() {
        assert zip_default([1, 2, 3], [4u, 5u, 6u], (0, 0u)) == [(1, 4u), (2, 5u), (3, 6u)];
        assert zip_default([1, 2, 3], [4u], (0, 0u)) == [(1, 4u), (2, 0u), (3, 0u)];
        assert zip_default([], [], (0, 0u)) == [];
    }

    #[test]
    fn test_div_mod() {
        let (d, m) = div_mod(1234u, 56u);
        assert d * 56u + m == 1234u;
    }
}
<|endoftext|>


<commit_before>#![feature(try_from)]
#![feature(const_fn)]
#![feature(const_size_of)]

#[macro_use] extern crate log;
extern crate pest;
#[macro_use] extern crate pest_derive;
pub extern crate core;
extern crate machine;

mod var_instr;
mod property;
mod label;

use std::rc::Rc;
use std::io::Write;
use std::borrow::Cow;
use std::mem;
use std::collections::HashMap;
use std::convert::TryFrom;
use pest::{Parser, Error};
use pest::inputs::{StringInput, Span};
use pest::iterators::Pair;
use machine::instruction::mem_size::MemSize;
use machine::instruction::Instruction;
use core::{Header, COREWAR_EXEC_MAGIC, PROG_NAME_LENGTH, COMMENT_LENGTH};
use var_instr::variable::LabelNotFound;
use var_instr::VarInstr;
use property::Property;
use label::Label;

// force recompilation
const _GRAMMAR: &'static str = include_str!("asm.pest");

#[derive(Parser)]
#[grammar = "asm.pest"]
struct AsmParser;

type AsmPair = Pair<Rule, StringInput>;
type AsmSpan = Span<StringInput>;
pub type AsmError = Error<Rule, StringInput>;

pub struct ParsedProgram {
    file_pair: AsmPair,
    properties: HashMap<String, (AsmSpan, Option<AsmSpan>)>,
    instructions: Vec<Instruction>,
}

pub fn compile(input: &str) -> Result<Vec<u8>, AsmError> {
    let parsed_program = parse_program(input)?;
    let (name, comment, instrs) = destruct_program(&parsed_program)?;

    let mut output = Vec::with_capacity(mem::size_of::<Header>());
    raw_compile(&name, &comment, &instrs, &mut output);
    Ok(output)
}

pub fn parse_program(input: &str) -> Result<ParsedProgram, AsmError> {
    let input = StringInput::new(input.to_string());
    let mut pairs = AsmParser::parse(Rule::asm, Rc::new(input))?;

    let mut properties = HashMap::new();
    let mut label_offsets = HashMap::new();
    let mut var_instrs = Vec::new();
    let mut offset = 0;

    let file_pair = pairs.next().unwrap();
    for inner_pair in file_pair.clone().into_inner() {
        match inner_pair.as_rule() {
            Rule::props => for property_pair in inner_pair.into_inner() {
                let Property{ name, value } = Property::from(property_pair);
                properties.insert(name.as_str().to_string(), (name, value));
            },
            Rule::instr => {
                let var_instr = VarInstr::try_from(inner_pair)?;
                offset += var_instr.mem_size();
                var_instrs.push(var_instr);
            },
            Rule::label_decl => {
                let label = Label::from(inner_pair);
                if label_offsets.insert(label.clone(), offset).is_some() {
                    return Err(Error::CustomErrorSpan {
                        message: "label already declared".into(),
                        span: label.as_span().clone(),
                    })
                }
            },
            _ => (),
        };
    }

    let mut instructions = Vec::with_capacity(var_instrs.len());
    let mut offset = 0;
    for var_instr in &var_instrs {
        match var_instr.as_instr(offset, &label_offsets) {
            Ok(instr) => {
                offset += instr.mem_size();
                instructions.push(instr);
            },
            Err(LabelNotFound(label)) => return Err(Error::CustomErrorSpan {
                message: "label not found".into(),
                span: label.as_span().clone(),
            })
        }
    }

    Ok(ParsedProgram { file_pair, properties, instructions })
}

pub fn destruct_program(parsed_program: &ParsedProgram) -> Result<(String, String, Vec<Instruction>), AsmError> {
    let &ParsedProgram { ref file_pair, ref properties, ref instructions } = parsed_program;

    let name = match properties.get("name") {
        Some(&(_, Some(ref value))) if value.as_str().is_empty() => return Err(Error::CustomErrorSpan {
            message: "name property's value can't be empty".into(),
            span: value.clone(),
        }),
        Some(&(_, Some(ref value))) => {
            let max_name_len = PROG_NAME_LENGTH;
            let value_len = value.as_str().as_bytes().len();
            let len = if max_name_len < value_len {
                eprintln!("name property's value as been clamped to {} chars.", max_name_len);
                max_name_len
            } else { value_len };

            let value_bytes = &value.as_str().as_bytes()[..len];
            String::from_utf8_lossy(value_bytes)
        },
        Some(&(ref span, None)) => return Err(Error::CustomErrorPos {
            message: "name property need a value".into(),
            pos: span.start_pos(),
        }),
        None => return Err(Error::CustomErrorPos {
            message: "name property not found".into(),
            pos: file_pair.clone().into_span().start_pos(),
        }),
    };

    let comment = match properties.get("comment") {
        Some(&(_, Some(ref value))) => {
            let max_comment_len = COMMENT_LENGTH;
            let value_len = value.as_str().as_bytes().len();
            let len = if max_comment_len < value_len {
                eprintln!("comment property's value as been clamped to {} chars.", max_comment_len);
                max_comment_len
            } else { value_len };

            let value_bytes = &value.as_str().as_bytes()[..len];
            String::from_utf8_lossy(value_bytes)
        },
        _ => Cow::Borrowed(""),
    };

    Ok((name.into(), comment.into(), instructions.clone()))
}

pub fn raw_compile(name: &str, comment: &str, instrs: &[Instruction], output: &mut Vec<u8>) {
    let mut header = Header {
        magic: COREWAR_EXEC_MAGIC.to_be(),
        prog_name: [0u8; PROG_NAME_LENGTH + 1],
        prog_size: (instrs.iter().map(|x| x.mem_size()).sum::<usize>() as u32).to_be(),
        comment: [0u8; COMMENT_LENGTH + 1],
    };

    (&mut header.prog_name[..]).write_all(name.as_bytes()).unwrap();
    (&mut header.comment[..]).write_all(comment.as_bytes()).unwrap();

    let header: [u8; mem::size_of::<Header>()] = unsafe { mem::transmute(header) };
    output.write_all(&header).unwrap();

    for instr in instrs {
        instr.write_to(output).unwrap();
    }
}
<commit_msg>clean: Clean up the MemSize computing<commit_after>#![feature(try_from)]
#![feature(const_fn)]
#![feature(const_size_of)]

#[macro_use] extern crate log;
extern crate pest;
#[macro_use] extern crate pest_derive;
pub extern crate core;
extern crate machine;

mod var_instr;
mod property;
mod label;

use std::rc::Rc;
use std::io::Write;
use std::borrow::Cow;
use std::mem;
use std::collections::HashMap;
use std::convert::TryFrom;
use pest::{Parser, Error};
use pest::inputs::{StringInput, Span};
use pest::iterators::Pair;
use machine::instruction::mem_size::MemSize;
use machine::instruction::Instruction;
use core::{Header, COREWAR_EXEC_MAGIC, PROG_NAME_LENGTH, COMMENT_LENGTH};
use var_instr::variable::LabelNotFound;
use var_instr::VarInstr;
use property::Property;
use label::Label;

// force recompilation
const _GRAMMAR: &'static str = include_str!("asm.pest");

#[derive(Parser)]
#[grammar = "asm.pest"]
struct AsmParser;

type AsmPair = Pair<Rule, StringInput>;
type AsmSpan = Span<StringInput>;
pub type AsmError = Error<Rule, StringInput>;

pub struct ParsedProgram {
    file_pair: AsmPair,
    properties: HashMap<String, (AsmSpan, Option<AsmSpan>)>,
    instructions: Vec<Instruction>,
}

pub fn compile(input: &str) -> Result<Vec<u8>, AsmError> {
    let parsed_program = parse_program(input)?;
    let (name, comment, instrs) = destruct_program(&parsed_program)?;

    let mut output = Vec::with_capacity(mem::size_of::<Header>());
    raw_compile(&name, &comment, &instrs, &mut output);
    Ok(output)
}

pub fn parse_program(input: &str) -> Result<ParsedProgram, AsmError> {
    let input = StringInput::new(input.to_string());
    let mut pairs = AsmParser::parse(Rule::asm, Rc::new(input))?;

    let mut properties = HashMap::new();
    let mut label_offsets = HashMap::new();
    let mut var_instrs = Vec::new();
    let mut offset = 0;

    let file_pair = pairs.next().unwrap();
    for inner_pair in file_pair.clone().into_inner() {
        match inner_pair.as_rule() {
            Rule::props => for property_pair in inner_pair.into_inner() {
                let Property{ name, value } = Property::from(property_pair);
                properties.insert(name.as_str().to_string(), (name, value));
            },
            Rule::instr => {
                let var_instr = VarInstr::try_from(inner_pair)?;
                offset += var_instr.mem_size();
                var_instrs.push(var_instr);
            },
            Rule::label_decl => {
                let label = Label::from(inner_pair);
                if label_offsets.insert(label.clone(), offset).is_some() {
                    return Err(Error::CustomErrorSpan {
                        message: "label already declared".into(),
                        span: label.as_span().clone(),
                    })
                }
            },
            _ => (),
        };
    }

    let mut instructions = Vec::with_capacity(var_instrs.len());
    let mut offset = 0;
    for var_instr in &var_instrs {
        match var_instr.as_instr(offset, &label_offsets) {
            Ok(instr) => {
                offset += instr.mem_size();
                instructions.push(instr);
            },
            Err(LabelNotFound(label)) => return Err(Error::CustomErrorSpan {
                message: "label not found".into(),
                span: label.as_span().clone(),
            })
        }
    }

    Ok(ParsedProgram { file_pair, properties, instructions })
}

pub fn destruct_program(parsed_program: &ParsedProgram) -> Result<(String, String, Vec<Instruction>), AsmError> {
    let &ParsedProgram { ref file_pair, ref properties, ref instructions } = parsed_program;

    let name = match properties.get("name") {
        Some(&(_, Some(ref value))) if value.as_str().is_empty() => return Err(Error::CustomErrorSpan {
            message: "name property's value can't be empty".into(),
            span: value.clone(),
        }),
        Some(&(_, Some(ref value))) => {
            let max_name_len = PROG_NAME_LENGTH;
            let value_len = value.as_str().as_bytes().len();
            let len = if max_name_len < value_len {
                eprintln!("name property's value as been clamped to {} chars.", max_name_len);
                max_name_len
            } else { value_len };

            let value_bytes = &value.as_str().as_bytes()[..len];
            String::from_utf8_lossy(value_bytes)
        },
        Some(&(ref span, None)) => return Err(Error::CustomErrorPos {
            message: "name property need a value".into(),
            pos: span.start_pos(),
        }),
        None => return Err(Error::CustomErrorPos {
            message: "name property not found".into(),
            pos: file_pair.clone().into_span().start_pos(),
        }),
    };

    let comment = match properties.get("comment") {
        Some(&(_, Some(ref value))) => {
            let max_comment_len = COMMENT_LENGTH;
            let value_len = value.as_str().as_bytes().len();
            let len = if max_comment_len < value_len {
                eprintln!("comment property's value as been clamped to {} chars.", max_comment_len);
                max_comment_len
            } else { value_len };

            let value_bytes = &value.as_str().as_bytes()[..len];
            String::from_utf8_lossy(value_bytes)
        },
        _ => Cow::Borrowed(""),
    };

    Ok((name.into(), comment.into(), instructions.clone()))
}

pub fn raw_compile(name: &str, comment: &str, instrs: &[Instruction], output: &mut Vec<u8>) {
    let mut header = Header {
        magic: COREWAR_EXEC_MAGIC.to_be(),
        prog_name: [0u8; PROG_NAME_LENGTH + 1],
        prog_size: (instrs.iter().map(MemSize::mem_size).sum::<usize>() as u32).to_be(),
        comment: [0u8; COMMENT_LENGTH + 1],
    };

    (&mut header.prog_name[..]).write_all(name.as_bytes()).unwrap();
    (&mut header.comment[..]).write_all(comment.as_bytes()).unwrap();

    let header: [u8; mem::size_of::<Header>()] = unsafe { mem::transmute(header) };
    output.write_all(&header).unwrap();

    for instr in instrs {
        instr.write_to(output).unwrap();
    }
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use webcore::value::{Reference, FromReference};
use webcore::try_from::TryInto;
use webapi::array_buffer::ArrayBuffer;

pub trait ArrayKind: Sized {
    fn is_typed_array( reference: &Reference ) -> bool;
    fn into_typed_array( slice: &[Self] ) -> TypedArray< Self >;
    unsafe fn into_typed_array_no_copy( slice: &[Self] ) -> TypedArray< Self >;
    fn from_typed_array( array: &TypedArray< Self > ) -> Vec< Self >;
}

// TODO: Abstract this away in a macro and implement for all the types.
impl ArrayKind for u8 {
    fn is_typed_array( reference: &Reference ) -> bool {
        instanceof!( *reference, Uint8Array )
    }

    fn into_typed_array( slice: &[Self] ) -> TypedArray< Self > {
        let raw = __js_raw_asm!(
            "return Module.STDWEB.acquire_rust_reference( HEAPU8.slice( $0, $1 ) );",
            slice.as_ptr() as i32,
            (slice.as_ptr() as i32 + slice.len() as i32)
        );

        let reference = unsafe {
            Reference::from_raw_unchecked( raw )
        };

        TypedArray::from_reference( reference ).unwrap()
    }

    // This is unsafe due to the erasure of the slice's lifetime.
    unsafe fn into_typed_array_no_copy( slice: &[Self] ) -> TypedArray< Self > {
        let raw = __js_raw_asm!(
            "return Module.STDWEB.acquire_rust_reference( new Uint8Array( HEAPU8.buffer, $0, $1 ) );",
            slice.as_ptr() as i32,
            slice.len() as i32
        );

        let reference = Reference::from_raw_unchecked( raw );
        TypedArray::from_reference( reference ).unwrap()
    }

    fn from_typed_array( array: &TypedArray< Self > ) -> Vec< Self > {
        let length = array.len();
        let mut vector = Vec::with_capacity( length );

        js!( @(no_return)
            var array = @{array};
            var pointer = @{vector.as_ptr() as i32};
            HEAPU8.set( array, pointer );
        );

        unsafe {
            vector.set_len( length );
        }

        vector
    }
}

/// JavaScript typed arrays are array-like objects and provide a mechanism for accessing raw binary data.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays)
pub struct TypedArray< T: ArrayKind >( Reference, PhantomData< T > );

reference_boilerplate! {
    impl< T > for TypedArray< T > where (T: ArrayKind)
}

impl< T: ArrayKind > FromReference for TypedArray< T > {
    #[inline]
    fn from_reference( reference: Reference ) -> Option< Self > {
        if T::is_typed_array( &reference ) {
            Some( TypedArray( reference, PhantomData ) )
        } else {
            None
        }
    }
}

impl< T: ArrayKind > TypedArray< T > {
    /// Returns the [TypedArray](struct.ArrayBuffer.html) referenced by this typed array.
    ///
    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/buffer)
    pub fn buffer( &self ) -> ArrayBuffer {
        js!( return @{self}.buffer; ).try_into().unwrap()
    }

    /// Returns the number of elements in the buffer.
    ///
    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/length)
    pub fn len( &self ) -> usize {
        let reference = self.as_ref();
        let length: i32 = js!( return @{reference}.length; ).try_into().unwrap();
        length as usize
    }

    /// Copies `self` into a new `Vec`.
    pub fn to_vec( &self ) -> Vec< T > {
        T::from_typed_array( self )
    }
}

impl< 'a, T: ArrayKind > From< &'a [T] > for TypedArray< T > {
    fn from( slice: &'a [T] ) -> Self {
        T::into_typed_array( slice )
    }
}

impl From< TypedArray< u8 > > for Vec< u8 > {
    fn from( array: TypedArray< u8 > ) -> Self {
        u8::from_typed_array( &array )
    }
}

impl< 'a > From< &'a TypedArray< u8 > > for Vec< u8 > {
    fn from( array: &'a TypedArray< u8 > ) -> Self {
        u8::from_typed_array( array )
    }
}

#[cfg(test)]
mod tests {
    use super::TypedArray;
    use webcore::value::Value;
    use webcore::try_from::TryInto;

    #[test]
    fn into() {
        let array: &[u8] = &[1, 2, 4];
        let typed_array: TypedArray< u8 > = array.into();
        assert_eq!( js!( return @{&typed_array} instanceof Uint8Array; ), Value::Bool( true ) );
        assert_eq!( js!( return @{&typed_array}.length === 3; ), Value::Bool( true ) );
        assert_eq!( js!( return @{&typed_array}[0] === 1; ), Value::Bool( true ) );
        assert_eq!( js!( return @{&typed_array}[1] === 2; ), Value::Bool( true ) );
        assert_eq!( js!( return @{&typed_array}[2] === 4; ), Value::Bool( true ) );
    }

    #[test]
    fn from() {
        let value = js!( return new Uint8Array( [1, 2, 4] ); );
        let typed_array: TypedArray< u8 > = value.try_into().unwrap();
        let vec: Vec< u8 > = typed_array.into();
        assert_eq!( vec.len(), 3 );
        assert_eq!( vec, &[1, 2, 4] );
    }
}
<commit_msg>Implement additional TypedArray support (#52)<commit_after>use std::marker::PhantomData;
use std::mem::size_of;
use webcore::value::{Reference, FromReference};
use webcore::try_from::TryInto;
use webapi::array_buffer::ArrayBuffer;

pub trait ArrayKind: Sized {
    fn is_typed_array( reference: &Reference ) -> bool;
    fn into_typed_array( slice: &[Self] ) -> TypedArray< Self >;
    unsafe fn into_typed_array_no_copy( slice: &[Self] ) -> TypedArray< Self >;
    fn from_typed_array( array: &TypedArray< Self > ) -> Vec< Self >;
}

macro_rules! arraykind {
    ($element_type:ty, $js_array_type:ident, $heap_type:ident, $ptr_type:ty) => {
        impl ArrayKind for $element_type {
            fn is_typed_array( reference: &Reference ) -> bool {
                instanceof!( *reference, $js_array_type )
            }

            fn into_typed_array( slice: &[Self] ) -> TypedArray< Self > {
                let slice_ptr = (slice.as_ptr() as usize / size_of::<$element_type>()) as $ptr_type;
                let raw = __js_raw_asm!(
                    concat!(
                        "return Module.STDWEB.acquire_rust_reference( ",
                        stringify!($heap_type),
                        ".slice( $0, $1 ) );"
                    ),
                    slice_ptr,
                    (slice_ptr + slice.len() as $ptr_type)
                );

                let reference = unsafe {
                    Reference::from_raw_unchecked( raw )
                };

                TypedArray::from_reference( reference ).unwrap()
            }

            // This is unsafe due to the erasure of the slice's lifetime.
            unsafe fn into_typed_array_no_copy( slice: &[Self] ) -> TypedArray< Self > {
                let slice_ptr = (slice.as_ptr() as usize / size_of::<$element_type>()) as $ptr_type;
                let raw = __js_raw_asm!(
                    concat!(
                        "return Module.STDWEB.acquire_rust_reference( new $0( ",
                        stringify!($heap_type),
                        ".buffer, $1, $2 ) );"
                    ),
                    stringify!($js_array_type),
                    slice_ptr,
                    slice.len() as $ptr_type
                );

                let reference = Reference::from_raw_unchecked( raw );
                TypedArray::from_reference( reference ).unwrap()
            }

            fn from_typed_array( array: &TypedArray< Self > ) -> Vec< Self > {
                let length = array.len();
                let mut vector = Vec::with_capacity( length );
                let vec_ptr = (vector.as_ptr() as usize / size_of::<$element_type>()) as $ptr_type;

                js!( @(no_return)
                    var array = @{array};
                    var pointer = @{vec_ptr};
                    $heap_type.set( array, pointer );
                );

                unsafe {
                    vector.set_len( length );
                }

                vector
            }
        }

        impl From< TypedArray< $element_type > > for Vec< $element_type > {
            fn from( array: TypedArray< $element_type > ) -> Self {
                <$element_type>::from_typed_array( &array )
            }
        }

        impl< 'a > From< &'a TypedArray< $element_type > > for Vec< $element_type > {
            fn from( array: &'a TypedArray< $element_type > ) -> Self {
                <$element_type>::from_typed_array( array )
            }
        }
    }
}

arraykind!(i8, Int8Array, HEAP8, i32);
arraykind!(u8, Uint8Array, HEAPU8, i32);
arraykind!(i16, Int16Array, HEAP16, i32);
arraykind!(u16, Uint16Array, HEAPU16, i32);
arraykind!(i32, Int32Array, HEAP32, i32);
arraykind!(u32, Uint32Array, HEAPU32, i32);
arraykind!(f32, Float32Array, HEAPF32, i32);
arraykind!(f64, Float64Array, HEAPF64, i32);

/// JavaScript typed arrays are array-like objects and provide a mechanism for accessing raw binary data.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays)
pub struct TypedArray< T: ArrayKind >( Reference, PhantomData< T > );

reference_boilerplate! {
    impl< T > for TypedArray< T > where (T: ArrayKind)
}

impl< T: ArrayKind > FromReference for TypedArray< T > {
    #[inline]
    fn from_reference( reference: Reference ) -> Option< Self > {
        if T::is_typed_array( &reference ) {
            Some( TypedArray( reference, PhantomData ) )
        } else {
            None
        }
    }
}

impl< T: ArrayKind > TypedArray< T > {
    /// Returns the [TypedArray](struct.ArrayBuffer.html) referenced by this typed array.
    ///
    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/buffer)
    pub fn buffer( &self ) -> ArrayBuffer {
        js!( return @{self}.buffer; ).try_into().unwrap()
    }

    /// Returns the number of elements in the buffer.
    ///
    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/length)
    pub fn len( &self ) -> usize {
        let reference = self.as_ref();
        let length: i32 = js!( return @{reference}.length; ).try_into().unwrap();
        length as usize
    }

    /// Copies `self` into a new `Vec`.
    pub fn to_vec( &self ) -> Vec< T > {
        T::from_typed_array( self )
    }
}

impl< 'a, T: ArrayKind > From< &'a [T] > for TypedArray< T > {
    fn from( slice: &'a [T] ) -> Self {
        T::into_typed_array( slice )
    }
}

#[cfg(test)]
mod tests {
    macro_rules! arraykind_test {
        ($element_type: ident, $js_array_type: ident) => {
            mod $element_type {
                use super::super::TypedArray;
                use std;
                use webcore::try_from::TryInto;
                use webcore::value::Value;
                const ARRAY: &[$element_type] = &[
                    std::$element_type::MIN,
                    std::$element_type::MAX
                ];

                #[test]
                fn into() {
                    let typed_array: TypedArray< $element_type > = ARRAY.into();
                    assert_eq!(
                        js!( return @{&typed_array} instanceof $js_array_type; ),
                        Value::Bool( true )
                    );
                    assert_eq!(
                        js!( return @{&typed_array}.length === @{ARRAY.len() as u32}; ),
                        Value::Bool( true )
                    );
                    assert_eq!(
                        js!( return @{&typed_array}[0] === @{ARRAY[0]}; ),
                        Value::Bool( true )
                    );
                    assert_eq!(
                        js!( return @{&typed_array}[1] === @{ARRAY[1]}; ),
                        Value::Bool( true )
                    );
                }

                #[test]
                fn from() {
                    let value = js!( return new $js_array_type( [@{ARRAY[0]}, @{ARRAY[1]}] ); );
                    let typed_array: TypedArray< $element_type > = value.try_into().unwrap();
                    let vec: Vec< $element_type > = typed_array.into();
                    assert_eq!( vec.len(), ARRAY.len() );
                    assert_eq!( vec, ARRAY);
                }
            }
        }
    }

    arraykind_test!(i8, Int8Array);
    arraykind_test!(u8, Uint8Array);
    arraykind_test!(i16, Int16Array);
    arraykind_test!(u16, Uint16Array);
    arraykind_test!(i32, Int32Array);
    arraykind_test!(u32, Uint32Array);
    arraykind_test!(f32, Float32Array);
    arraykind_test!(f64, Float64Array);
}
<|endoftext|>


<commit_before>use std::io::Write;

fn main() {
    let out_dir = std::env::var("OUT_DIR").unwrap();
    let destination = std::path::Path::new(&out_dir).join("reftests.rs");
    let mut f = std::fs::File::create(&destination).unwrap();

    let files = std::fs::read_dir("../../tests/xc2bit/reftests").unwrap();

    for file in files {
        let path = file.expect("failed to get path").path();
        if path.extension().expect("bogus reftest filename (doesn't have extension)") == "jed" {
            let path = path.canonicalize().unwrap();

            let id_string = path.file_name().unwrap().to_str().unwrap().chars().map(|x| match x {
                'A'...'Z' | 'a'...'z' | '0'...'9' => x,
                _ => '_',
            }).collect::<String>();

            write!(f, r#"
                #[test]
                fn reftest_{}() {{
                    run_one_reftest("{}");
                }}
                "#, id_string, path.to_str().unwrap()).unwrap();
        }
    }
}
<commit_msg>xc2bit: Use CARGO_MANIFEST_DIR rather than assuming the cwd<commit_after>use std::io::Write;

fn main() {
    let out_dir = std::env::var("OUT_DIR").unwrap();
    let destination = std::path::Path::new(&out_dir).join("reftests.rs");
    let mut f = std::fs::File::create(&destination).unwrap();

    let root_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
    let reftests_dir = std::path::Path::new(&root_dir).join("../../tests/xc2bit/reftests");
    let files = std::fs::read_dir(reftests_dir).unwrap();

    for file in files {
        let path = file.expect("failed to get path").path();
        if path.extension().expect("bogus reftest filename (doesn't have extension)") == "jed" {
            let path = path.canonicalize().unwrap();

            let id_string = path.file_name().unwrap().to_str().unwrap().chars().map(|x| match x {
                'A'...'Z' | 'a'...'z' | '0'...'9' => x,
                _ => '_',
            }).collect::<String>();

            write!(f, r#"
                #[test]
                fn reftest_{}() {{
                    run_one_reftest("{}");
                }}
                "#, id_string, path.to_str().unwrap()).unwrap();
        }
    }
}
<|endoftext|>