<commit_before>extern crate chrono;

use chrono::prelude::*;
use regex::{Regex, Captures};

#[derive(Debug)]
pub enum Command {
    BadCommand,
    OneTimeEvent(OneTimeEventImpl),
}

#[derive(Debug)]
pub struct OneTimeEventImpl{
    pub event_time : DateTime<Utc>,
    pub event_text : String,
}

const MOMENT_REGEX: &str =
    r"(?P<day>[\d]*)(?:-(?P<month>[\d]*))?(?:-(?P<year>[\d]*))? (?P<hour>[\d]*).(?P<minute>[\d]*)";

const DURATION_REGEX: &str =
    r"(:?(?P<days>[\d]*)[D|d|Д|д])?(:?(?P<hours>[\d]*)[H|h|Ч|ч])?(:?(?P<minuts>[\d]*)[M|m|М|м])?(:?(?P<seconds>[\d]*)[S|s|С|с])?";

pub fn parse_command(command_line : String) -> Command {
    let command_line = String::from(command_line.trim());
    debug!("parse incoming text: {}", command_line);

    let mut result;
    result = try_parse_for(&command_line);
    if result.is_some() {
        return result.unwrap();
    }

    result = try_parse_at(&command_line);
    if result.is_some() {
        return result.unwrap();
    }

    warn!("parse_command: line {} doesn't match any regex", command_line);
    Command::BadCommand
}

fn try_parse_at(command_line : &String) -> Option<Command>{
    let reg = String::from(r"[at|в]\s*") + MOMENT_REGEX + " (?P<main_text>.*)";
    let time_format = Regex::new(&reg[..]).unwrap();

    let date_captures = time_format.captures(command_line);
    if date_captures.is_none() {
        return None;
    }
    let date_captures = date_captures.unwrap();
    let text = date_captures.name("main_text").unwrap().as_str();

    const DEFAULT_TZ : i64 = -3;
    let dt = chrono::Duration::seconds(DEFAULT_TZ * 60 * 60);
    let d = get_datetime_from_capture(&date_captures) + dt;

    Some(Command::OneTimeEvent
            (OneTimeEventImpl
                { event_text : String::from(text)
                , event_time : d }
            ))
}

fn try_parse_for(command_line : &String) -> Option<Command>{
    let reg_main    = Regex::new(r"^(?P<spec>[\d|D|d|Д|д|H|h|Ч|ч|M|m|М|м|S|s|С|с]*)(?P<divider> )(?P<main_text>.*)").unwrap();

    let reg_day     = r"(?P<days>[\d]*)[D|d|Д|д]";
    let reg_hour    = r"(?P<hours>[\d]*)[H|h|Ч|ч]";
    let reg_min     = r"(?P<minuts>[\d]*)[M|m|М|м]";
    let reg_sec     = r"(?P<seconds>[\d]*)[S|s|С|с]";

    let caps = reg_main.captures(command_line);
    if caps.is_none() {
        return None;
    }
    let caps = caps.unwrap();
    let spec = caps.name("spec").unwrap().as_str();
    let text = caps.name("main_text").unwrap().as_str();

    let days    = get_first_regex_group_as_u32(reg_day,  spec);
    let hours   = get_first_regex_group_as_u32(reg_hour, spec);
    let minutes = get_first_regex_group_as_u32(reg_min,  spec);
    let seconds = get_first_regex_group_as_u32(reg_sec,  spec);

    if days == 0 && hours == 0 && minutes == 0 && seconds == 0 {
        return None;
    }

    // TODO: should we a better way to do this
    let dt = chrono::Duration::seconds((days as i64) * (60*60*24)
                                    + (hours as i64) * (60*60)
                                    + (minutes as i64) * 60
                                    + (seconds as i64)
                                    );

    let event_time = Utc::now() + dt;

    Some(Command::OneTimeEvent(OneTimeEventImpl
            { event_text : String::from(text)
            , event_time : event_time }
        ))
}

fn get_first_regex_group_as_u32(reg : &str, text : &str) -> u32{
    let reg = Regex::new(reg).expect("get_first_regex_group_as_u32: wrong regex string");
    let number = match reg.captures(text){
        None => "0",
        Some(d) => d.get(1)
            .expect("get_first_regex_group_as_u32: expect regex string with at least 1 group")
            .as_str(),
    };
    number.parse().unwrap_or(0)
}

fn get_datetime_from_capture(cap: &Captures) -> DateTime<Utc>{
    let day = cap.name("day").unwrap().as_str().parse().unwrap();
    let hour = cap.name("hour").unwrap().as_str().parse().unwrap();
    let minute = cap.name("minute").unwrap().as_str().parse().unwrap();

    let now = Utc::now();

    let month = match cap.name("month"){
        None => now.month(),
        Some(m) => m.as_str().parse().unwrap(),
    };

    let year = match cap.name("year"){
        None => now.year(),
        Some(m) => m.as_str().parse().unwrap(),
    };

    Utc.ymd(year, month, day).and_hms(hour, minute, 0)
}

//-------- TESTS ---------------------------------------------------------------

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

    #[test]
    fn parse_for_general() {
        let command = String::from("1d2h3m4s");
        let text = "some text";
        let command_text = command + " " + text;
        let dt = chrono::Duration::seconds((1 as i64) * (60*60*24)  // days
                                        + (2 as i64) * (60*60)      // hours
                                        + (3 as i64) * 60           // minutes
                                        + (4 as i64)                // seconds
                                        );
        let result = try_parse_for(&command_text);
        assert!(result.is_some());
        match result.unwrap(){
            OneTimeEvent(res) => {
                assert_eq!(res.event_text, text);
                assert!(time_moment_eq(res.event_time, Utc::now() + dt));
            },
            _ => panic!("Wrong command type")
        };
    }

    #[test]
    fn parse_for_hm() {
        let command = String::from("2h30m");
        let text = "some text";
        let command_text = command + " " + text;
        let dt = chrono::Duration::seconds((0 as i64) * (60*60*24)  // days
                                        + (2 as i64) * (60*60)      // hours
                                        + (30 as i64) * 60           // minutes
                                        + (0 as i64)                // seconds
                                        );
        let result = try_parse_for(&command_text);
        assert!(result.is_some());
        match result.unwrap(){
            OneTimeEvent(res) => {
                assert_eq!(res.event_text, text);
                assert!(time_moment_eq(res.event_time, Utc::now() + dt));
            },
            _ => panic!("Wrong command type")
        };
    }

    #[test]
    // #[ignore]
    fn parse_for_negative() {
        let command = String::from("1d-2h3m4s");
        let text = "some text";
        let command_text = command + " " + text;
        let result = try_parse_for(&command_text);
        assert!(result.is_none());
    }

    #[test]
    // #[ignore]
    fn parse_for_misspell() {
        let command = String::from("1d2j3m4s");
        let text = "some text";
        let command_text = command + " " + text;
        let result = try_parse_for(&command_text);
        assert!(result.is_none());
    }

    #[test]
    fn parse_at_tests() {
        {
            let command = String::from("24-10 18:30");
            let text = "some text";
            let mut command_text = command + " " + text;
            command_text.insert_str(0, "at");
            let now = Utc::now();
            let t = Utc.ymd(now.year(), 10, 24).and_hms(18-3, 30, 0);

            let result = try_parse_at(&command_text);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }

        {
            let command = String::from("24 18:30");
            let text = "some text";
            let mut command_text = command + " " + text;
            command_text.insert_str(0, "at");
            let now = Utc::now();
            let t = Utc.ymd(now.year(), now.month(), 24).and_hms(18-3, 30, 0);

            let result = try_parse_at(&command_text);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }
    }

    fn time_moment_eq(t1: DateTime<Utc>, t2: DateTime<Utc>) -> bool{
        t1.signed_duration_since(t2).num_milliseconds().abs() < 100
    }
}<commit_msg>Command refactoring + regex bugfix<commit_after>extern crate chrono;

use chrono::prelude::*;
use regex::{Regex, Captures};

#[derive(Debug)]
pub enum Command {
    BadCommand,
    OneTimeEvent(OneTimeEventImpl),
}

#[derive(Debug)]
pub struct OneTimeEventImpl{
    pub event_time : DateTime<Utc>,
    pub event_text : String,
}

const MOMENT_REGEX: &str =
    r"(?P<day>[\d]*)(?:-(?P<month>[\d]*))?(?:-(?P<year>[\d]*))? (?P<hour>[\d]*).(?P<minute>[\d]*)";

const DURATION_REGEX: &str =
    r"(:?(?P<day>[\d]*)[D|d|Д|д])?(:?(?P<hour>[\d]*)[H|h|Ч|ч])?(:?(?P<minute>[\d]*)[M|m|М|м])?(:?(?P<second>[\d]*)[S|s|С|с])?";

pub fn parse_command(command_line : String) -> Command {
    let command_line = String::from(command_line.trim());
    debug!("parse incoming text: {}", command_line);

    let mut result;
    result = try_parse_for(&command_line);
    if result.is_some() {
        return result.unwrap();
    }

    result = try_parse_at(&command_line);
    if result.is_some() {
        return result.unwrap();
    }

    result = try_parse_rep(&command_line);
    if result.is_some() {
        return result.unwrap();
    }

    warn!("parse_command: line {} doesn't match any regex", command_line);
    Command::BadCommand
}

fn try_parse_at(command_line : &String) -> Option<Command>{
    let reg = String::from(r"^(at|в)\s*") + MOMENT_REGEX + " (?P<main_text>.*)";
    let time_format = Regex::new(&reg[..]).unwrap();

    let date_captures = time_format.captures(command_line);

    if date_captures.is_none() {
        return None;
    }
    let date_captures = date_captures.unwrap();
    let text = date_captures.name("main_text").unwrap().as_str();

    const DEFAULT_TZ : i64 = -3;
    let dt = chrono::Duration::seconds(DEFAULT_TZ * 60 * 60);
    let d = get_datetime_from_capture(&date_captures) + dt;

    Some(Command::OneTimeEvent
            (OneTimeEventImpl
                { event_text : String::from(text)
                , event_time : d }
            ))
}

fn try_parse_for(command_line : &String) -> Option<Command>{
    let reg = String::from("^") + DURATION_REGEX + "(?P<divider> )(?P<main_text>.*)";
    let reg = Regex::new(&reg[..]).unwrap();

    let capture = reg.captures(command_line);
    if capture.is_none(){
        return None;
    }
    let capture = capture.unwrap();

    let text = capture.name("main_text").unwrap().as_str();
    let dt = get_duration_from_capture(&capture);
    if dt.is_none() {
        return None;
    }

    Some(Command::OneTimeEvent(OneTimeEventImpl
            { event_text : String::from(text)
            , event_time : Utc::now() + dt.unwrap() }
        ))
}

// fn try_parse_for(command_line : &String) -> Option<Command>{
//     let reg_main    = Regex::new(r"^(?P<spec>[\d|D|d|Д|д|H|h|Ч|ч|M|m|М|м|S|s|С|с]*)(?P<divider> )(?P<main_text>.*)").unwrap();

//     let reg_day     = r"(?P<days>[\d]*)[D|d|Д|д]";
//     let reg_hour    = r"(?P<hours>[\d]*)[H|h|Ч|ч]";
//     let reg_min     = r"(?P<minuts>[\d]*)[M|m|М|м]";
//     let reg_sec     = r"(?P<seconds>[\d]*)[S|s|С|с]";

//     let caps = reg_main.captures(command_line);
//     if caps.is_none() {
//         return None;
//     }
//     let caps = caps.unwrap();
//     let spec = caps.name("spec").unwrap().as_str();
//     let text = caps.name("main_text").unwrap().as_str();

//     let days    = get_first_regex_group_as_u32(reg_day,  spec);
//     let hours   = get_first_regex_group_as_u32(reg_hour, spec);
//     let minutes = get_first_regex_group_as_u32(reg_min,  spec);
//     let seconds = get_first_regex_group_as_u32(reg_sec,  spec);

//     if days == 0 && hours == 0 && minutes == 0 && seconds == 0 {
//         return None;
//     }

//     // TODO: should we a better way to do this
//     let dt = chrono::Duration::seconds((days as i64) * (60*60*24)
//                                     + (hours as i64) * (60*60)
//                                     + (minutes as i64) * 60
//                                     + (seconds as i64)
//                                     );

//     let event_time = Utc::now() + dt;

//     Some(Command::OneTimeEvent(OneTimeEventImpl
//             { event_text : String::from(text)
//             , event_time : event_time }
//         ))
// }

fn try_parse_rep(command_line: &String) -> Option<Command> {
    None
}

// fn get_first_regex_group_as_u32(reg : &str, text : &str) -> u32{
//     let reg = Regex::new(reg).expect("get_first_regex_group_as_u32: wrong regex string");
//     let number = match reg.captures(text){
//         None => "0",
//         Some(d) => d.get(1)
//             .expect("get_first_regex_group_as_u32: expect regex string with at least 1 group")
//             .as_str(),
//     };
//     number.parse().unwrap_or(0)
// }

fn get_duration_from_capture(cap: &Captures) -> Option<chrono::Duration>{
    let day:    i64 = cap.name("day").map_or("0", |c| c.as_str() ).parse().unwrap();
    let hour:   i64 = cap.name("hour").map_or("0", |c| c.as_str() ).parse().unwrap();
    let minute: i64 = cap.name("minute").map_or("0", |c| c.as_str() ).parse().unwrap();
    let second: i64 = cap.name("second").map_or("0", |c| c.as_str() ).parse().unwrap();

    if day == 0 && hour == 0 && minute == 0 && second == 0 {
        return None;
    }

    Some(chrono::Duration::seconds((day as i64) * (60*60*24)
                                + (hour as i64) * (60*60)
                                + (minute as i64) * 60
                                + (second as i64)))
}

fn get_datetime_from_capture(cap: &Captures) -> DateTime<Utc>{
    let day = cap.name("day").unwrap().as_str().parse().unwrap();
    let hour = cap.name("hour").unwrap().as_str().parse().unwrap();
    let minute = cap.name("minute").unwrap().as_str().parse().unwrap();

    let now = Utc::now();

    let month = match cap.name("month"){
        None => now.month(),
        Some(m) => m.as_str().parse().unwrap(),
    };

    let year = match cap.name("year"){
        None => now.year(),
        Some(m) => m.as_str().parse().unwrap(),
    };

    Utc.ymd(year, month, day).and_hms(hour, minute, 0)
}

//-------- TESTS ---------------------------------------------------------------

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

    #[test]
    fn parse_for_general() {
        let command = String::from("1d2h3m4s");
        let text = "some text";
        let command_text = command + " " + text;
        let dt = chrono::Duration::seconds((1 as i64) * (60*60*24)  // days
                                        + (2 as i64) * (60*60)      // hours
                                        + (3 as i64) * 60           // minutes
                                        + (4 as i64)                // seconds
                                        );
        let result = try_parse_for(&command_text);
        assert!(result.is_some());
        match result.unwrap(){
            OneTimeEvent(res) => {
                assert_eq!(res.event_text, text);
                assert!(time_moment_eq(res.event_time, Utc::now() + dt));
            },
            _ => panic!("Wrong command type")
        };
    }

    #[test]
    fn parse_for_hm() {
        let command = String::from("2h30m");
        let text = "some text";
        let command_text = command + " " + text;
        let dt = chrono::Duration::seconds((0 as i64) * (60*60*24)  // days
                                        + (2 as i64) * (60*60)      // hours
                                        + (30 as i64) * 60           // minutes
                                        + (0 as i64)                // seconds
                                        );
        let result = try_parse_for(&command_text);
        assert!(result.is_some());
        match result.unwrap(){
            OneTimeEvent(res) => {
                assert_eq!(res.event_text, text);
                assert!(time_moment_eq(res.event_time, Utc::now() + dt));
            },
            _ => panic!("Wrong command type")
        };
    }

    #[test]
    // #[ignore]
    fn parse_for_negative() {
        let command = String::from("1d-2h3m4s");
        let text = "some text";
        let command_text = command + " " + text;
        let result = try_parse_for(&command_text);
        assert!(result.is_none());
    }

    #[test]
    // #[ignore]
    fn parse_for_misspell() {
        let command = String::from("1d2j3m4s");
        let text = "some text";
        let command_text = command + " " + text;
        let result = try_parse_for(&command_text);
        assert!(result.is_none());
    }

    #[test]
    fn parse_at_tests() {
        {
            let command = String::from("24-10 18:30");
            let text = "some text";
            let mut command_text = command + " " + text;
            command_text.insert_str(0, "at ");
            let now = Utc::now();
            let t = Utc.ymd(now.year(), 10, 24).and_hms(18-3, 30, 0);

            let result = try_parse_at(&command_text);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }

        {
            let command = String::from("24 18:30");
            let text = "some text";
            let mut command_text = command + " " + text;
            command_text.insert_str(0, "at ");
            let now = Utc::now();
            let t = Utc.ymd(now.year(), now.month(), 24).and_hms(18-3, 30, 0);

            let result = try_parse_at(&command_text);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }
    }

    fn time_moment_eq(t1: DateTime<Utc>, t2: DateTime<Utc>) -> bool{
        t1.signed_duration_since(t2).num_milliseconds().abs() < 100
    }
}<|endoftext|>


<commit_before>#![feature(track_caller)]
use std::fmt::Debug;

// Helper function to avoid promotion so that this tests "run-time" casts, not CTFE.
// Doesn't make a big difference when running this in Miri, but it means we can compare this
// with the LLVM backend by running `rustc -Zmir-opt-level=0 -Zsaturating-float-casts`.
#[track_caller]
#[inline(never)]
fn assert_eq<T: PartialEq + Debug>(x: T, y: T) {
    assert_eq!(x, y);
}

fn main() {
    // basic arithmetic
    assert_eq(6.0_f32*6.0_f32, 36.0_f32);
    assert_eq(6.0_f64*6.0_f64, 36.0_f64);
    assert_eq(-{5.0_f32}, -5.0_f32);
    assert_eq(-{5.0_f64}, -5.0_f64);
    // infinities, NaN
    assert!((5.0_f32/0.0).is_infinite());
    assert!((5.0_f64/0.0).is_infinite());
    assert!((-5.0_f32).sqrt().is_nan());
    assert!((-5.0_f64).sqrt().is_nan());
    // byte-level transmute
    let x: u64 = unsafe { std::mem::transmute(42.0_f64) };
    let y: f64 = unsafe { std::mem::transmute(x) };
    assert_eq(y, 42.0_f64);
    let x: u32 = unsafe { std::mem::transmute(42.0_f32) };
    let y: f32 = unsafe { std::mem::transmute(x) };
    assert_eq(y, 42.0_f32);

    // f32 <-> int casts
    assert_eq(5.0f32 as u32, 5);
    assert_eq(-5.0f32 as u32, 0);
    assert_eq(5.0f32 as i32, 5);
    assert_eq(-5.0f32 as i32, -5);
    assert_eq(std::f32::MAX as i32, i32::MAX);
    assert_eq(std::f32::INFINITY as i32, i32::MAX);
    assert_eq(std::f32::MAX as u32, u32::MAX);
    assert_eq(std::f32::INFINITY as u32, u32::MAX);
    assert_eq(std::f32::MIN as i32, i32::MIN);
    assert_eq(std::f32::NEG_INFINITY as i32, i32::MIN);
    assert_eq(std::f32::MIN as u32, 0);
    assert_eq(std::f32::NEG_INFINITY as u32, 0);
    assert_eq(std::f32::NAN as i32, 0);
    assert_eq(std::f32::NAN as u32, 0);
    assert_eq((u32::MAX-127) as f32 as u32, u32::MAX); // rounding loss
    assert_eq((u32::MAX-128) as f32 as u32, u32::MAX-255); // rounding loss
    assert_eq(127i8 as f32, 127.0f32);
    assert_eq(i128::MIN as f32, -170141183460469231731687303715884105728.0f32);
    assert_eq(u128::MAX as f32, std::f32::INFINITY); // saturation

    // f64 <-> int casts
    assert_eq(5.0f64 as u64, 5);
    assert_eq(-5.0f64 as u64, 0);
    assert_eq(5.0f64 as i64, 5);
    assert_eq(-5.0f64 as i64, -5);
    assert_eq(std::f64::MAX as i64, i64::MAX);
    assert_eq(std::f64::INFINITY as i64, i64::MAX);
    assert_eq(std::f64::MAX as u64, u64::MAX);
    assert_eq(std::f64::INFINITY as u64, u64::MAX);
    assert_eq(std::f64::MIN as i64, i64::MIN);
    assert_eq(std::f64::NEG_INFINITY as i64, i64::MIN);
    assert_eq(std::f64::MIN as u64, 0);
    assert_eq(std::f64::NEG_INFINITY as u64, 0);
    assert_eq(std::f64::NAN as i64, 0);
    assert_eq(std::f64::NAN as u64, 0);
    assert_eq((u64::MAX-1023) as f64 as u64, u64::MAX); // rounding loss
    assert_eq((u64::MAX-1024) as f64 as u64, u64::MAX-2047); // rounding loss
    assert_eq(u128::MAX as f64 as u128, u128::MAX);
    assert_eq(i16::MIN as f64, -32768.0f64);
    assert_eq(u128::MAX as f64, 340282366920938463463374607431768211455.0f64); // even that fits...

    // f32 <-> f64 casts
    assert_eq(5.0f64 as f32, 5.0f32);
    assert_eq(5.0f32 as f64, 5.0f64);
    assert_eq(std::f64::MAX as f32, std::f32::INFINITY);
    assert_eq(std::f64::MIN as f32, std::f32::NEG_INFINITY);
    assert_eq(std::f32::INFINITY as f64, std::f64::INFINITY);
    assert_eq(std::f32::NEG_INFINITY as f64, std::f64::NEG_INFINITY);

    // f32 min/max
    assert_eq((1.0 as f32).max(-1.0), 1.0);
    assert_eq((1.0 as f32).min(-1.0), -1.0);
    assert_eq(std::f32::NAN.min(9.0), 9.0);
    assert_eq(std::f32::NAN.max(-9.0), -9.0);
    assert_eq((9.0 as f32).min(std::f32::NAN), 9.0);
    assert_eq((-9.0 as f32).max(std::f32::NAN), -9.0);

    // f64 min/max
    assert_eq((1.0 as f64).max(-1.0), 1.0);
    assert_eq((1.0 as f64).min(-1.0), -1.0);
    assert_eq(std::f64::NAN.min(9.0), 9.0);
    assert_eq(std::f64::NAN.max(-9.0), -9.0);
    assert_eq((9.0 as f64).min(std::f64::NAN), 9.0);
    assert_eq((-9.0 as f64).max(std::f64::NAN), -9.0);

    // f32 copysign
    assert_eq(3.5_f32.copysign(0.42), 3.5_f32);
    assert_eq(3.5_f32.copysign(-0.42), -3.5_f32);
    assert_eq((-3.5_f32).copysign(0.42), 3.5_f32);
    assert_eq((-3.5_f32).copysign(-0.42), -3.5_f32);
    assert!(std::f32::NAN.copysign(1.0).is_nan());

    // f64 copysign
    assert_eq(3.5_f64.copysign(0.42), 3.5_f64);
    assert_eq(3.5_f64.copysign(-0.42), -3.5_f64);
    assert_eq((-3.5_f64).copysign(0.42), 3.5_f64);
    assert_eq((-3.5_f64).copysign(-0.42), -3.5_f64);
    assert!(std::f64::NAN.copysign(1.0).is_nan());
}
<commit_msg>Auto merge of #1310 - RalfJung:float-assoc, r=RalfJung<commit_after>#![feature(track_caller)]
use std::fmt::Debug;

// Helper function to avoid promotion so that this tests "run-time" casts, not CTFE.
// Doesn't make a big difference when running this in Miri, but it means we can compare this
// with the LLVM backend by running `rustc -Zmir-opt-level=0 -Zsaturating-float-casts`.
#[track_caller]
#[inline(never)]
fn assert_eq<T: PartialEq + Debug>(x: T, y: T) {
    assert_eq!(x, y);
}

fn main() {
    // basic arithmetic
    assert_eq(6.0_f32*6.0_f32, 36.0_f32);
    assert_eq(6.0_f64*6.0_f64, 36.0_f64);
    assert_eq(-{5.0_f32}, -5.0_f32);
    assert_eq(-{5.0_f64}, -5.0_f64);
    // infinities, NaN
    assert!((5.0_f32/0.0).is_infinite());
    assert!((5.0_f64/0.0).is_infinite());
    assert!((-5.0_f32).sqrt().is_nan());
    assert!((-5.0_f64).sqrt().is_nan());
    // byte-level transmute
    let x: u64 = unsafe { std::mem::transmute(42.0_f64) };
    let y: f64 = unsafe { std::mem::transmute(x) };
    assert_eq(y, 42.0_f64);
    let x: u32 = unsafe { std::mem::transmute(42.0_f32) };
    let y: f32 = unsafe { std::mem::transmute(x) };
    assert_eq(y, 42.0_f32);

    // f32 <-> int casts
    assert_eq(5.0f32 as u32, 5);
    assert_eq(-5.0f32 as u32, 0);
    assert_eq(5.0f32 as i32, 5);
    assert_eq(-5.0f32 as i32, -5);
    assert_eq(f32::MAX as i32, i32::MAX);
    assert_eq(f32::INFINITY as i32, i32::MAX);
    assert_eq(f32::MAX as u32, u32::MAX);
    assert_eq(f32::INFINITY as u32, u32::MAX);
    assert_eq(f32::MIN as i32, i32::MIN);
    assert_eq(f32::NEG_INFINITY as i32, i32::MIN);
    assert_eq(f32::MIN as u32, 0);
    assert_eq(f32::NEG_INFINITY as u32, 0);
    assert_eq(f32::NAN as i32, 0);
    assert_eq(f32::NAN as u32, 0);
    assert_eq((u32::MAX-127) as f32 as u32, u32::MAX); // rounding loss
    assert_eq((u32::MAX-128) as f32 as u32, u32::MAX-255); // rounding loss
    assert_eq(127i8 as f32, 127.0f32);
    assert_eq(i128::MIN as f32, -170141183460469231731687303715884105728.0f32);
    assert_eq(u128::MAX as f32, f32::INFINITY); // saturation

    // f64 <-> int casts
    assert_eq(5.0f64 as u64, 5);
    assert_eq(-5.0f64 as u64, 0);
    assert_eq(5.0f64 as i64, 5);
    assert_eq(-5.0f64 as i64, -5);
    assert_eq(f64::MAX as i64, i64::MAX);
    assert_eq(f64::INFINITY as i64, i64::MAX);
    assert_eq(f64::MAX as u64, u64::MAX);
    assert_eq(f64::INFINITY as u64, u64::MAX);
    assert_eq(f64::MIN as i64, i64::MIN);
    assert_eq(f64::NEG_INFINITY as i64, i64::MIN);
    assert_eq(f64::MIN as u64, 0);
    assert_eq(f64::NEG_INFINITY as u64, 0);
    assert_eq(f64::NAN as i64, 0);
    assert_eq(f64::NAN as u64, 0);
    assert_eq((u64::MAX-1023) as f64 as u64, u64::MAX); // rounding loss
    assert_eq((u64::MAX-1024) as f64 as u64, u64::MAX-2047); // rounding loss
    assert_eq(u128::MAX as f64 as u128, u128::MAX);
    assert_eq(i16::MIN as f64, -32768.0f64);
    assert_eq(u128::MAX as f64, 340282366920938463463374607431768211455.0f64); // even that fits...

    // f32 <-> f64 casts
    assert_eq(5.0f64 as f32, 5.0f32);
    assert_eq(5.0f32 as f64, 5.0f64);
    assert_eq(f64::MAX as f32, f32::INFINITY);
    assert_eq(f64::MIN as f32, f32::NEG_INFINITY);
    assert_eq(f32::INFINITY as f64, f64::INFINITY);
    assert_eq(f32::NEG_INFINITY as f64, f64::NEG_INFINITY);

    // f32 min/max
    assert_eq((1.0 as f32).max(-1.0), 1.0);
    assert_eq((1.0 as f32).min(-1.0), -1.0);
    assert_eq(f32::NAN.min(9.0), 9.0);
    assert_eq(f32::NAN.max(-9.0), -9.0);
    assert_eq((9.0 as f32).min(f32::NAN), 9.0);
    assert_eq((-9.0 as f32).max(f32::NAN), -9.0);

    // f64 min/max
    assert_eq((1.0 as f64).max(-1.0), 1.0);
    assert_eq((1.0 as f64).min(-1.0), -1.0);
    assert_eq(f64::NAN.min(9.0), 9.0);
    assert_eq(f64::NAN.max(-9.0), -9.0);
    assert_eq((9.0 as f64).min(f64::NAN), 9.0);
    assert_eq((-9.0 as f64).max(f64::NAN), -9.0);

    // f32 copysign
    assert_eq(3.5_f32.copysign(0.42), 3.5_f32);
    assert_eq(3.5_f32.copysign(-0.42), -3.5_f32);
    assert_eq((-3.5_f32).copysign(0.42), 3.5_f32);
    assert_eq((-3.5_f32).copysign(-0.42), -3.5_f32);
    assert!(f32::NAN.copysign(1.0).is_nan());

    // f64 copysign
    assert_eq(3.5_f64.copysign(0.42), 3.5_f64);
    assert_eq(3.5_f64.copysign(-0.42), -3.5_f64);
    assert_eq((-3.5_f64).copysign(0.42), 3.5_f64);
    assert_eq((-3.5_f64).copysign(-0.42), -3.5_f64);
    assert!(f64::NAN.copysign(1.0).is_nan());
}
<|endoftext|>


<commit_before>use raw::*;
use function::UncompiledFunction;
use function::ABI::CDECL;
use types::get;
use libc::c_long;
use value::Value;
use types::Type;
use util::{from_ptr, NativeRef};
/// A type that can be compiled into a LibJIT representation
pub trait Compile for Sized? {
    /// Get a JIT representation of this value
    fn compile<'a>(&self, func:&UncompiledFunction<'a>) -> Value<'a>;
    /// Get the JIT type repr of the value
    fn jit_type(_:Option<Self>) -> Type;
}
impl Compile for () {
    #[inline(always)]
    fn compile<'a>(&self, func:&UncompiledFunction<'a>) -> Value<'a> {
        let ty = get::<()>();
        Value::new(func, ty)
    }
    #[inline(always)]
    fn jit_type(_:Option<()>) -> Type {
        unsafe {
            from_ptr(jit_type_void)
        }
    }
}
compile_prims!{
    (f64, f64) => (jit_type_float64, jit_value_create_float64_constant),
    (f32, f32) => (jit_type_float32, jit_value_create_float32_constant),
    (int, c_long) => (jit_type_nint, jit_value_create_nint_constant),
    (uint, c_long) => (jit_type_nuint, jit_value_create_nint_constant),
    (i64, c_long) => (jit_type_long, jit_value_create_long_constant),
    (u64, c_long) => (jit_type_ulong, jit_value_create_long_constant),
    (i32, c_long) => (jit_type_int, jit_value_create_nint_constant),
    (u32, c_long) => (jit_type_uint, jit_value_create_nint_constant),
    (i16, c_long) => (jit_type_short, jit_value_create_nint_constant),
    (u16, c_long) => (jit_type_ushort, jit_value_create_nint_constant),
    (i8, c_long) => (jit_type_sbyte, jit_value_create_nint_constant),
    (u8, c_long) => (jit_type_ubyte, jit_value_create_nint_constant),
    (bool, c_long) => (jit_type_sys_bool, jit_value_create_nint_constant),
    (char, c_long) => (jit_type_sys_char, jit_value_create_nint_constant)
}
impl<T:Compile> Compile for *mut T {
    fn compile<'a>(&self, func:&UncompiledFunction<'a>) -> Value<'a> {
        unsafe {
            from_ptr(jit_value_create_nint_constant(
                func.as_ptr(),
                get::<*mut T>().as_ptr(),
                self.to_uint() as c_long
            ))
        }
    }
    #[inline(always)]
    fn jit_type(_:Option<*mut T>) -> Type {
        Type::create_pointer(get::<T>())
    }
}
impl<T:Compile> Compile for *const T {
    fn compile<'a>(&self, func:&UncompiledFunction<'a>) -> Value<'a> {
        unsafe {
            from_ptr(jit_value_create_nint_constant(
                func.as_ptr(),
                get::<*const T>().as_ptr(),
                self.to_uint() as c_long
            ))
        }
    }
    #[inline(always)]
    fn jit_type(_:Option<*const T>) -> Type {
        Type::create_pointer(get::<T>())
    }
}
impl<T:Compile> Compile for &'static T {
    #[inline(always)]
    fn compile<'a>(&self, func:&UncompiledFunction<'a>) -> Value<'a> {
        unsafe {
            from_ptr(jit_value_create_nint_constant(
                func.as_ptr(),
                get::<&'static T>().as_ptr(),
                (*self as *const T).to_uint() as c_long
            ))
        }
    }
    #[inline(always)]
    fn jit_type(_:Option<&'static T>) -> Type {
        Type::create_pointer(get::<T>())
    }
}
compile_tuple!(A, B => a, b);
compile_tuple!(A, B, C => a, b, c);
compile_tuple!(A, B, C, D => a, b, c, d);
compile_tuple!(A, B, C, D, E => a, b, c, d, e);
compile_func!(fn() -> R, fn() -> R, extern fn() -> R);
compile_func!(fn(A) -> R, fn(A) -> R, extern fn(A) -> R);
compile_func!(fn(A, B) -> R, fn(A, B) -> R, extern fn(A, B) -> R);
compile_func!(fn(A, B, C) -> R, fn(A, B, C) -> R, extern fn(A, B, C) -> R);
compile_func!(fn(A, B, C, D) -> R, fn(A, B, C, D) -> R, extern fn(A, B, C, D) -> R);<commit_msg>Suppress warnings about deprecated to_uint on pointers<commit_after>use raw::*;
use function::UncompiledFunction;
use function::ABI::CDECL;
use types::get;
use libc::c_long;
use value::Value;
use types::Type;
use util::{from_ptr, NativeRef};
/// A type that can be compiled into a LibJIT representation
pub trait Compile for Sized? {
    /// Get a JIT representation of this value
    fn compile<'a>(&self, func:&UncompiledFunction<'a>) -> Value<'a>;
    /// Get the JIT type repr of the value
    fn jit_type(_:Option<Self>) -> Type;
}
impl Compile for () {
    #[inline(always)]
    fn compile<'a>(&self, func:&UncompiledFunction<'a>) -> Value<'a> {
        let ty = get::<()>();
        Value::new(func, ty)
    }
    #[inline(always)]
    fn jit_type(_:Option<()>) -> Type {
        unsafe {
            from_ptr(jit_type_void)
        }
    }
}
compile_prims!{
    (f64, f64) => (jit_type_float64, jit_value_create_float64_constant),
    (f32, f32) => (jit_type_float32, jit_value_create_float32_constant),
    (int, c_long) => (jit_type_nint, jit_value_create_nint_constant),
    (uint, c_long) => (jit_type_nuint, jit_value_create_nint_constant),
    (i64, c_long) => (jit_type_long, jit_value_create_long_constant),
    (u64, c_long) => (jit_type_ulong, jit_value_create_long_constant),
    (i32, c_long) => (jit_type_int, jit_value_create_nint_constant),
    (u32, c_long) => (jit_type_uint, jit_value_create_nint_constant),
    (i16, c_long) => (jit_type_short, jit_value_create_nint_constant),
    (u16, c_long) => (jit_type_ushort, jit_value_create_nint_constant),
    (i8, c_long) => (jit_type_sbyte, jit_value_create_nint_constant),
    (u8, c_long) => (jit_type_ubyte, jit_value_create_nint_constant),
    (bool, c_long) => (jit_type_sys_bool, jit_value_create_nint_constant),
    (char, c_long) => (jit_type_sys_char, jit_value_create_nint_constant)
}
impl<T:Compile> Compile for *mut T {
    fn compile<'a>(&self, func:&UncompiledFunction<'a>) -> Value<'a> {
        unsafe {
            from_ptr(jit_value_create_nint_constant(
                func.as_ptr(),
                get::<*mut T>().as_ptr(),
                *self as c_long
            ))
        }
    }
    #[inline(always)]
    fn jit_type(_:Option<*mut T>) -> Type {
        Type::create_pointer(get::<T>())
    }
}
impl<T:Compile> Compile for *const T {
    fn compile<'a>(&self, func:&UncompiledFunction<'a>) -> Value<'a> {
        unsafe {
            from_ptr(jit_value_create_nint_constant(
                func.as_ptr(),
                get::<*const T>().as_ptr(),
                *self as c_long
            ))
        }
    }
    #[inline(always)]
    fn jit_type(_:Option<*const T>) -> Type {
        Type::create_pointer(get::<T>())
    }
}
impl<T:Compile> Compile for &'static T {
    #[inline(always)]
    fn compile<'a>(&self, func:&UncompiledFunction<'a>) -> Value<'a> {
        unsafe {
            from_ptr(jit_value_create_nint_constant(
                func.as_ptr(),
                get::<&'static T>().as_ptr(),
                *self as *const T as c_long
            ))
        }
    }
    #[inline(always)]
    fn jit_type(_:Option<&'static T>) -> Type {
        Type::create_pointer(get::<T>())
    }
}
compile_tuple!(A, B => a, b);
compile_tuple!(A, B, C => a, b, c);
compile_tuple!(A, B, C, D => a, b, c, d);
compile_tuple!(A, B, C, D, E => a, b, c, d, e);
compile_func!(fn() -> R, fn() -> R, extern fn() -> R);
compile_func!(fn(A) -> R, fn(A) -> R, extern fn(A) -> R);
compile_func!(fn(A, B) -> R, fn(A, B) -> R, extern fn(A, B) -> R);
compile_func!(fn(A, B, C) -> R, fn(A, B, C) -> R, extern fn(A, B, C) -> R);
compile_func!(fn(A, B, C, D) -> R, fn(A, B, C, D) -> R, extern fn(A, B, C, D) -> R);<|endoftext|>


<commit_before>use std::io::{File,fs};
use std::path::posix::Path;
use std::os::{homedir};
use std::rand::random;

pub fn new(name: &str) {
    let home_unwrap = homedir().unwrap();
    let path = format!("{}/.muxed/{}", home_unwrap.display(), name);
    File::create(&Path::new(path));
}

fn create_muxed_dir(name: &String) -> Path {
    let home_unwrap = homedir().unwrap();
    let path = &Path::new(format!("{}/.{}", home_unwrap.display(), name));
    fs::mkdir(path, ::std::io::UserRWX);
    path.clone()
}

fn muxed_dir_exists(name: &String) -> bool {
    let home_unwrap = homedir().unwrap();
    let path = &Path::new(format!("{}/.{}", home_unwrap.display(), name));
    path.exists()
}

#[test]
fn muxed_dir_exists_returns_false() {
  let dir = format!("test_dir_{}", random::<f64>());
  assert!(!muxed_dir_exists(&dir));
}

#[test]
fn muxed_dir_exists_returns_true() {
  let dir = format!("test_dir_{}", random::<f64>());
  create_muxed_dir(&dir);
  assert!(muxed_dir_exists(&dir));

  let home_unwrap = homedir().unwrap();
  let muxed_path  = &Path::new(format!("{}/.{}/", home_unwrap.display(), dir.as_slice()));
  fs::rmdir_recursive(muxed_path);
}

#[test]
fn creates_muxed_dir() {
    let name        = format!("test_project_{}", random::<f64>());
    let dir         = format!("test_dir_{}", random::<f64>());
    let home_unwrap = homedir().unwrap();
    let muxed_path  = &Path::new(format!("{}/.{}/", home_unwrap.display(), dir.as_slice()));
    create_muxed_dir(&dir);
    assert!(muxed_path.exists());
    fs::rmdir_recursive(muxed_path);
}

#[test]
fn new_writes_file_to_muxed_dir() {
    let name = format!("test_project_{}", random::<f64>());
    let home_unwrap = homedir().unwrap();
    let path = &Path::new(format!("{}/.muxed/{}", home_unwrap.display(), name));
    new(name.as_slice());
    assert!(path.exists());
    fs::unlink(path);
}
<commit_msg>Create the mixed dir if it doesn't exist.<commit_after>use std::io::{File,fs};
use std::path::posix::Path;
use std::os::{homedir};
use std::rand::random;

pub fn new(name: &str) {
    let home_unwrap = homedir().unwrap();
    let home        = home_unwrap.display();

    let muxed_dir = if muxed_dir_exists(&"muxed".to_string()) {
      Path::new(format!("{}/.muxed/", home))
    } else {
      create_muxed_dir(&"muxed".to_string())
    };

    let path = format!("{}/{}", muxed_dir.display(), name);

    File::create(&Path::new(path));
}

fn create_muxed_dir(name: &String) -> Path {
    let home_unwrap = homedir().unwrap();
    let path = &Path::new(format!("{}/.{}", home_unwrap.display(), name));
    fs::mkdir(path, ::std::io::UserRWX);
    path.clone()
}

fn muxed_dir_exists(name: &String) -> bool {
    let home_unwrap = homedir().unwrap();
    let path = &Path::new(format!("{}/.{}", home_unwrap.display(), name));
    path.exists()
}

#[test]
fn muxed_dir_exists_returns_false() {
  let dir = format!("test_dir_{}", random::<f64>());
  assert!(!muxed_dir_exists(&dir));
}

#[test]
fn muxed_dir_exists_returns_true() {
  let dir = format!("test_dir_{}", random::<f64>());
  create_muxed_dir(&dir);
  assert!(muxed_dir_exists(&dir));

  let home_unwrap = homedir().unwrap();
  let muxed_path  = &Path::new(format!("{}/.{}/", home_unwrap.display(), dir.as_slice()));
  fs::rmdir_recursive(muxed_path);
}

#[test]
fn creates_muxed_dir() {
    let name        = format!("test_project_{}", random::<f64>());
    let dir         = format!("test_dir_{}", random::<f64>());
    let home_unwrap = homedir().unwrap();
    let muxed_path  = &Path::new(format!("{}/.{}/", home_unwrap.display(), dir.as_slice()));
    create_muxed_dir(&dir);
    assert!(muxed_path.exists());
    fs::rmdir_recursive(muxed_path);
}

#[test]
fn new_writes_file_to_muxed_dir() {
    let name = format!("test_project_{}", random::<f64>());
    let home_unwrap = homedir().unwrap();
    let path = &Path::new(format!("{}/.muxed/{}", home_unwrap.display(), name));
    new(name.as_slice());
    assert!(path.exists());
    fs::unlink(path);
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::path::PathBuf;
use std::env;

use tempdir::TempDir;

use super::stream::{Stream, StreamError};
use super::parser::{parse_statement, Statement, ParseError, ColumnSpec};
use super::row::builder::RowBuilder;
use super::parser::Expression;

use super::storage::disk::Disk;
use super::storage::memory::Memory;

#[derive(Debug, PartialEq)]
pub enum DatabaseError {
    TableExists,
    QueryParseError,
    UnknownError,
    StreamNotFound,
    FieldNotFound(String),
}

type DatabaseResult<T> = Result<T, DatabaseError>;

#[derive(Debug)]
pub enum QueryResult {
    ResultSet(ResultSet),
    Insert(u64),
    StreamCreated,
}

impl From<ParseError> for DatabaseError {
    fn from(err: ParseError) -> DatabaseError {
        DatabaseError::QueryParseError
    }
}
impl From<StreamError> for DatabaseError {
    fn from(err: StreamError) -> DatabaseError {
        let tmp = match err {
            StreamError::FieldNotFound(x) =>
                DatabaseError::FieldNotFound(x),
            _ =>
                DatabaseError::UnknownError

        };
        tmp
    }
}

#[derive(Debug)]
struct ResultSet {
    statement: Statement,
    num_results: u64,
}
impl ResultSet {
    fn new(statement: Statement) -> ResultSet {
        ResultSet{num_results:0, statement: statement}
    }
}

pub struct Database {
    path: PathBuf,
    tables: HashMap<String, Stream>
}

impl Database {
    pub fn new(path: PathBuf) -> Database {
        // create the directory if it doesn't exist?
        Database{
            tables: HashMap::new(),
            path: path,
        }
    }
    pub fn new_temp() -> Database {
        let tmpdir = TempDir::new("totalrecalldb").expect("Creating temp dir failed");
        info!("Created temporary DB at {:?}", tmpdir.path());
        Database::new(tmpdir.into_path())
    }

    pub fn create_stream(&mut self, name: &str) -> Result<&mut Stream, DatabaseError> {
        let mut p = self.path.clone();
        p.push(name);
        let storage = Disk::new(50, p).expect("Could not create disk storage");
        let tmp = Stream::new(storage);

        if self.tables.contains_key(name) {
            return Err(DatabaseError::TableExists);
        }
        self.tables.insert(name.to_string(), tmp);
        let stream = self.tables.get_mut(name).unwrap();
        Ok(stream)
    }
    pub fn create_temp_stream(&mut self) -> DatabaseResult<Stream> {
        let storage = Memory::new().expect("Memory is failing uggghhh");
        let mut stream = Stream::new(storage);
        Ok(stream)
    }

    fn get_stream(&self, name: &str) -> Option<&Stream> {
        self.tables.get(name)
    }

    fn get_stream_mut(&mut self, name: &str) -> Option<&mut Stream> {
        self.tables.get_mut(name)
    }

    pub fn execute(&mut self, query: &str) -> Result<QueryResult, DatabaseError> {
        let parsed = parse_statement(query)?;
        let p2 = parsed.clone();
        let result = match parsed {
            Statement::Insert(stream, row_builder) =>
                self.insert(&stream, row_builder),
            Statement::DeclareStream(stream, fields) =>
                self.declare_stream(&stream, fields),
            Statement::Select(table, predicates) => {
                // going to return the resultset now
                // the expectation is that all the validation be done up front
                let tmp = ResultSet::new(p2);
                Ok(QueryResult::ResultSet(tmp))
            }
            _ => Err(DatabaseError::UnknownError)
        };
        result
    }

    pub fn select(&self, stream: &str, predicates: Option<Box<Expression>>) ->
                    Result<QueryResult, DatabaseError> {
        let result = self.get_stream(stream)
                         .ok_or(DatabaseError::StreamNotFound)?
                         .select(predicates)?;

        Err(DatabaseError::UnknownError)
    }

    pub fn insert(&mut self, stream: &str, row_builder: RowBuilder) -> Result<QueryResult, DatabaseError> {
        let stream = self.get_stream_mut(stream).ok_or(DatabaseError::StreamNotFound)?;
        let id = stream.insert(row_builder)?;
        Ok(QueryResult::Insert(id))
    }

    pub fn declare_stream(&mut self,
                          stream: &str,
                          fields: Vec<ColumnSpec>) -> Result<QueryResult, DatabaseError> {
        let stream = self.create_stream(stream)?;
        for col_spec in fields {
            stream.schema.add_type(&col_spec.name, col_spec.ftype);
        }
        Ok(QueryResult::StreamCreated)
    }

}

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

    // returns a valid DB for use with testing with valid simple schema
    fn get_db_with_stream() -> Database {
        let mut db = Database::new_temp();
        db.create_stream("Jon");

        db
    }

    #[test]
    fn create_table() {
        let mut db = Database::new_temp();
        db.create_stream("Jon");
    }

    #[test]
    fn create_table_fails_when_table_exists() {
        let mut db = get_db_with_stream();
        if let Err(result) = db.create_stream("Jon") {
            assert_eq!(result, DatabaseError::TableExists);
        } else {
            panic!("Was expecting DatabaseError::TableExists, got an OK");
        }

    }
}
<commit_msg>using the more ergonomic DatabaseResult<commit_after>use std::collections::HashMap;
use std::path::PathBuf;
use std::env;

use tempdir::TempDir;

use super::stream::{Stream, StreamError};
use super::parser::{parse_statement, Statement, ParseError, ColumnSpec};
use super::row::builder::RowBuilder;
use super::parser::Expression;

use super::storage::disk::Disk;
use super::storage::memory::Memory;

#[derive(Debug, PartialEq)]
pub enum DatabaseError {
    TableExists,
    QueryParseError,
    UnknownError,
    StreamNotFound,
    FieldNotFound(String),
}

type DatabaseResult<T> = Result<T, DatabaseError>;

#[derive(Debug)]
pub enum QueryResult {
    ResultSet(ResultSet),
    Insert(u64),
    StreamCreated,
}

impl From<ParseError> for DatabaseError {
    fn from(err: ParseError) -> DatabaseError {
        DatabaseError::QueryParseError
    }
}
impl From<StreamError> for DatabaseError {
    fn from(err: StreamError) -> DatabaseError {
        let tmp = match err {
            StreamError::FieldNotFound(x) =>
                DatabaseError::FieldNotFound(x),
            _ =>
                DatabaseError::UnknownError

        };
        tmp
    }
}

#[derive(Debug)]
struct ResultSet {
    statement: Statement,
    num_results: u64,
}
impl ResultSet {
    fn new(statement: Statement) -> ResultSet {
        ResultSet{num_results:0, statement: statement}
    }
}

pub struct Database {
    path: PathBuf,
    tables: HashMap<String, Stream>
}

impl Database {
    pub fn new(path: PathBuf) -> Database {
        // create the directory if it doesn't exist?
        Database{
            tables: HashMap::new(),
            path: path,
        }
    }
    pub fn new_temp() -> Database {
        let tmpdir = TempDir::new("totalrecalldb").expect("Creating temp dir failed");
        info!("Created temporary DB at {:?}", tmpdir.path());
        Database::new(tmpdir.into_path())
    }

    pub fn create_stream(&mut self, name: &str) -> DatabaseResult<&mut Stream> {
        let mut p = self.path.clone();
        p.push(name);
        let storage = Disk::new(50, p).expect("Could not create disk storage");
        let tmp = Stream::new(storage);

        if self.tables.contains_key(name) {
            return Err(DatabaseError::TableExists);
        }
        self.tables.insert(name.to_string(), tmp);
        let stream = self.tables.get_mut(name).unwrap();
        Ok(stream)
    }
    pub fn create_temp_stream(&mut self) -> DatabaseResult<Stream> {
        let storage = Memory::new().expect("Memory is failing uggghhh");
        let mut stream = Stream::new(storage);
        Ok(stream)
    }

    fn get_stream(&self, name: &str) -> Option<&Stream> {
        self.tables.get(name)
    }

    fn get_stream_mut(&mut self, name: &str) -> Option<&mut Stream> {
        self.tables.get_mut(name)
    }

    pub fn execute(&mut self, query: &str) -> Result<QueryResult, DatabaseError> {
        let parsed = parse_statement(query)?;
        let p2 = parsed.clone();
        let result = match parsed {
            Statement::Insert(stream, row_builder) =>
                self.insert(&stream, row_builder),
            Statement::DeclareStream(stream, fields) =>
                self.declare_stream(&stream, fields),
            Statement::Select(table, predicates) => {
                // going to return the resultset now
                // the expectation is that all the validation be done up front
                let tmp = ResultSet::new(p2);
                Ok(QueryResult::ResultSet(tmp))
            }
            _ => Err(DatabaseError::UnknownError)
        };
        result
    }

    pub fn select(&self, stream: &str, predicates: Option<Box<Expression>>) ->
                    Result<QueryResult, DatabaseError> {
        let result = self.get_stream(stream)
                         .ok_or(DatabaseError::StreamNotFound)?
                         .select(predicates)?;

        Err(DatabaseError::UnknownError)
    }

    pub fn insert(&mut self, stream: &str, row_builder: RowBuilder) -> DatabaseResult<QueryResult> {
        let stream = self.get_stream_mut(stream).ok_or(DatabaseError::StreamNotFound)?;
        let id = stream.insert(row_builder)?;
        Ok(QueryResult::Insert(id))
    }

    pub fn declare_stream(&mut self,
                          stream: &str,
                          fields: Vec<ColumnSpec>) -> DatabaseResult<QueryResult> {
        let stream = self.create_stream(stream)?;
        for col_spec in fields {
            stream.schema.add_type(&col_spec.name, col_spec.ftype);
        }
        Ok(QueryResult::StreamCreated)
    }

}

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

    // returns a valid DB for use with testing with valid simple schema
    fn get_db_with_stream() -> Database {
        let mut db = Database::new_temp();
        db.create_stream("Jon");

        db
    }

    #[test]
    fn create_table() {
        let mut db = Database::new_temp();
        db.create_stream("Jon");
    }

    #[test]
    fn create_table_fails_when_table_exists() {
        let mut db = get_db_with_stream();
        if let Err(result) = db.create_stream("Jon") {
            assert_eq!(result, DatabaseError::TableExists);
        } else {
            panic!("Was expecting DatabaseError::TableExists, got an OK");
        }

    }
}
<|endoftext|>


<commit_before>
extern crate ndarray;
extern crate rand;
extern crate ndarray_rand;

use ndarray::{Axis, ArrayView,ArrayViewMut, Ix};
use rayon;
use rayon::prelude::*;
use simple_parallel;

// various array views for divide-and-conquering
pub type VectorView<'a,A> = ArrayView<'a,A, Ix>;
pub type MatView<'a,A> = ArrayView<'a,A,(Ix,Ix)>;
pub type MatViewMut<'a,A> = ArrayViewMut<'a,A,(Ix,Ix)>;

// basic vector dot product
pub fn vector_dot(left : VectorView<f64>,right:  VectorView<f64>) -> f64 {
    unsafe{
        (0..right.len()).fold(0.0, |x, y| x + *right.uget(y) * *left.uget(y) )
    }

}

// basic matrix multiplication
pub fn matrix_dot( left : &MatView<f64>, right: &MatView<f64>, init : &mut MatViewMut<f64>){
    let (m,k1)= left.dim();
    let (k2,n) = right.dim();
    assert_eq!(k1, k2);
    for ix in 0..m{
        for jx in 0..n{
            let left_row = left.row(ix);
            let right_col = right.column(jx);
            unsafe{
                let mut value = init.uget_mut((ix,jx));
                *value += vector_dot(left_row, right_col);
            }
        }
    }
}

pub const BLOCKSIZE : usize = 50;

// parallelized matrix multiplication via rayon.
pub fn matrix_dot_rayon(left: &MatView<f64>, right : &MatView<f64>,  init : &mut MatViewMut<f64>){

    let (m, k1) = left.dim();
    let (k2, n) = right.dim();
    assert_eq!(k1, k2);

    if m <= BLOCKSIZE && n <= BLOCKSIZE {
        matrix_dot(left,right,init);
        return;
    } else{
        if m > BLOCKSIZE {
           let mid = m / 2;
           let (left_0, left_1) = left.split_at(Axis(0), mid);
           let (mut init_left, mut init_right) = init.view_mut().split_at(Axis(0), mid);
           rayon::join(|| matrix_dot_rayon(&left_0, right, &mut init_left),
                       || matrix_dot_rayon(&left_1, right, &mut init_right));

       } else if n > BLOCKSIZE {
           let mid = n / 2;
           let (right_0, right_1) = right.split_at(Axis(1), mid);
           let (mut init_left, mut init_right) = init.view_mut().split_at(Axis(1), mid);
           rayon::join(|| matrix_dot_rayon(left,&right_0, &mut init_left),
                       || matrix_dot_rayon(left, &right_1, &mut init_right));
        }
    }
}

// parallelized matrix multiplication via simple_parallel.
pub fn matrix_dot_simple_parallel(left: &MatView<f64>, right : &MatView<f64>,  init : &mut MatViewMut<f64>){

    let (m, k1) = left.dim();
    let (k2, n) = right.dim();

    debug_assert_eq!(k1, k2);
    if m <= BLOCKSIZE && n <= BLOCKSIZE {
        matrix_dot(left,right,init);
        return;
    } else{
        if m > BLOCKSIZE {
           let mid = m / 2;
           let (left_0, left_1) = left.split_at(Axis(0), mid);
           let (mut init_left, mut init_right) = init.view_mut().split_at(Axis(0), mid);
           simple_parallel::both((&left_0, right, &mut init_left),
                                (&left_1, right, &mut init_right),
                                |(x,y,z)| matrix_dot_simple_parallel(x,y,z));

       } else if n > BLOCKSIZE {
           let mid = n / 2;
           let (right_0, right_1) = right.split_at(Axis(1), mid);
           let (mut init_left, mut init_right) = init.view_mut().split_at(Axis(1), mid);
           simple_parallel::both((left, &right_0, &mut init_left),
                                (left, &right_1, &mut init_right),
                                |(x,y,z)| matrix_dot_simple_parallel(x,y,z));

        }
    }
}
<commit_msg>Update dot.rs<commit_after>
extern crate ndarray;
extern crate rand;
extern crate ndarray_rand;

use ndarray::{Axis, ArrayView,ArrayViewMut, Ix};
use rayon;
use rayon::prelude::*;
use simple_parallel;

// various array views for divide-and-conquering
pub type VectorView<'a,A> = ArrayView<'a,A, Ix>;
pub type MatView<'a,A> = ArrayView<'a,A,(Ix,Ix)>;
pub type MatViewMut<'a,A> = ArrayViewMut<'a,A,(Ix,Ix)>;

// basic vector dot product
pub fn vector_dot(left : VectorView<f64>,right:  VectorView<f64>) -> f64 {
    unsafe{
        (0..right.len()).fold(0.0, |x, y| x + *right.uget(y) * *left.uget(y) )
    }

}

// basic matrix multiplication
pub fn matrix_dot( left : &MatView<f64>, right: &MatView<f64>, init : &mut MatViewMut<f64>){
    let (m,k1)= left.dim();
    let (k2,n) = right.dim();
    assert_eq!(k1, k2);
    for ix in 0..m{
        for jx in 0..n{
            let left_row = left.row(ix);
            let right_col = right.column(jx);
            unsafe{
                let mut value = init.uget_mut((ix,jx));
                *value += vector_dot(left_row, right_col);
            }
        }
    }
}

pub const BLOCKSIZE : usize = 50;

// parallelized matrix multiplication via rayon.
pub fn matrix_dot_rayon(left: &MatView<f64>, right : &MatView<f64>,  init : &mut MatViewMut<f64>){

    let (m, k1) = left.dim();
    let (k2, n) = right.dim();
    assert_eq!(k1, k2);

    if m <= BLOCKSIZE && n <= BLOCKSIZE {
        matrix_dot(left,right,init);
        return;
    } else{
        if m > BLOCKSIZE {
           let mid = m / 2;
           let (left_0, left_1) = left.split_at(Axis(0), mid);
           let (mut init_left, mut init_right) = init.view_mut().split_at(Axis(0), mid);
           rayon::join(|| matrix_dot_rayon(&left_0, right, &mut init_left),
                       || matrix_dot_rayon(&left_1, right, &mut init_right));

       } else if n > BLOCKSIZE {
           let mid = n / 2;
           let (right_0, right_1) = right.split_at(Axis(1), mid);
           let (mut init_left, mut init_right) = init.view_mut().split_at(Axis(1), mid);
           rayon::join(|| matrix_dot_rayon(left,&right_0, &mut init_left),
                       || matrix_dot_rayon(left, &right_1, &mut init_right));
        }
    }
}

// parallelized matrix multiplication via simple_parallel.
pub fn matrix_dot_simple_parallel(left: &MatView<f64>, right : &MatView<f64>,  init : &mut MatViewMut<f64>){

    let (m, k1) = left.dim();
    let (k2, n) = right.dim();

    assert_eq!(k1, k2);
    if m <= BLOCKSIZE && n <= BLOCKSIZE {
        matrix_dot(left,right,init);
        return;
    } else{
        if m > BLOCKSIZE {
           let mid = m / 2;
           let (left_0, left_1) = left.split_at(Axis(0), mid);
           let (mut init_left, mut init_right) = init.view_mut().split_at(Axis(0), mid);
           simple_parallel::both((&left_0, right, &mut init_left),
                                (&left_1, right, &mut init_right),
                                |(x,y,z)| matrix_dot_simple_parallel(x,y,z));

       } else if n > BLOCKSIZE {
           let mid = n / 2;
           let (right_0, right_1) = right.split_at(Axis(1), mid);
           let (mut init_left, mut init_right) = init.view_mut().split_at(Axis(1), mid);
           simple_parallel::both((left, &right_0, &mut init_left),
                                (left, &right_1, &mut init_right),
                                |(x,y,z)| matrix_dot_simple_parallel(x,y,z));

        }
    }
}
<|endoftext|>


<commit_before>#[cfg(not(feature = "fitsio-src"))]
fn bind_cfitsio() {
    use pkg_config::Error;
    use std::io::Write;

    let package_name = "cfitsio";
    match pkg_config::probe_library(package_name) {
        Ok(_) => {}
        Err(Error::Failure { output, .. }) => {
            // Handle the case where the user has not installed cfitsio, and thusly it is not on
            // the PKG_CONFIG_PATH
            let stderr = String::from_utf8(output.stderr).unwrap();
            if stderr.contains::<&str>(
                format!(
                    "{} was not found in the pkg-config search path",
                    package_name
                )
                .as_ref(),
            ) {
                let err_msg = format!(
                    "
Cannot find {} on the pkg-config search path.  Consider installing the library for your
system (e.g. through homebrew, apt-get etc.).  Alternatively if it is installed, then add
the directory that contains `cfitsio.pc` on your PKG_CONFIG_PATH, e.g.:

PKG_CONFIG_PATH=<blah> cargo build
",
                    package_name
                );
                std::io::stderr().write_all(err_msg.as_bytes()).unwrap();
                std::process::exit(output.status.code().unwrap());
            }
        }
        Err(e) => panic!("Unhandled error: {:?}", e),
    };
}

#[cfg(feature = "fitsio-src")]
fn bind_cfitsio() {
    use std::env::var;
    use std::path::PathBuf;

    let cfitsio_project_dir = PathBuf::from("ext/cfitsio");
    if !cfitsio_project_dir.exists() {
        panic!(
            "Expected to find cfitsio source directory {}",
            cfitsio_project_dir.display()
        );
    }
    // Make sure the source directory isn't empty.
    match std::fs::read_dir(&cfitsio_project_dir) {
        Ok(mut d) => {
            if let None = d.next() {
                panic!("cfitsio source directory ext/cfitsio is empty!");
            }
        }
        _ => panic!("Could not read from cfitsio source directory ext/cfitsio !"),
    }

    // Translate rustc optimisation levels to things a C compiler can
    // understand. I don't know if all C compilers agree here, but it should
    // at least work for gcc.
    let opt_level: String = match var("OPT_LEVEL").as_ref().map(|o| o.as_str()) {
        Err(_) => panic!("Something wrong with OPT_LEVEL"),
        // gcc doesn't handle 'z'. Just set it to 's', which also optimises
        // for size.
        Ok("z") => "s",
        Ok(o) => o,
    }
    .to_string();

    // Run the contigure script. I'd use the autotools crate here, but it
    // always outputs two of --{enable,disable}-{shared,static}, none of
    // which is supported by the cfitsio configure script! So, just run the
    // script manually.
    let dst = PathBuf::from(var("OUT_DIR").unwrap());

    std::process::Command::new("make")
        .arg("clean")
        .current_dir(&cfitsio_project_dir)
        .spawn()
        .expect("Couldn't run cfitsio make clean")
        .wait()
        .expect("Failed to wait on child");

    std::process::Command::new("sh")
        .args(&[
            "./configure",
            &format!("--prefix={}", dst.display()),
            // cfitsio should always be built with reentrant support.
            "--enable-reentrant",
            // The user's reference guide states that using SSSE3 and SSE2
            // can make reading or writing FITS images 20-30% faster(!).
            // Enabling SSSE3 and SSE2 could cause portability problems, but
            // it's unlikely that anyone is using such a CPU...
            // https://stackoverflow.com/questions/52858556/most-recent-processor-without-support-of-ssse3-instructions
            "--enable-ssse3",
            "--enable-sse2",
            // Don't link against curl.
            "--disable-curl",
        ])
        .env("CFLAGS", &format!("-Wall -O{} -fPIE", opt_level))
        .current_dir(&cfitsio_project_dir)
        .spawn()
        .expect("Couldn't run cfitsio configure script")
        .wait()
        .expect("Failed to wait on child");

    std::process::Command::new("make")
        .arg("-j4")
        .arg("install")
        .current_dir(&cfitsio_project_dir)
        .spawn()
        .expect("Couldn't run cfitsio makefile")
        .wait()
        .expect("Failed to wait on child");

    println!("cargo:rustc-link-search=native={}/lib", dst.display());
    println!("cargo:rustc-link-lib=static=cfitsio");
}

fn main() {
    bind_cfitsio();
}
<commit_msg>Even more tweaks to the fitsio-src feature.<commit_after>#[cfg(not(feature = "fitsio-src"))]
fn bind_cfitsio() {
    use pkg_config::Error;
    use std::io::Write;

    let package_name = "cfitsio";
    match pkg_config::probe_library(package_name) {
        Ok(_) => {}
        Err(Error::Failure { output, .. }) => {
            // Handle the case where the user has not installed cfitsio, and thusly it is not on
            // the PKG_CONFIG_PATH
            let stderr = String::from_utf8(output.stderr).unwrap();
            if stderr.contains::<&str>(
                format!(
                    "{} was not found in the pkg-config search path",
                    package_name
                )
                .as_ref(),
            ) {
                let err_msg = format!(
                    "
Cannot find {} on the pkg-config search path.  Consider installing the library for your
system (e.g. through homebrew, apt-get etc.).  Alternatively if it is installed, then add
the directory that contains `cfitsio.pc` on your PKG_CONFIG_PATH, e.g.:

PKG_CONFIG_PATH=<blah> cargo build
",
                    package_name
                );
                std::io::stderr().write_all(err_msg.as_bytes()).unwrap();
                std::process::exit(output.status.code().unwrap());
            }
        }
        Err(e) => panic!("Unhandled error: {:?}", e),
    };
}

#[cfg(feature = "fitsio-src")]
fn bind_cfitsio() {
    use std::env::var;
    use std::path::PathBuf;

    let cfitsio_project_dir = PathBuf::from("ext/cfitsio");
    if !cfitsio_project_dir.exists() {
        panic!(
            "Expected to find cfitsio source directory {}",
            cfitsio_project_dir.display()
        );
    }
    // Make sure the source directory isn't empty.
    match std::fs::read_dir(&cfitsio_project_dir) {
        Ok(mut d) => {
            if let None = d.next() {
                panic!("cfitsio source directory ext/cfitsio is empty!");
            }
        }
        _ => panic!("Could not read from cfitsio source directory ext/cfitsio !"),
    }

    // Translate rustc optimisation levels to things a C compiler can
    // understand. I don't know if all C compilers agree here, but it should
    // at least work for gcc.
    let opt_level: String = match var("OPT_LEVEL").as_ref().map(|o| o.as_str()) {
        Err(_) => panic!("Something wrong with OPT_LEVEL"),
        // gcc doesn't handle 'z'. Just set it to 's', which also optimises
        // for size.
        Ok("z") => "s",
        Ok(o) => o,
    }
    .to_string();

    // Run the contigure script. I'd use the autotools crate here, but it
    // always outputs two of --{enable,disable}-{shared,static}, none of
    // which is supported by the cfitsio configure script! So, just run the
    // script manually.
    let dst = PathBuf::from(var("OUT_DIR").unwrap());

    std::process::Command::new("make")
        .arg("clean")
        .current_dir(&cfitsio_project_dir)
        .spawn()
        .expect("Couldn't run cfitsio make clean")
        .wait()
        .expect("Failed to wait on child");

    std::process::Command::new("sh")
        .args(&[
            "./configure",
            &format!("--prefix={}", dst.display()),
            // cfitsio should always be built with reentrant support.
            "--enable-reentrant",
        ])
        .env(
            "CFLAGS",
            &format!("-Wall -O{} -march=native -fPIE", opt_level),
        )
        .current_dir(&cfitsio_project_dir)
        .spawn()
        .expect("Couldn't run cfitsio configure script")
        .wait()
        .expect("Failed to wait on child");

    std::process::Command::new("make")
        .arg("-j")
        .arg("install")
        .current_dir(&cfitsio_project_dir)
        .spawn()
        .expect("Couldn't run cfitsio makefile")
        .wait()
        .expect("Failed to wait on child");

    println!("cargo:rustc-link-search=native={}/lib", dst.display());
    println!("cargo:rustc-link-lib=static=cfitsio");
}

fn main() {
    bind_cfitsio();
}
<|endoftext|>


<commit_before><commit_msg>Remove `LeafNode` to `Node` conversion.<commit_after><|endoftext|>


<commit_before>//! This module implements the ODBC Environment
use super::{Error, DiagRec, Result, raw};
use std::collections::HashMap;
use std;

/// Handle to an ODBC Environment
///
/// Creating an instance of this type is the first thing you do then using ODBC. The environment
/// must outlive all connections created with it
pub struct Environment {
    handle: raw::SQLHENV,
}

/// Holds name and description of a datasource
///
/// Can be obtained via `Environment::data_sources`
#[derive(Clone, Debug)]
pub struct DataSourceInfo {
    /// Name of the data source
    pub server_name: String,
    /// Description of the data source
    pub description: String,
}

/// Struct holding information available on a driver.
///
/// Can be obtained via `Environment::drivers`
#[derive(Clone, Debug)]
pub struct DriverInfo {
    /// Name of the odbc driver
    pub description: String,
    /// List of attributes of the odbc driver
    pub attributes: HashMap<String, String>,
}

/// Signature shared by `raw::SQLDrivers` and `raw::SQLDataSources`
type SqlInfoFunction = unsafe extern "C" fn(raw::SQLHENV,
                                            raw::SQLUSMALLINT,
                                            *mut raw::SQLCHAR,
                                            raw::SQLSMALLINT,
                                            *mut raw::SQLSMALLINT,
                                            *mut raw::SQLCHAR,
                                            raw::SQLSMALLINT,
                                            *mut raw::SQLSMALLINT)
                                            -> raw::SQLRETURN;

impl Environment {
    /// Allocates a new ODBC Environment
    ///
    /// Declares the Application's ODBC Version to be 3
    pub fn new() -> Result<Environment> {
        unsafe {
            let mut env = std::ptr::null_mut();
            let mut result =
                match raw::SQLAllocHandle(raw::SQL_HANDLE_ENV, std::ptr::null_mut(), &mut env) {
                    raw::SQL_SUCCESS => Environment { handle: env },
                    raw::SQL_SUCCESS_WITH_INFO => Environment { handle: env },
                    // Driver Manager failed to allocate environment
                    raw::SQL_ERROR => return Err(Error::EnvAllocFailure),
                    _ => unreachable!(),
                };
            // no leak if we return an error here, env handle is already wrapped and would be
            // dropped
            result.set_attribute(raw::SQL_ATTR_ODBC_VERSION,
                               raw::SQL_OV_ODBC3 as *mut std::os::raw::c_void,
                               0)?;

            Ok(result)
        }
    }

    /// Stores all driver description and attributes in a Vec
    pub fn drivers(&self) -> Result<Vec<DriverInfo>> {
        // Iterate twice, once for reading the maximum required buffer lengths so we can read
        // everything without truncating and a second time for actually storing the values
        let mut desc_length_out: raw::SQLSMALLINT = 0;
        let mut attr_length_out: raw::SQLSMALLINT = 0;
        let mut result;

        // alloc_info iterates ones over every driver to obtain the requiered buffer sizes
        let (max_desc, max_attr, num_drivers) = self.alloc_info(raw::SQLDrivers)?;

        let mut driver_list = Vec::with_capacity(num_drivers);
        loop {
            let mut description_buffer: Vec<_> = (0..(max_desc + 1)).map(|_| 0u8).collect();
            let mut attribute_buffer: Vec<_> = (0..(max_attr + 1)).map(|_| 0u8).collect();
            unsafe {
                result = raw::SQLDrivers(self.handle,
                                         // Its ok to use fetch next here, since we know
                                         // last state has been SQL_NO_DATA
                                         raw::SQL_FETCH_NEXT,
                                         &mut description_buffer[0] as *mut u8,
                                         max_desc + 1,
                                         &mut desc_length_out as *mut raw::SQLSMALLINT,
                                         &mut attribute_buffer[0] as *mut u8,
                                         max_attr + 1,
                                         &mut attr_length_out as *mut raw::SQLSMALLINT);
            }
            match result {
                raw::SQL_SUCCESS |
                raw::SQL_SUCCESS_WITH_INFO => {
                    description_buffer.resize(desc_length_out as usize, 0);
                    driver_list.push(DriverInfo {
                        description: String::from_utf8(description_buffer)
                            .expect("String returned by Driver Manager should be utf8 encoded"),
                        attributes: Self::parse_attributes(attribute_buffer),
                    })
                }
                raw::SQL_ERROR => unsafe {
                    return Err(Error::SqlError(DiagRec::create(raw::SQL_HANDLE_ENV, self.handle)));
                },
                raw::SQL_NO_DATA => break,
                /// The only other value allowed by ODBC here is SQL_INVALID_HANDLE. We protect the
                /// validity of this handle with our invariant. In save code the user should not be
                /// able to reach this code path.
                _ => panic!("Environment invariant violated"),
            }
        }
        Ok(driver_list)
    }

    /// Stores all data source server names and descriptions in a Vec
    pub fn data_sources(&self) -> Result<Vec<DataSourceInfo>> {
        // Iterate twice, once for reading the maximum required buffer lengths so we can read
        // everything without truncating and a second time for actually storing the values
        let mut name_length_out: raw::SQLSMALLINT = 0;
        let mut desc_length_out: raw::SQLSMALLINT = 0;
        let mut result;

        // alloc_info iterates ones over every datasource to obtain the requiered buffer sizes
        let (max_name, max_desc, num_sources) = self.alloc_info(raw::SQLDataSources)?;

        let mut source_list = Vec::with_capacity(num_sources);
        loop {
            let mut name_buffer: Vec<_> = (0..(max_name + 1)).map(|_| 0u8).collect();
            let mut description_buffer: Vec<_> = (0..(max_desc + 1)).map(|_| 0u8).collect();
            unsafe {
                result = raw::SQLDataSources(self.handle,
                                             // Its ok to use fetch next here, since we know
                                             // last state has been SQL_NO_DATA
                                             raw::SQL_FETCH_NEXT,
                                             &mut name_buffer[0] as *mut u8,
                                             max_name + 1,
                                             &mut name_length_out as *mut raw::SQLSMALLINT,
                                             &mut description_buffer[0] as *mut u8,
                                             max_desc + 1,
                                             &mut desc_length_out as *mut raw::SQLSMALLINT);
            }
            match result {
                raw::SQL_SUCCESS |
                raw::SQL_SUCCESS_WITH_INFO => {
                    name_buffer.resize(name_length_out as usize, 0);
                    description_buffer.resize(desc_length_out as usize, 0);
                    source_list.push(DataSourceInfo {
                        server_name: String::from_utf8(name_buffer)
                            .expect("String returned by Driver Manager should be utf8 encoded"),
                        description: String::from_utf8(description_buffer)
                            .expect("String returned by Driver Manager should be utf8 encoded"),
                    })
                }
                raw::SQL_ERROR => unsafe {
                    return Err(Error::SqlError(DiagRec::create(raw::SQL_HANDLE_ENV, self.handle)));
                },
                raw::SQL_NO_DATA => break,
                /// The only other value allowed by ODBC here is SQL_INVALID_HANDLE. We protect the
                /// validity of this handle with our invariant. In save code the user should not be
                /// able to reach this code path.
                _ => panic!("Environment invariant violated"),
            }
        }
        Ok(source_list)
    }

    /// Allows access to the raw ODBC handle
    pub unsafe fn raw(&mut self) -> raw::SQLHENV {
        self.handle
    }

    /// Allows setting attributes to Environment
    pub unsafe fn set_attribute(&mut self,
                                attribute: raw::SQLINTEGER,
                                value: raw::SQLPOINTER,
                                length: raw::SQLINTEGER)
                                -> Result<()> {
        match raw::SQLSetEnvAttr(self.handle, attribute, value, length) {
            raw::SQL_SUCCESS => Ok(()),
            raw::SQL_SUCCESS_WITH_INFO => Ok(()),
            _ => Err(Error::SqlError(DiagRec::create(raw::SQL_HANDLE_ENV, self.handle))),
        }
    }

    fn alloc_info(&self,
                  f: SqlInfoFunction)
                  -> Result<(raw::SQLSMALLINT, raw::SQLSMALLINT, usize)> {
        let string_buf = std::ptr::null_mut();
        let mut buf1_length_out: raw::SQLSMALLINT = 0;
        let mut buf2_length_out: raw::SQLSMALLINT = 0;
        let mut max1 = 0;
        let mut max2 = 0;
        let mut count = 0;
        let mut result = unsafe {
            // Although the rather lengthy function call kind of blows the code, let's do the first
            // one using SQL_FETCH_FIRST, so we list all drivers independent from environment state
            f(self.handle,
              raw::SQL_FETCH_FIRST,
              string_buf,
              0,
              &mut buf1_length_out as *mut raw::SQLSMALLINT,
              string_buf,
              0,
              &mut buf2_length_out as *mut raw::SQLSMALLINT)
        };
        loop {
            match result {
                raw::SQL_SUCCESS |
                raw::SQL_SUCCESS_WITH_INFO => {
                    count += 1;
                    max1 = std::cmp::max(max1, buf1_length_out);
                    max2 = std::cmp::max(max2, buf2_length_out);
                }
                raw::SQL_NO_DATA => break,
                raw::SQL_ERROR => unsafe {
                    return Err(Error::SqlError(DiagRec::create(raw::SQL_HANDLE_ENV, self.handle)));
                },
                /// The only other value allowed by ODBC here is SQL_INVALID_HANDLE. We protect the
                /// validity of this handle with our invariant. In save code the user should not be
                /// able to reach this code path.
                _ => panic!("Environment invariant violated"),
            }
            unsafe {
                result = f(self.handle,
                           raw::SQL_FETCH_NEXT,
                           string_buf,
                           0,
                           &mut buf1_length_out as *mut raw::SQLSMALLINT,
                           string_buf,
                           0,
                           &mut buf2_length_out as *mut raw::SQLSMALLINT);
            }
        }

        Ok((max1, max2, count))
    }

    /// Called by drivers to pares list of attributes
    ///
    /// Key value pairs are seperated by `\0`. Key and value are seperated by `=`
    fn parse_attributes(attribute_buffer: Vec<u8>) -> HashMap<String, String> {
        String::from_utf8(attribute_buffer)
            .expect("String returned by Driver Manager should be utf8 encoded")
            .split('\0')
            .take_while(|kv_str| *kv_str != String::new())
            .map(|kv_str| {
                let mut iter = kv_str.split('=');
                let key = iter.next().unwrap();
                let value = iter.next().unwrap();
                (key.to_string(), value.to_string())
            })
            .collect()
    }
}

impl Drop for Environment {
    fn drop(&mut self) {
        unsafe {
            raw::SQLFreeEnv(self.handle);
        }
    }
}

#[cfg(test)]
mod test {

    use super::*;

    #[test]
    fn parse_attributes() {
        let buffer = "APILevel=2\0ConnectFunctions=YYY\0CPTimeout=60\0DriverODBCVer=03.\
                      50\0FileUsage=0\0SQLLevel=1\0UsageCount=1\0\0"
            .as_bytes()
            .iter()
            .cloned()
            .collect();
        let attributes = Environment::parse_attributes(buffer);
        assert_eq!(attributes["APILevel"], "2");
        assert_eq!(attributes["ConnectFunctions"], "YYY");
        assert_eq!(attributes["CPTimeout"], "60");
        assert_eq!(attributes["DriverODBCVer"], "03.50");
        assert_eq!(attributes["FileUsage"], "0");
        assert_eq!(attributes["SQLLevel"], "1");
        assert_eq!(attributes["UsageCount"], "1");
    }
}<commit_msg>started using CStr for conversion to String<commit_after>//! This module implements the ODBC Environment
use super::{Error, DiagRec, Result, raw};
use std::collections::HashMap;
use std::ffi::CStr;
use std;

/// Handle to an ODBC Environment
///
/// Creating an instance of this type is the first thing you do then using ODBC. The environment
/// must outlive all connections created with it
pub struct Environment {
    handle: raw::SQLHENV,
}

/// Holds name and description of a datasource
///
/// Can be obtained via `Environment::data_sources`
#[derive(Clone, Debug)]
pub struct DataSourceInfo {
    /// Name of the data source
    pub server_name: String,
    /// Description of the data source
    pub description: String,
}

/// Struct holding information available on a driver.
///
/// Can be obtained via `Environment::drivers`
#[derive(Clone, Debug)]
pub struct DriverInfo {
    /// Name of the odbc driver
    pub description: String,
    /// List of attributes of the odbc driver
    pub attributes: HashMap<String, String>,
}

/// Signature shared by `raw::SQLDrivers` and `raw::SQLDataSources`
type SqlInfoFunction = unsafe extern "C" fn(raw::SQLHENV,
                                            raw::SQLUSMALLINT,
                                            *mut raw::SQLCHAR,
                                            raw::SQLSMALLINT,
                                            *mut raw::SQLSMALLINT,
                                            *mut raw::SQLCHAR,
                                            raw::SQLSMALLINT,
                                            *mut raw::SQLSMALLINT)
                                            -> raw::SQLRETURN;

impl Environment {
    /// Allocates a new ODBC Environment
    ///
    /// Declares the Application's ODBC Version to be 3
    pub fn new() -> Result<Environment> {
        unsafe {
            let mut env = std::ptr::null_mut();
            let mut result =
                match raw::SQLAllocHandle(raw::SQL_HANDLE_ENV, std::ptr::null_mut(), &mut env) {
                    raw::SQL_SUCCESS => Environment { handle: env },
                    raw::SQL_SUCCESS_WITH_INFO => Environment { handle: env },
                    // Driver Manager failed to allocate environment
                    raw::SQL_ERROR => return Err(Error::EnvAllocFailure),
                    _ => unreachable!(),
                };
            // no leak if we return an error here, env handle is already wrapped and would be
            // dropped
            result.set_attribute(raw::SQL_ATTR_ODBC_VERSION,
                               raw::SQL_OV_ODBC3 as *mut std::os::raw::c_void,
                               0)?;

            Ok(result)
        }
    }

    /// Stores all driver description and attributes in a Vec
    pub fn drivers(&self) -> Result<Vec<DriverInfo>> {
        // Iterate twice, once for reading the maximum required buffer lengths so we can read
        // everything without truncating and a second time for actually storing the values
        let mut desc_length_out: raw::SQLSMALLINT = 0;
        let mut attr_length_out: raw::SQLSMALLINT = 0;
        let mut result;

        // alloc_info iterates ones over every driver to obtain the requiered buffer sizes
        let (max_desc, max_attr, num_drivers) = self.alloc_info(raw::SQLDrivers)?;

        let mut driver_list = Vec::with_capacity(num_drivers);
        loop {
            let mut description_buffer: Vec<_> = (0..(max_desc + 1)).map(|_| 0u8).collect();
            let mut attribute_buffer: Vec<_> = (0..(max_attr + 1)).map(|_| 0u8).collect();
            unsafe {
                result = raw::SQLDrivers(self.handle,
                                         // Its ok to use fetch next here, since we know
                                         // last state has been SQL_NO_DATA
                                         raw::SQL_FETCH_NEXT,
                                         &mut description_buffer[0] as *mut u8,
                                         max_desc + 1,
                                         &mut desc_length_out as *mut raw::SQLSMALLINT,
                                         &mut attribute_buffer[0] as *mut u8,
                                         max_attr + 1,
                                         &mut attr_length_out as *mut raw::SQLSMALLINT);
            }
            match result {
                raw::SQL_SUCCESS |
                raw::SQL_SUCCESS_WITH_INFO => {
                    description_buffer.resize(desc_length_out as usize, 0);
                    driver_list.push(DriverInfo {
                        description: String::from_utf8(description_buffer)
                            .expect("String returned by Driver Manager should be utf8 encoded"),
                        attributes: Self::parse_attributes(attribute_buffer),
                    })
                }
                raw::SQL_ERROR => unsafe {
                    return Err(Error::SqlError(DiagRec::create(raw::SQL_HANDLE_ENV, self.handle)));
                },
                raw::SQL_NO_DATA => break,
                /// The only other value allowed by ODBC here is SQL_INVALID_HANDLE. We protect the
                /// validity of this handle with our invariant. In save code the user should not be
                /// able to reach this code path.
                _ => panic!("Environment invariant violated"),
            }
        }
        Ok(driver_list)
    }

    /// Stores all data source server names and descriptions in a Vec
    pub fn data_sources(&self) -> Result<Vec<DataSourceInfo>> {
        // Iterate twice, once for reading the maximum required buffer lengths so we can read
        // everything without truncating and a second time for actually storing the values
        let mut name_length_out: raw::SQLSMALLINT = 0;
        let mut desc_length_out: raw::SQLSMALLINT = 0;
        let mut result;

        // alloc_info iterates ones over every datasource to obtain the requiered buffer sizes
        let (max_name, max_desc, num_sources) = self.alloc_info(raw::SQLDataSources)?;

        let mut source_list = Vec::with_capacity(num_sources);
        let mut name_buffer: Vec<_> = (0..(max_name + 1)).map(|_| 0u8).collect();
        let mut description_buffer: Vec<_> = (0..(max_desc + 1)).map(|_| 0u8).collect();

        loop {
            unsafe {
                result = raw::SQLDataSources(self.handle,
                                             // Its ok to use fetch next here, since we know
                                             // last state has been SQL_NO_DATA
                                             raw::SQL_FETCH_NEXT,
                                             &mut name_buffer[0] as *mut u8,
                                             max_name + 1,
                                             &mut name_length_out as *mut raw::SQLSMALLINT,
                                             &mut description_buffer[0] as *mut u8,
                                             max_desc + 1,
                                             &mut desc_length_out as *mut raw::SQLSMALLINT);
            }
            match result {
                raw::SQL_SUCCESS |
                raw::SQL_SUCCESS_WITH_INFO => {
                    let name = CStr::from_bytes_with_nul(name_buffer.as_slice())
                        .unwrap()
                        .to_str()
                        .unwrap()
                        .to_owned();
                    let description = CStr::from_bytes_with_nul(description_buffer.as_slice())
                        .unwrap()
                        .to_str()
                        .unwrap()
                        .to_owned();
                    source_list.push(DataSourceInfo {
                        server_name: name,
                        description: description,
                    })
                }
                raw::SQL_ERROR => unsafe {
                    return Err(Error::SqlError(DiagRec::create(raw::SQL_HANDLE_ENV, self.handle)));
                },
                raw::SQL_NO_DATA => break,
                /// The only other value allowed by ODBC here is SQL_INVALID_HANDLE. We protect the
                /// validity of this handle with our invariant. In save code the user should not be
                /// able to reach this code path.
                _ => panic!("Environment invariant violated"),
            }
        }
        Ok(source_list)
    }

    /// Allows access to the raw ODBC handle
    pub unsafe fn raw(&mut self) -> raw::SQLHENV {
        self.handle
    }

    /// Allows setting attributes to Environment
    pub unsafe fn set_attribute(&mut self,
                                attribute: raw::SQLINTEGER,
                                value: raw::SQLPOINTER,
                                length: raw::SQLINTEGER)
                                -> Result<()> {
        match raw::SQLSetEnvAttr(self.handle, attribute, value, length) {
            raw::SQL_SUCCESS => Ok(()),
            raw::SQL_SUCCESS_WITH_INFO => Ok(()),
            _ => Err(Error::SqlError(DiagRec::create(raw::SQL_HANDLE_ENV, self.handle))),
        }
    }

    fn alloc_info(&self,
                  f: SqlInfoFunction)
                  -> Result<(raw::SQLSMALLINT, raw::SQLSMALLINT, usize)> {
        let string_buf = std::ptr::null_mut();
        let mut buf1_length_out: raw::SQLSMALLINT = 0;
        let mut buf2_length_out: raw::SQLSMALLINT = 0;
        let mut max1 = 0;
        let mut max2 = 0;
        let mut count = 0;
        let mut result = unsafe {
            // Although the rather lengthy function call kind of blows the code, let's do the first
            // one using SQL_FETCH_FIRST, so we list all drivers independent from environment state
            f(self.handle,
              raw::SQL_FETCH_FIRST,
              string_buf,
              0,
              &mut buf1_length_out as *mut raw::SQLSMALLINT,
              string_buf,
              0,
              &mut buf2_length_out as *mut raw::SQLSMALLINT)
        };
        loop {
            match result {
                raw::SQL_SUCCESS |
                raw::SQL_SUCCESS_WITH_INFO => {
                    count += 1;
                    max1 = std::cmp::max(max1, buf1_length_out);
                    max2 = std::cmp::max(max2, buf2_length_out);
                }
                raw::SQL_NO_DATA => break,
                raw::SQL_ERROR => unsafe {
                    return Err(Error::SqlError(DiagRec::create(raw::SQL_HANDLE_ENV, self.handle)));
                },
                /// The only other value allowed by ODBC here is SQL_INVALID_HANDLE. We protect the
                /// validity of this handle with our invariant. In save code the user should not be
                /// able to reach this code path.
                _ => panic!("Environment invariant violated"),
            }
            unsafe {
                result = f(self.handle,
                           raw::SQL_FETCH_NEXT,
                           string_buf,
                           0,
                           &mut buf1_length_out as *mut raw::SQLSMALLINT,
                           string_buf,
                           0,
                           &mut buf2_length_out as *mut raw::SQLSMALLINT);
            }
        }

        Ok((max1, max2, count))
    }

    /// Called by drivers to pares list of attributes
    ///
    /// Key value pairs are seperated by `\0`. Key and value are seperated by `=`
    fn parse_attributes(attribute_buffer: Vec<u8>) -> HashMap<String, String> {
        String::from_utf8(attribute_buffer)
            .expect("String returned by Driver Manager should be utf8 encoded")
            .split('\0')
            .take_while(|kv_str| *kv_str != String::new())
            .map(|kv_str| {
                let mut iter = kv_str.split('=');
                let key = iter.next().unwrap();
                let value = iter.next().unwrap();
                (key.to_string(), value.to_string())
            })
            .collect()
    }
}

impl Drop for Environment {
    fn drop(&mut self) {
        unsafe {
            raw::SQLFreeEnv(self.handle);
        }
    }
}

#[cfg(test)]
mod test {

    use super::*;

    #[test]
    fn parse_attributes() {
        let buffer = "APILevel=2\0ConnectFunctions=YYY\0CPTimeout=60\0DriverODBCVer=03.\
                      50\0FileUsage=0\0SQLLevel=1\0UsageCount=1\0\0"
            .as_bytes()
            .iter()
            .cloned()
            .collect();
        let attributes = Environment::parse_attributes(buffer);
        assert_eq!(attributes["APILevel"], "2");
        assert_eq!(attributes["ConnectFunctions"], "YYY");
        assert_eq!(attributes["CPTimeout"], "60");
        assert_eq!(attributes["DriverODBCVer"], "03.50");
        assert_eq!(attributes["FileUsage"], "0");
        assert_eq!(attributes["SQLLevel"], "1");
        assert_eq!(attributes["UsageCount"], "1");
    }
}<|endoftext|>