<commit_before>use crate::{
    analysis::Analysis,
    app::{AppContext, AppContextPointer},
    errors::SondeError,
};
use gtk::{prelude::*, TextTag, TextView};
use metfor::{Fahrenheit, Inches, Quantity};
use sounding_analysis::experimental::fire::partition_cape;

macro_rules! make_default_tag {
    ($tb:ident) => {
        if let Some(tag_table) = $tb.get_tag_table() {
            let tag = TextTag::new(Some("default"));

            tag.set_property_font(Some("courier bold 12"));

            let success = tag_table.add(&tag);
            debug_assert!(success, "Failed to add tag to text tag table");
        }
    };
}

macro_rules! set_text {
    ($tb:ident, $txt:expr) => {
        $tb.set_text($txt);
        let start = $tb.get_start_iter();
        let end = $tb.get_end_iter();
        $tb.apply_tag_by_name("default", &start, &end);
    };
}

const TEXT_AREA_ID: &str = "indexes_text_area";

pub fn set_up_indexes_area(acp: &AppContextPointer) -> Result<(), SondeError> {
    let text_area: TextView = acp.fetch_widget(TEXT_AREA_ID)?;

    if let Some(tb) = text_area.get_buffer() {
        make_default_tag!(tb);
        set_text!(tb, "No data loaded");

        tb.create_mark(Some("scroll_mark"), &tb.get_start_iter(), true);

        Ok(())
    } else {
        Err(SondeError::TextBufferLoadError(TEXT_AREA_ID))
    }
}

pub fn update_indexes_area(ac: &AppContext) {
    let text_area: TextView = if let Ok(ta) = ac.fetch_widget(TEXT_AREA_ID) {
        ta
    } else {
        return;
    };

    if !text_area.is_visible() {
        return;
    }

    if let Some(tb) = text_area.get_buffer() {
        if let Some(anal) = ac.get_sounding_for_display() {
            let anal = &anal.borrow();
            let text = &mut String::with_capacity(4096);

            push_profile_indexes(text, anal);
            push_parcel_indexes(text, anal);
            push_fire_indexes(text, anal);

            // Get the scroll position before setting the text
            let old_adj = text_area.get_vadjustment().map(|adj| adj.get_value());

            set_text!(tb, &text);

            // I don't totally understand this, but after quite a lot of experimentation this works
            // well at keeping the scroll of the text view in the same area as you step through
            // time.
            if let Some(adj) = text_area.get_vadjustment() {
                if let Some(val) = old_adj {
                    let val = if val.round() < (adj.get_upper() - adj.get_page_size()).round() {
                        val.round()
                    } else {
                        (adj.get_upper() - adj.get_page_size() - 1.0).round()
                    };
                    adj.set_value(val);
                }
            }
        }
    }
}

macro_rules! push_prof {
    ($anal: expr, $buf:ident, $name:expr, $selector:tt, $format:expr, $empty_val:expr) => {
        $buf.push_str($name);
        $anal
            .$selector()
            .into_option()
            .and_then(|val| {
                $buf.push_str(&format!($format, val.unpack()));
                Some(())
            })
            .or_else(|| {
                $buf.push_str($empty_val);
                Some(())
            });
        $buf.push('\n');
    };
    ($anal: expr, $buf:ident, $name:expr, $selector:tt, $format:expr, temp, $format2:expr, $empty_val:expr) => {
        $buf.push_str($name);
        $anal
            .$selector()
            .into_option()
            .and_then(|val| {
                $buf.push_str(&format!($format, val.unpack()));
                $buf.push_str(&format!($format2, Fahrenheit::from(val).unpack().round()));
                Some(())
            })
            .or_else(|| {
                $buf.push_str($empty_val);
                Some(())
            });
        $buf.push('\n');
    };
    ($anal: expr, $buf:ident, $name:expr, $selector:tt, $format:expr, mm, $format2:expr, $empty_val:expr) => {
        $buf.push_str($name);
        $anal
            .$selector()
            .into_option()
            .and_then(|val| {
                $buf.push_str(&format!($format, val.unpack()));
                $buf.push_str(&format!(
                    $format2,
                    (Inches::from(val).unpack() * 100.0).round() / 100.0
                ));
                Some(())
            })
            .or_else(|| {
                $buf.push_str($empty_val);
                Some(())
            });
        $buf.push('\n');
    };
}

#[inline]
#[rustfmt::skip]
fn push_profile_indexes(buffer: &mut String, anal: &Analysis){
    let empty_val = "    -    ";

    buffer.push('\n');

    buffer.push_str("Index                Value\n");
    buffer.push_str("----------------------------------------------------\n");
    push_prof!(anal, buffer, "DCAPE               ", dcape,              "{:>5.0} J/kg",                                             empty_val);
    push_prof!(anal, buffer, "PWAT                ", pwat,               "{:>7.0} mm",                  mm,   " ({:>4.2} in)",       empty_val);
    push_prof!(anal, buffer, "Downrush T          ", downrush_t,         "{:>8.0}\u{00b0}C",            temp, " ({:>3.0}\u{00b0}F)", empty_val);
    push_prof!(anal, buffer, "Convective T        ", convective_t,       "{:>8.0}\u{00b0}C",            temp, " ({:>3.0}\u{00b0}F)", empty_val);
    push_prof!(anal, buffer, "3km SR Helicity (RM)", sr_helicity_3k_rm,  "{:>4.0} m\u{00b2}/s\u{00b2}",                              empty_val);
    push_prof!(anal, buffer, "3km SR Helicity (LM)", sr_helicity_3k_lm,  "{:>4.0} m\u{00b2}/s\u{00b2}",                              empty_val);
    push_prof!(anal, buffer, "Eff SR Helicity (RM)", sr_helicity_eff_rm, "{:>4.0} m\u{00b2}/s\u{00b2}",                              empty_val);
    push_prof!(anal, buffer, "Eff SR Helicity (LM)", sr_helicity_eff_lm, "{:>4.0} m\u{00b2}/s\u{00b2}",                              empty_val);
}

#[inline]
#[rustfmt::skip]
fn push_parcel_indexes(buffer: &mut String, anal: &Analysis) {

    buffer.push('\n');

    macro_rules! push_var {
        ($buf:ident, $opt_anal:ident, $selector:tt, $fmt:expr,$empty:expr) => {
            $opt_anal.and_then(|anal| {
                anal.$selector().into_option().and_then(|val|{
                    $buf.push_str(&format!($fmt, val.unpack()));
                    Some(())
                }).or_else(||{
                    $buf.push_str($empty);
                    Some(())
                });
                Some(())
            });
        }
    }

    macro_rules! parcel_index_row {
        ($buf:ident, $pcl_name:expr, $opt_pcl_anal:ident, $empty:expr) => {
            $buf.push_str($pcl_name);

            push_var!($buf, $opt_pcl_anal, cape,         " {:>5.0}", $empty);
            push_var!($buf, $opt_pcl_anal, cin,          " {:>5.0}", $empty);
            push_var!($buf, $opt_pcl_anal, ncape,        " {:>5.2}", $empty);
            push_var!($buf, $opt_pcl_anal, hail_cape,    " {:>5.0}", $empty);

            $buf.push('\n');
        }
    }

    macro_rules! parcel_level_row {
        ($buf:ident, $pcl_name:expr, $opt_pcl_anal:tt, $empty:expr) => {
            $buf.push_str($pcl_name);

            push_var!($buf, $opt_pcl_anal, lcl_pressure,   " {:>5.0}", $empty);
            push_var!($buf, $opt_pcl_anal, lcl_height_agl, " {:>6.0}", $empty);
            push_var!($buf, $opt_pcl_anal, lfc_pressure,   " {:>5.0}", $empty);
            push_var!($buf, $opt_pcl_anal, el_pressure,    " {:>5.0}", $empty);
            push_var!($buf, $opt_pcl_anal, el_height_asl,  " {:>6.0}", $empty);
            push_var!($buf, $opt_pcl_anal, el_temperature, " {:>5.0}", $empty);

            $buf.push('\n');
        }
    }

    let sfc = anal.surface_parcel_analysis();
    let ml = anal.mixed_layer_parcel_analysis();
    let mu = anal.most_unstable_parcel_analysis();
    let con = anal.convective_parcel_analysis();
    let eff = anal.effective_parcel_analysis();

    let empty = "     -";
    buffer.push_str("Parcel          CAPE   CIN NCAPE  Hail\n");
    buffer.push_str("                J/Kg  J/Kg        CAPE\n");
    buffer.push_str("----------------------------------------------------\n");
    parcel_index_row!(buffer, "Surface       ", sfc, empty);
    parcel_index_row!(buffer, "Mixed Layer   ", ml,  empty);
    parcel_index_row!(buffer, "Most Unstable ", mu,  empty);
    parcel_index_row!(buffer, "Convective    ", con, empty);
    parcel_index_row!(buffer, "Effective     ", eff, empty);
    buffer.push('\n');
    buffer.push_str("Parcel           LCL    LCL   LFC    EL     EL    EL\n");
    buffer.push_str("                 hPa  m AGL   hPa   hPa  m ASL     C\n");
    buffer.push_str("----------------------------------------------------\n");
    parcel_level_row!(buffer, "Surface       ", sfc, empty);
    parcel_level_row!(buffer, "Mixed Layer   ", ml,  empty);
    parcel_level_row!(buffer, "Most Unstable ", mu,  empty);
    parcel_level_row!(buffer, "Convective    ", con, empty);
    parcel_level_row!(buffer, "Effective     ", eff, empty);
}

#[inline]
#[rustfmt::skip]
fn push_fire_indexes(buffer: &mut String, anal: &Analysis) {

    macro_rules! push_fire_index {
        ($buf:ident, $label:expr, $anal:ident, $selector:tt, $fmt:expr, $empty:expr) => {
            $buf.push_str($label);
            if let Some(val) = $anal.$selector().into_option() {
                $buf.push_str(&format!($fmt, val.unpack()));
            } else {
                $buf.push_str($empty);
            }
        };
    }

    buffer.push('\n');
    buffer.push_str("Fire Weather\n");
    buffer.push_str("----------------------------------------------------\n");

    buffer.push_str("Haines     Low   Mid  High\n");
    buffer.push_str("         ");

    let empty = "  -   ";
    for &hns in [anal.haines_low(), anal.haines_mid(), anal.haines_high()].iter() {
        if let Some(val) = hns.into_option() {
            buffer.push_str(&format!("{:>5.0} ", val));
        } else {
            buffer.push_str(empty);
        }
    }
    buffer.push('\n');
    push_fire_index!(buffer, "HDW           ", anal, hdw, "{:>12.0}\n", empty);

    let empty = " - \n";

    buffer.push_str("\nExperimental\n");
    buffer.push_str("----------------------------------------------------\n");
    push_fire_index!(buffer, "Blow Up ∆T    ", anal, blow_up_dt,            "{:>9.1} \u{00b0}C\n", empty);
    push_fire_index!(buffer, "Blow Up Height", anal, blow_up_height_change, "{:>11.0}m\n",         empty);

    if let Some(parcel_anal) = anal.convective_parcel_analysis(){
        if let Ok((dry, wet)) = partition_cape(parcel_anal){
            let dry = dry.unpack();
            let wet = wet.unpack();
            buffer.push_str(
                &format!("Dry cape      {:>7.0} J/kg\nWet cape      {:>7.0} J/kg\n", dry, wet)
            );

            buffer.push_str(
                &format!("Pct Wet               {:>3.0}%\n", 100.0 * wet / (wet + dry))
            );
        }
    }
}
<commit_msg>Added dcape velocity to indexes.<commit_after>use crate::{
    analysis::Analysis,
    app::{AppContext, AppContextPointer},
    errors::SondeError,
};
use gtk::{prelude::*, TextTag, TextView};
use metfor::{Fahrenheit, Inches, Quantity};
use sounding_analysis::experimental::fire::partition_cape;

macro_rules! make_default_tag {
    ($tb:ident) => {
        if let Some(tag_table) = $tb.get_tag_table() {
            let tag = TextTag::new(Some("default"));

            tag.set_property_font(Some("courier bold 12"));

            let success = tag_table.add(&tag);
            debug_assert!(success, "Failed to add tag to text tag table");
        }
    };
}

macro_rules! set_text {
    ($tb:ident, $txt:expr) => {
        $tb.set_text($txt);
        let start = $tb.get_start_iter();
        let end = $tb.get_end_iter();
        $tb.apply_tag_by_name("default", &start, &end);
    };
}

const TEXT_AREA_ID: &str = "indexes_text_area";

pub fn set_up_indexes_area(acp: &AppContextPointer) -> Result<(), SondeError> {
    let text_area: TextView = acp.fetch_widget(TEXT_AREA_ID)?;

    if let Some(tb) = text_area.get_buffer() {
        make_default_tag!(tb);
        set_text!(tb, "No data loaded");

        tb.create_mark(Some("scroll_mark"), &tb.get_start_iter(), true);

        Ok(())
    } else {
        Err(SondeError::TextBufferLoadError(TEXT_AREA_ID))
    }
}

pub fn update_indexes_area(ac: &AppContext) {
    let text_area: TextView = if let Ok(ta) = ac.fetch_widget(TEXT_AREA_ID) {
        ta
    } else {
        return;
    };

    if !text_area.is_visible() {
        return;
    }

    if let Some((tb, anal)) = text_area
        .get_buffer()
        .and_then(|tb| ac.get_sounding_for_display().map(|anal| (tb, anal)))
    {
        let anal = &anal.borrow();
        let text = &mut String::with_capacity(4096);

        push_profile_indexes(text, anal);
        push_parcel_indexes(text, anal);
        push_fire_indexes(text, anal);

        // Get the scroll position before setting the text
        let old_adj = text_area.get_vadjustment().map(|adj| adj.get_value());

        set_text!(tb, &text);

        // I don't totally understand this, but after quite a lot of experimentation this works
        // well at keeping the scroll of the text view in the same area as you step through
        // time.
        if let Some(adj) = text_area.get_vadjustment() {
            if let Some(val) = old_adj {
                let val = if val.round() < (adj.get_upper() - adj.get_page_size()).round() {
                    val.round()
                } else {
                    (adj.get_upper() - adj.get_page_size() - 1.0).round()
                };
                adj.set_value(val);
            }
        }
    }
}

macro_rules! push_prof {
    ($anal: expr, $buf:ident, $name:expr, $selector:tt, $format:expr, $empty_val:expr) => {
        $buf.push_str($name);
        $anal
            .$selector()
            .into_option()
            .and_then(|val| {
                $buf.push_str(&format!($format, val.unpack()));
                Some(())
            })
            .or_else(|| {
                $buf.push_str($empty_val);
                Some(())
            });
        $buf.push('\n');
    };
    ($anal: expr, $buf:ident, $name:expr, $selector:tt, $format:expr, temp, $format2:expr, $empty_val:expr) => {
        $buf.push_str($name);
        $anal
            .$selector()
            .into_option()
            .and_then(|val| {
                $buf.push_str(&format!($format, val.unpack()));
                $buf.push_str(&format!($format2, Fahrenheit::from(val).unpack().round()));
                Some(())
            })
            .or_else(|| {
                $buf.push_str($empty_val);
                Some(())
            });
        $buf.push('\n');
    };
    ($anal: expr, $buf:ident, $name:expr, $selector:tt, $format:expr, mm, $format2:expr, $empty_val:expr) => {
        $buf.push_str($name);
        $anal
            .$selector()
            .into_option()
            .and_then(|val| {
                $buf.push_str(&format!($format, val.unpack()));
                $buf.push_str(&format!(
                    $format2,
                    (Inches::from(val).unpack() * 100.0).round() / 100.0
                ));
                Some(())
            })
            .or_else(|| {
                $buf.push_str($empty_val);
                Some(())
            });
        $buf.push('\n');
    };
    ($anal: expr, $buf:ident, $name:expr, $selector:tt, $format:expr, cape, $format2:expr, $empty_val:expr) => {
        $buf.push_str($name);
        $anal
            .$selector()
            .into_option()
            .and_then(|val| {
                let mps = (val.unpack() * 2.0).sqrt();
                let mph = mps * 2.23694;
                $buf.push_str(&format!($format, val.unpack()));
                $buf.push_str(&format!($format2, mps.round(), mph.round(),));
                Some(())
            })
            .or_else(|| {
                $buf.push_str($empty_val);
                Some(())
            });
        $buf.push('\n');
    };
}

#[inline]
#[rustfmt::skip]
fn push_profile_indexes(buffer: &mut String, anal: &Analysis){
    let empty_val = "    -    ";

    buffer.push('\n');

    buffer.push_str("Index                Value\n");
    buffer.push_str("----------------------------------------------------\n");
    push_prof!(anal, buffer, "DCAPE               ", dcape,              "{:>5.0} J/kg",                cape, " ({:>3.0} m/s {:>3.0} m/h)", empty_val);
    push_prof!(anal, buffer, "PWAT                ", pwat,               "{:>7.0} mm",                  mm,   " ({:>4.2} in)",              empty_val);
    push_prof!(anal, buffer, "Downrush T          ", downrush_t,         "{:>8.0}\u{00b0}C",            temp, " ({:>3.0}\u{00b0}F)",        empty_val);
    push_prof!(anal, buffer, "Convective T        ", convective_t,       "{:>8.0}\u{00b0}C",            temp, " ({:>3.0}\u{00b0}F)",        empty_val);
    push_prof!(anal, buffer, "3km SR Helicity (RM)", sr_helicity_3k_rm,  "{:>4.0} m\u{00b2}/s\u{00b2}",                                     empty_val);
    push_prof!(anal, buffer, "3km SR Helicity (LM)", sr_helicity_3k_lm,  "{:>4.0} m\u{00b2}/s\u{00b2}",                                     empty_val);
    push_prof!(anal, buffer, "Eff SR Helicity (RM)", sr_helicity_eff_rm, "{:>4.0} m\u{00b2}/s\u{00b2}",                                     empty_val);
    push_prof!(anal, buffer, "Eff SR Helicity (LM)", sr_helicity_eff_lm, "{:>4.0} m\u{00b2}/s\u{00b2}",                                     empty_val);
}

#[inline]
#[rustfmt::skip]
fn push_parcel_indexes(buffer: &mut String, anal: &Analysis) {

    buffer.push('\n');

    macro_rules! push_var {
        ($buf:ident, $opt_anal:ident, $selector:tt, $fmt:expr,$empty:expr) => {
            $opt_anal.and_then(|anal| {
                anal.$selector().into_option().and_then(|val|{
                    $buf.push_str(&format!($fmt, val.unpack()));
                    Some(())
                }).or_else(||{
                    $buf.push_str($empty);
                    Some(())
                });
                Some(())
            });
        }
    }

    macro_rules! parcel_index_row {
        ($buf:ident, $pcl_name:expr, $opt_pcl_anal:ident, $empty:expr) => {
            $buf.push_str($pcl_name);

            push_var!($buf, $opt_pcl_anal, cape,         " {:>5.0}", $empty);
            push_var!($buf, $opt_pcl_anal, cin,          " {:>5.0}", $empty);
            push_var!($buf, $opt_pcl_anal, ncape,        " {:>5.2}", $empty);
            push_var!($buf, $opt_pcl_anal, hail_cape,    " {:>5.0}", $empty);

            $buf.push('\n');
        }
    }

    macro_rules! parcel_level_row {
        ($buf:ident, $pcl_name:expr, $opt_pcl_anal:tt, $empty:expr) => {
            $buf.push_str($pcl_name);

            push_var!($buf, $opt_pcl_anal, lcl_pressure,   " {:>5.0}", $empty);
            push_var!($buf, $opt_pcl_anal, lcl_height_agl, " {:>6.0}", $empty);
            push_var!($buf, $opt_pcl_anal, lfc_pressure,   " {:>5.0}", $empty);
            push_var!($buf, $opt_pcl_anal, el_pressure,    " {:>5.0}", $empty);
            push_var!($buf, $opt_pcl_anal, el_height_asl,  " {:>6.0}", $empty);
            push_var!($buf, $opt_pcl_anal, el_temperature, " {:>5.0}", $empty);

            $buf.push('\n');
        }
    }

    let sfc = anal.surface_parcel_analysis();
    let ml = anal.mixed_layer_parcel_analysis();
    let mu = anal.most_unstable_parcel_analysis();
    let con = anal.convective_parcel_analysis();
    let eff = anal.effective_parcel_analysis();

    let empty = "     -";
    buffer.push_str("Parcel          CAPE   CIN NCAPE  Hail\n");
    buffer.push_str("                J/Kg  J/Kg        CAPE\n");
    buffer.push_str("----------------------------------------------------\n");
    parcel_index_row!(buffer, "Surface       ", sfc, empty);
    parcel_index_row!(buffer, "Mixed Layer   ", ml,  empty);
    parcel_index_row!(buffer, "Most Unstable ", mu,  empty);
    parcel_index_row!(buffer, "Convective    ", con, empty);
    parcel_index_row!(buffer, "Effective     ", eff, empty);
    buffer.push('\n');
    buffer.push_str("Parcel           LCL    LCL   LFC    EL     EL    EL\n");
    buffer.push_str("                 hPa  m AGL   hPa   hPa  m ASL     C\n");
    buffer.push_str("----------------------------------------------------\n");
    parcel_level_row!(buffer, "Surface       ", sfc, empty);
    parcel_level_row!(buffer, "Mixed Layer   ", ml,  empty);
    parcel_level_row!(buffer, "Most Unstable ", mu,  empty);
    parcel_level_row!(buffer, "Convective    ", con, empty);
    parcel_level_row!(buffer, "Effective     ", eff, empty);
}

#[inline]
#[rustfmt::skip]
fn push_fire_indexes(buffer: &mut String, anal: &Analysis) {

    macro_rules! push_fire_index {
        ($buf:ident, $label:expr, $anal:ident, $selector:tt, $fmt:expr, $empty:expr) => {
            $buf.push_str($label);
            if let Some(val) = $anal.$selector().into_option() {
                $buf.push_str(&format!($fmt, val.unpack()));
            } else {
                $buf.push_str($empty);
            }
        };
    }

    buffer.push('\n');
    buffer.push_str("Fire Weather\n");
    buffer.push_str("----------------------------------------------------\n");

    buffer.push_str("Haines     Low   Mid  High\n");
    buffer.push_str("         ");

    let empty = "  -   ";
    for &hns in [anal.haines_low(), anal.haines_mid(), anal.haines_high()].iter() {
        if let Some(val) = hns.into_option() {
            buffer.push_str(&format!("{:>5.0} ", val));
        } else {
            buffer.push_str(empty);
        }
    }
    buffer.push('\n');
    push_fire_index!(buffer, "HDW           ", anal, hdw, "{:>12.0}\n", empty);

    let empty = " - \n";

    buffer.push_str("\nExperimental\n");
    buffer.push_str("----------------------------------------------------\n");
    push_fire_index!(buffer, "Blow Up ∆T    ", anal, blow_up_dt,            "{:>9.1} \u{00b0}C\n", empty);
    push_fire_index!(buffer, "Blow Up Height", anal, blow_up_height_change, "{:>11.0}m\n",         empty);

    if let Some(parcel_anal) = anal.convective_parcel_analysis(){
        if let Ok((dry, wet)) = partition_cape(parcel_anal){
            let dry = dry.unpack();
            let wet = wet.unpack();
            buffer.push_str(
                &format!("Dry cape      {:>7.0} J/kg\nWet cape      {:>7.0} J/kg\n", dry, wet)
            );

            buffer.push_str(
                &format!("Pct Wet               {:>3.0}%\n", 100.0 * wet / (wet + dry))
            );
        }
    }
}
<|endoftext|>


<commit_before>use args;
use timing_analyzer;
use result;
use hyper;
use prometheus::Encoder;
use render;
use render::Renderer;
use run;

struct HttpHandler {
    args: args::RequestLogAnalyzerArgs,
    run: fn(&args::RequestLogAnalyzerArgs) -> result::RequestLogAnalyzerResult,
}

impl hyper::server::Handler for HttpHandler {
    fn handle(&self, _: hyper::server::Request, mut res: hyper::server::Response) {
        let result = (self.run)(&self.args);

        let mut renderer = render::prometheus::PrometheusRenderer::new();
        renderer.render(result);
        res.headers_mut()
            .set(hyper::header::ContentType(renderer.encoder
                .format_type()
                .parse::<hyper::mime::Mime>()
                .unwrap()));
        res.send(&renderer.buffer).unwrap();
    }
}

pub fn listen_http(args: args::RequestLogAnalyzerArgs, binding_address: &str) {
    let handler = HttpHandler {
        args: args,
        run: run,
    };

    info!("listening addr {:?}", binding_address);
    hyper::server::Server::http(binding_address).unwrap().handle(handler).unwrap();
}

#[cfg(test)]
mod tests {
    use std::str;
    use std::net::{IpAddr, Ipv4Addr, SocketAddr};
    use hyper::server::Handler;
    use hyper;

    use filter;
    use timing_analyzer;
    use super::*;

    mod mock;

    #[test]
    fn test_handle() {
        let args = args::RequestLogAnalyzerArgs {
            filename: String::from("src/test/simple-1.log"),
            conditions: filter::FilterConditions {
                include_terms: None,
                exclude_terms: None,
                latest_time: None,
            },
            graphite_server: None,
            graphite_port: Some(2003),
            graphite_prefix: None,
            prometheus_listen: None,
        };

        fn run_fn(_: &args::RequestLogAnalyzerArgs) -> result::RequestLogAnalyzerResult {
            result::RequestLogAnalyzerResult {
                count: 3,
                timing: Some(timing_analyzer::TimingResult {
                    max: 100,
                    min: 1,
                    avg: 37,
                    median: 10,
                    percentile90: 100,
                }),
                error: None,
            }
        };

        let handler = HttpHandler {
            args: args,
            run: run_fn,
        };

        // Create a minimal HTTP request
        let mut request_mock_network_stream =
            mock::MockStream::with_input(b"GET / HTTP/1.0\r\n\r\n");

        let mut reader = hyper::buffer::BufReader::new(&mut request_mock_network_stream as
                                                       &mut hyper::net::NetworkStream);

        let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);

        let request = hyper::server::Request::new(&mut reader, socket).unwrap();

        let mut headers = hyper::header::Headers::new();
        let mut response_mock_network_stream = mock::MockStream::new();

        {
            let response = hyper::server::Response::new(&mut response_mock_network_stream,
                                                        &mut headers);

            handler.handle(request, response);
        }

        let result = str::from_utf8(&response_mock_network_stream.write).unwrap();

        assert!(result.contains("request_count 3"));
        assert!(result.contains("request_time_max 100"));
        assert!(result.contains("request_time_min 1"));
        assert!(result.contains("request_time_avg 37"));
        assert!(result.contains("request_time_median 10"));
        assert!(result.contains("request_time_percentile90 100"));

    }
}
<commit_msg>Unneeded import<commit_after>use args;
use result;
use hyper;
use prometheus::Encoder;
use render;
use render::Renderer;
use run;

struct HttpHandler {
    args: args::RequestLogAnalyzerArgs,
    run: fn(&args::RequestLogAnalyzerArgs) -> result::RequestLogAnalyzerResult,
}

impl hyper::server::Handler for HttpHandler {
    fn handle(&self, _: hyper::server::Request, mut res: hyper::server::Response) {
        let result = (self.run)(&self.args);

        let mut renderer = render::prometheus::PrometheusRenderer::new();
        renderer.render(result);
        res.headers_mut()
            .set(hyper::header::ContentType(renderer.encoder
                .format_type()
                .parse::<hyper::mime::Mime>()
                .unwrap()));
        res.send(&renderer.buffer).unwrap();
    }
}

pub fn listen_http(args: args::RequestLogAnalyzerArgs, binding_address: &str) {
    let handler = HttpHandler {
        args: args,
        run: run,
    };

    info!("listening addr {:?}", binding_address);
    hyper::server::Server::http(binding_address).unwrap().handle(handler).unwrap();
}

#[cfg(test)]
mod tests {
    use std::str;
    use std::net::{IpAddr, Ipv4Addr, SocketAddr};
    use hyper::server::Handler;
    use hyper;

    use filter;
    use timing_analyzer;
    use super::*;

    mod mock;

    #[test]
    fn test_handle() {
        let args = args::RequestLogAnalyzerArgs {
            filename: String::from("src/test/simple-1.log"),
            conditions: filter::FilterConditions {
                include_terms: None,
                exclude_terms: None,
                latest_time: None,
            },
            graphite_server: None,
            graphite_port: Some(2003),
            graphite_prefix: None,
            prometheus_listen: None,
        };

        fn run_fn(_: &args::RequestLogAnalyzerArgs) -> result::RequestLogAnalyzerResult {
            result::RequestLogAnalyzerResult {
                count: 3,
                timing: Some(timing_analyzer::TimingResult {
                    max: 100,
                    min: 1,
                    avg: 37,
                    median: 10,
                    percentile90: 100,
                }),
                error: None,
            }
        };

        let handler = HttpHandler {
            args: args,
            run: run_fn,
        };

        // Create a minimal HTTP request
        let mut request_mock_network_stream =
            mock::MockStream::with_input(b"GET / HTTP/1.0\r\n\r\n");

        let mut reader = hyper::buffer::BufReader::new(&mut request_mock_network_stream as
                                                       &mut hyper::net::NetworkStream);

        let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);

        let request = hyper::server::Request::new(&mut reader, socket).unwrap();

        let mut headers = hyper::header::Headers::new();
        let mut response_mock_network_stream = mock::MockStream::new();

        {
            let response = hyper::server::Response::new(&mut response_mock_network_stream,
                                                        &mut headers);

            handler.handle(request, response);
        }

        let result = str::from_utf8(&response_mock_network_stream.write).unwrap();

        assert!(result.contains("request_count 3"));
        assert!(result.contains("request_time_max 100"));
        assert!(result.contains("request_time_min 1"));
        assert!(result.contains("request_time_avg 37"));
        assert!(result.contains("request_time_median 10"));
        assert!(result.contains("request_time_percentile90 100"));

    }
}
<|endoftext|>


<commit_before>use gobjects::*;
use library::*;

pub struct StatusedTypeId{
    pub type_id: TypeId,
    pub name: String,
    pub status: GStatus,
}

pub fn widget_tid(library: &Library) -> TypeId {
    library.find_type(0, "Gtk.Widget").unwrap_or_else(|| unreachable!())
}

pub trait IsWidget {
    fn is_widget(&self, library: &Library) -> bool;
}

impl IsWidget for Class {
    fn is_widget(&self, library: &Library) -> bool {
        self.glib_type_name == "GtkWidget" || self.parents.contains(&widget_tid(&library))
    }
}

impl IsWidget for Type {
    fn is_widget(&self, library: &Library) -> bool {
        match self {
            &Type::Class(ref klass) => klass.is_widget(&library),
            _ => false,
        }
    }
}

impl IsWidget for TypeId {
    fn is_widget(&self, library: &Library) -> bool {
        library.type_(*self).is_widget(&library)
    }
}

impl IsWidget for String {
    fn is_widget(&self, library: &Library) -> bool {
        library.find_type(0, self).unwrap().is_widget(library)
    }
}
<commit_msg>Fix "no talking" panic on bad object names in Gir.toml<commit_after>use gobjects::*;
use library::*;

pub struct StatusedTypeId{
    pub type_id: TypeId,
    pub name: String,
    pub status: GStatus,
}

pub fn widget_tid(library: &Library) -> TypeId {
    library.find_type(0, "Gtk.Widget").unwrap_or_else(|| unreachable!())
}

pub trait IsWidget {
    fn is_widget(&self, library: &Library) -> bool;
}

impl IsWidget for Class {
    fn is_widget(&self, library: &Library) -> bool {
        self.glib_type_name == "GtkWidget" || self.parents.contains(&widget_tid(&library))
    }
}

impl IsWidget for Type {
    fn is_widget(&self, library: &Library) -> bool {
        match self {
            &Type::Class(ref klass) => klass.is_widget(&library),
            _ => false,
        }
    }
}

impl IsWidget for TypeId {
    fn is_widget(&self, library: &Library) -> bool {
        library.type_(*self).is_widget(&library)
    }
}

impl IsWidget for String {
    fn is_widget(&self, library: &Library) -> bool {
        library.find_type_unwrapped(0, self, "Type").is_widget(library)
    }
}
<|endoftext|>


<commit_before>use super::namespaces;
use crate::{library::Library, nameutil::crate_name, version::Version};
use std::borrow::Cow;
use std::cmp::Ordering;
use std::collections::btree_map::BTreeMap;
use std::ops::{Deref, DerefMut};
use std::vec::IntoIter;

fn is_first_char_up(s: &str) -> bool {
    s.chars().next().unwrap().is_uppercase()
}

fn check_up_eq(a: &str, b: &str) -> Ordering {
    let is_a_up = is_first_char_up(a);
    let is_b_up = is_first_char_up(b);
    if is_a_up != is_b_up {
        if is_a_up {
            return Ordering::Greater;
        }
        return Ordering::Less;
    }
    Ordering::Equal
}

/// This function is used by the `Imports` type to generate output like `cargo fmt` would.
///
/// For example:
///
/// ```text
/// use gdk; // lowercases come first.
/// use Window;
///
/// use gdk::foo; // lowercases come first here as well.
/// use gdk::Foo;
/// ```
#[allow(clippy::trivially_copy_pass_by_ref)] // because of the way this is used, the refs are needed
fn compare_imports(a: &(&String, &ImportConditions), b: &(&String, &ImportConditions)) -> Ordering {
    let s = check_up_eq(a.0, b.0);
    if s != Ordering::Equal {
        return s;
    }
    let mut a = a.0.split("::");
    let mut b = b.0.split("::");
    loop {
        match (a.next(), b.next()) {
            (Some(a), Some(b)) => {
                let s = check_up_eq(a, b);
                if s != Ordering::Equal {
                    break s;
                }
                let s = a.partial_cmp(b).unwrap();
                if s != Ordering::Equal {
                    break s;
                }
            }
            (Some(_), None) => break Ordering::Greater,
            (None, Some(_)) => break Ordering::Less,
            (None, None) => break Ordering::Equal,
        }
    }
}

/// Provides assistance in generating use declarations.
///
/// It takes into account that use declaration referring to names within the
/// same crate will look differently. It also avoids generating spurious
/// declarations referring to names from within the same module as the one we
/// are generating code for.
#[derive(Clone, Debug, Default)]
pub struct Imports {
    /// Name of the current crate.
    crate_name: String,
    /// Name defined within current module. It doesn't need use declaration.
    ///
    /// NOTE: Currently we don't need to support more than one such name.
    defined: Option<String>,
    defaults: ImportConditions,
    map: BTreeMap<String, ImportConditions>,
}

impl Imports {
    pub fn new(gir: &Library) -> Imports {
        Imports {
            crate_name: make_crate_name(gir),
            defined: None,
            defaults: ImportConditions::default(),
            map: BTreeMap::new(),
        }
    }

    pub fn with_defined(gir: &Library, name: &str) -> Imports {
        Imports {
            crate_name: make_crate_name(gir),
            defined: Some(name.to_owned()),
            defaults: ImportConditions::default(),
            map: BTreeMap::new(),
        }
    }

    #[must_use = "ImportsWithDefault must live while defaults are needed"]
    pub fn with_defaults(
        &mut self,
        version: Option<Version>,
        constraint: &Option<String>,
    ) -> ImportsWithDefault<'_> {
        let constraints = if let Some(constraint) = constraint {
            vec![constraint.clone()]
        } else {
            vec![]
        };
        self.defaults = ImportConditions {
            version,
            constraints,
        };

        ImportsWithDefault::new(self)
    }

    fn reset_defaults(&mut self) {
        self.defaults.clear();
    }

    /// The goals of this function is to discard unwanted imports like "ffi" and "crate". It
    /// also extends the checks in case you are implementing "X". For example, you don't want to
    /// import "X" or "crate::X" in this case.
    fn common_checks(&self, name: &str) -> bool {
        if name == "crate" || name == "ffi" {
            false
        } else if let Some(ref defined) = self.defined {
            !((name.starts_with("crate::") && &name[7..] == defined) || name == defined)
        } else {
            true
        }
    }

    /// Declares that name should be available through its last path component.
    ///
    /// For example, if name is `X::Y::Z` then it will be available as `Z`.
    /// Uses defaults.
    pub fn add(&mut self, name: &str) {
        if !self.common_checks(name) {
            return;
        }
        if let Some(mut name) = self.strip_crate_name(name) {
            if name == "xlib" {
                name = if self.crate_name == "gdk_x11" {
                    // Dirty little hack to allow to have correct import for GDKX11.
                    Cow::Borrowed("x11::xlib")
                } else {
                    // gtk has a module named "xlib" which is why this hack is needed too.
                    Cow::Borrowed("crate::xlib")
                };
            }
            let defaults = &self.defaults;
            let entry = self
                .map
                .entry(name.into_owned())
                .or_insert_with(|| defaults.clone());
            entry.update_version(self.defaults.version);
            entry.update_constraints(&self.defaults.constraints);
        }
    }

    /// Declares that name should be available through its last path component.
    ///
    /// For example, if name is `X::Y::Z` then it will be available as `Z`.
    pub fn add_with_version(&mut self, name: &str, version: Option<Version>) {
        if !self.common_checks(name) {
            return;
        }
        if let Some(name) = self.strip_crate_name(name) {
            let entry = self
                .map
                .entry(name.into_owned())
                .or_insert(ImportConditions {
                    version,
                    constraints: Vec::new(),
                });
            entry.update_version(version);
            // Since there is no constraint on this import, if any constraint
            // is present, we can just remove it.
            entry.constraints.clear();
        }
    }

    /// Declares that name should be available through its last path component and provides
    /// an optional feature constraint.
    ///
    /// For example, if name is `X::Y::Z` then it will be available as `Z`.
    pub fn add_with_constraint(
        &mut self,
        name: &str,
        version: Option<Version>,
        constraint: Option<&str>,
    ) {
        if !self.common_checks(name) {
            return;
        }
        if let Some(name) = self.strip_crate_name(name) {
            let entry = if let Some(constraint) = constraint {
                let constraint = String::from(constraint);
                let entry = self
                    .map
                    .entry(name.into_owned())
                    .or_insert(ImportConditions {
                        version,
                        constraints: vec![constraint.clone()],
                    });
                entry.add_constraint(constraint);
                entry
            } else {
                let entry = self
                    .map
                    .entry(name.into_owned())
                    .or_insert(ImportConditions {
                        version,
                        constraints: Vec::new(),
                    });
                // Since there is no constraint on this import, if any constraint
                // is present, we can just remove it.
                entry.constraints.clear();
                entry
            };
            entry.update_version(version);
        }
    }

    /// Declares that name should be available through its full path.
    ///
    /// For example, if name is `X::Y` then it will be available as `X::Y`.
    pub fn add_used_type(&mut self, used_type: &str) {
        if let Some(i) = used_type.find("::") {
            if i == 0 {
                self.add(&used_type[2..]);
            } else {
                self.add(&used_type[..i]);
            }
        } else {
            self.add(&format!("crate::{}", used_type));
        }
    }

    pub fn add_used_types(&mut self, used_types: &[String]) {
        for s in used_types {
            self.add_used_type(s);
        }
    }

    /// Declares that name should be available through its full path.
    ///
    /// For example, if name is `X::Y` then it will be available as `X::Y`.
    pub fn add_used_type_with_version(&mut self, used_type: &str, version: Option<Version>) {
        if let Some(i) = used_type.find("::") {
            if i == 0 {
                self.add_with_version(&used_type[2..], version);
            } else {
                self.add_with_version(&used_type[..i], version);
            }
        } else {
            self.add_with_version(&format!("crate::{}", used_type), version);
        }
    }

    /// Tries to strip crate name prefix from given name.
    ///
    /// Returns `None` if name matches crate name exactly. Otherwise returns
    /// name with crate name prefix stripped or full name if there was no match.
    fn strip_crate_name<'a>(&self, name: &'a str) -> Option<Cow<'a, str>> {
        let prefix = &self.crate_name;
        if !name.starts_with(prefix) {
            return Some(Cow::Borrowed(name));
        }
        let rest = &name[prefix.len()..];
        if rest.is_empty() {
            None
        } else if rest.starts_with("::") {
            Some(Cow::Owned(format!("crate{}", rest)))
        } else {
            // It was false positive, return the whole name.
            Some(Cow::Borrowed(name))
        }
    }

    pub fn iter(&self) -> IntoIter<(&String, &ImportConditions)> {
        let mut imports = self.map.iter().collect::<Vec<_>>();
        imports.sort_by(compare_imports);
        imports.into_iter()
    }
}

pub struct ImportsWithDefault<'a> {
    imports: &'a mut Imports,
}

impl<'a> ImportsWithDefault<'a> {
    fn new(imports: &'a mut Imports) -> Self {
        Self { imports }
    }
}

impl Drop for ImportsWithDefault<'_> {
    fn drop(&mut self) {
        self.imports.reset_defaults()
    }
}

impl Deref for ImportsWithDefault<'_> {
    type Target = Imports;
    fn deref(&self) -> &Self::Target {
        self.imports
    }
}

impl DerefMut for ImportsWithDefault<'_> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.imports
    }
}

#[derive(Clone, Debug, Default)]
pub struct ImportConditions {
    pub version: Option<Version>,
    pub constraints: Vec<String>,
}

impl ImportConditions {
    fn clear(&mut self) {
        self.version = None;
        self.constraints.clear();
    }

    fn update_version(&mut self, version: Option<Version>) {
        if version < self.version {
            self.version = version;
        }
    }

    fn add_constraint(&mut self, constraint: String) {
        // If the import is already present but doesn't have any constraint,
        // we don't want to add one.
        if self.constraints.is_empty() {
            return;
        }
        // Otherwise, we just check if the constraint
        // is already present or not before adding it.
        if !self.constraints.iter().any(|x| x == &constraint) {
            self.constraints.push(constraint);
        }
    }

    fn update_constraints(&mut self, constraints: &[String]) {
        // If the import is already present but doesn't have any constraint,
        // we don't want to add one.
        if self.constraints.is_empty() {
            return;
        }
        if constraints.is_empty() {
            // Since there is no constraint on this import, if any constraint
            // is present, we can just remove it.
            self.constraints.clear();
        } else {
            // Otherwise, we just check if the constraint
            // is already present or not before adding it.
            for constraint in constraints {
                if !self.constraints.iter().any(|x| x == constraint) {
                    self.constraints.push(constraint.clone());
                }
            }
        }
    }
}

fn make_crate_name(gir: &Library) -> String {
    if gir.is_glib_crate() {
        crate_name("GLib")
    } else {
        crate_name(gir.namespace(namespaces::MAIN).name.as_str())
    }
}
<commit_msg>Remove all unnecessary remaining imports<commit_after>use super::namespaces;
use crate::{library::Library, nameutil::crate_name, version::Version};
use std::borrow::Cow;
use std::cmp::Ordering;
use std::collections::btree_map::BTreeMap;
use std::ops::{Deref, DerefMut};
use std::vec::IntoIter;

fn is_first_char_up(s: &str) -> bool {
    s.chars().next().unwrap().is_uppercase()
}

fn check_up_eq(a: &str, b: &str) -> Ordering {
    let is_a_up = is_first_char_up(a);
    let is_b_up = is_first_char_up(b);
    if is_a_up != is_b_up {
        if is_a_up {
            return Ordering::Greater;
        }
        return Ordering::Less;
    }
    Ordering::Equal
}

/// This function is used by the `Imports` type to generate output like `cargo fmt` would.
///
/// For example:
///
/// ```text
/// use gdk; // lowercases come first.
/// use Window;
///
/// use gdk::foo; // lowercases come first here as well.
/// use gdk::Foo;
/// ```
#[allow(clippy::trivially_copy_pass_by_ref)] // because of the way this is used, the refs are needed
fn compare_imports(a: &(&String, &ImportConditions), b: &(&String, &ImportConditions)) -> Ordering {
    let s = check_up_eq(a.0, b.0);
    if s != Ordering::Equal {
        return s;
    }
    let mut a = a.0.split("::");
    let mut b = b.0.split("::");
    loop {
        match (a.next(), b.next()) {
            (Some(a), Some(b)) => {
                let s = check_up_eq(a, b);
                if s != Ordering::Equal {
                    break s;
                }
                let s = a.partial_cmp(b).unwrap();
                if s != Ordering::Equal {
                    break s;
                }
            }
            (Some(_), None) => break Ordering::Greater,
            (None, Some(_)) => break Ordering::Less,
            (None, None) => break Ordering::Equal,
        }
    }
}

/// Provides assistance in generating use declarations.
///
/// It takes into account that use declaration referring to names within the
/// same crate will look differently. It also avoids generating spurious
/// declarations referring to names from within the same module as the one we
/// are generating code for.
#[derive(Clone, Debug, Default)]
pub struct Imports {
    /// Name of the current crate.
    crate_name: String,
    /// Name defined within current module. It doesn't need use declaration.
    ///
    /// NOTE: Currently we don't need to support more than one such name.
    defined: Option<String>,
    defaults: ImportConditions,
    map: BTreeMap<String, ImportConditions>,
}

impl Imports {
    pub fn new(gir: &Library) -> Imports {
        Imports {
            crate_name: make_crate_name(gir),
            defined: None,
            defaults: ImportConditions::default(),
            map: BTreeMap::new(),
        }
    }

    pub fn with_defined(gir: &Library, name: &str) -> Imports {
        Imports {
            crate_name: make_crate_name(gir),
            defined: Some(name.to_owned()),
            defaults: ImportConditions::default(),
            map: BTreeMap::new(),
        }
    }

    #[must_use = "ImportsWithDefault must live while defaults are needed"]
    pub fn with_defaults(
        &mut self,
        version: Option<Version>,
        constraint: &Option<String>,
    ) -> ImportsWithDefault<'_> {
        let constraints = if let Some(constraint) = constraint {
            vec![constraint.clone()]
        } else {
            vec![]
        };
        self.defaults = ImportConditions {
            version,
            constraints,
        };

        ImportsWithDefault::new(self)
    }

    fn reset_defaults(&mut self) {
        self.defaults.clear();
    }

    /// The goals of this function is to discard unwanted imports like "ffi" and "crate". It
    /// also extends the checks in case you are implementing "X". For example, you don't want to
    /// import "X" or "crate::X" in this case.
    fn common_checks(&self, name: &str) -> bool {
        if !name.contains("::") && name != "xlib" {
            false
        } else if let Some(ref defined) = self.defined {
            !((name.starts_with("crate::") && &name[7..] == defined) || name == defined)
        } else {
            true
        }
    }

    /// Declares that name should be available through its last path component.
    ///
    /// For example, if name is `X::Y::Z` then it will be available as `Z`.
    /// Uses defaults.
    pub fn add(&mut self, name: &str) {
        if !self.common_checks(name) {
            return;
        }
        if let Some(mut name) = self.strip_crate_name(name) {
            if name == "xlib" {
                name = if self.crate_name == "gdk_x11" {
                    // Dirty little hack to allow to have correct import for GDKX11.
                    Cow::Borrowed("x11::xlib")
                } else {
                    // gtk has a module named "xlib" which is why this hack is needed too.
                    Cow::Borrowed("crate::xlib")
                };
            }
            let defaults = &self.defaults;
            let entry = self
                .map
                .entry(name.into_owned())
                .or_insert_with(|| defaults.clone());
            entry.update_version(self.defaults.version);
            entry.update_constraints(&self.defaults.constraints);
        }
    }

    /// Declares that name should be available through its last path component.
    ///
    /// For example, if name is `X::Y::Z` then it will be available as `Z`.
    pub fn add_with_version(&mut self, name: &str, version: Option<Version>) {
        if !self.common_checks(name) {
            return;
        }
        if let Some(name) = self.strip_crate_name(name) {
            let entry = self
                .map
                .entry(name.into_owned())
                .or_insert(ImportConditions {
                    version,
                    constraints: Vec::new(),
                });
            entry.update_version(version);
            // Since there is no constraint on this import, if any constraint
            // is present, we can just remove it.
            entry.constraints.clear();
        }
    }

    /// Declares that name should be available through its last path component and provides
    /// an optional feature constraint.
    ///
    /// For example, if name is `X::Y::Z` then it will be available as `Z`.
    pub fn add_with_constraint(
        &mut self,
        name: &str,
        version: Option<Version>,
        constraint: Option<&str>,
    ) {
        if !self.common_checks(name) {
            return;
        }
        if let Some(name) = self.strip_crate_name(name) {
            let entry = if let Some(constraint) = constraint {
                let constraint = String::from(constraint);
                let entry = self
                    .map
                    .entry(name.into_owned())
                    .or_insert(ImportConditions {
                        version,
                        constraints: vec![constraint.clone()],
                    });
                entry.add_constraint(constraint);
                entry
            } else {
                let entry = self
                    .map
                    .entry(name.into_owned())
                    .or_insert(ImportConditions {
                        version,
                        constraints: Vec::new(),
                    });
                // Since there is no constraint on this import, if any constraint
                // is present, we can just remove it.
                entry.constraints.clear();
                entry
            };
            entry.update_version(version);
        }
    }

    /// Declares that name should be available through its full path.
    ///
    /// For example, if name is `X::Y` then it will be available as `X::Y`.
    pub fn add_used_type(&mut self, used_type: &str) {
        if let Some(i) = used_type.find("::") {
            if i == 0 {
                self.add(&used_type[2..]);
            } else {
                self.add(&used_type[..i]);
            }
        } else {
            self.add(&format!("crate::{}", used_type));
        }
    }

    pub fn add_used_types(&mut self, used_types: &[String]) {
        for s in used_types {
            self.add_used_type(s);
        }
    }

    /// Declares that name should be available through its full path.
    ///
    /// For example, if name is `X::Y` then it will be available as `X::Y`.
    pub fn add_used_type_with_version(&mut self, used_type: &str, version: Option<Version>) {
        if let Some(i) = used_type.find("::") {
            if i == 0 {
                self.add_with_version(&used_type[2..], version);
            } else {
                self.add_with_version(&used_type[..i], version);
            }
        } else {
            self.add_with_version(&format!("crate::{}", used_type), version);
        }
    }

    /// Tries to strip crate name prefix from given name.
    ///
    /// Returns `None` if name matches crate name exactly. Otherwise returns
    /// name with crate name prefix stripped or full name if there was no match.
    fn strip_crate_name<'a>(&self, name: &'a str) -> Option<Cow<'a, str>> {
        let prefix = &self.crate_name;
        if !name.starts_with(prefix) {
            return Some(Cow::Borrowed(name));
        }
        let rest = &name[prefix.len()..];
        if rest.is_empty() {
            None
        } else if rest.starts_with("::") {
            Some(Cow::Owned(format!("crate{}", rest)))
        } else {
            // It was false positive, return the whole name.
            Some(Cow::Borrowed(name))
        }
    }

    pub fn iter(&self) -> IntoIter<(&String, &ImportConditions)> {
        let mut imports = self.map.iter().collect::<Vec<_>>();
        imports.sort_by(compare_imports);
        imports.into_iter()
    }
}

pub struct ImportsWithDefault<'a> {
    imports: &'a mut Imports,
}

impl<'a> ImportsWithDefault<'a> {
    fn new(imports: &'a mut Imports) -> Self {
        Self { imports }
    }
}

impl Drop for ImportsWithDefault<'_> {
    fn drop(&mut self) {
        self.imports.reset_defaults()
    }
}

impl Deref for ImportsWithDefault<'_> {
    type Target = Imports;
    fn deref(&self) -> &Self::Target {
        self.imports
    }
}

impl DerefMut for ImportsWithDefault<'_> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.imports
    }
}

#[derive(Clone, Debug, Default)]
pub struct ImportConditions {
    pub version: Option<Version>,
    pub constraints: Vec<String>,
}

impl ImportConditions {
    fn clear(&mut self) {
        self.version = None;
        self.constraints.clear();
    }

    fn update_version(&mut self, version: Option<Version>) {
        if version < self.version {
            self.version = version;
        }
    }

    fn add_constraint(&mut self, constraint: String) {
        // If the import is already present but doesn't have any constraint,
        // we don't want to add one.
        if self.constraints.is_empty() {
            return;
        }
        // Otherwise, we just check if the constraint
        // is already present or not before adding it.
        if !self.constraints.iter().any(|x| x == &constraint) {
            self.constraints.push(constraint);
        }
    }

    fn update_constraints(&mut self, constraints: &[String]) {
        // If the import is already present but doesn't have any constraint,
        // we don't want to add one.
        if self.constraints.is_empty() {
            return;
        }
        if constraints.is_empty() {
            // Since there is no constraint on this import, if any constraint
            // is present, we can just remove it.
            self.constraints.clear();
        } else {
            // Otherwise, we just check if the constraint
            // is already present or not before adding it.
            for constraint in constraints {
                if !self.constraints.iter().any(|x| x == constraint) {
                    self.constraints.push(constraint.clone());
                }
            }
        }
    }
}

fn make_crate_name(gir: &Library) -> String {
    if gir.is_glib_crate() {
        crate_name("GLib")
    } else {
        crate_name(gir.namespace(namespaces::MAIN).name.as_str())
    }
}
<|endoftext|>


<commit_before>// Used internally for evalulating expressions
// Note: for now, negative is falsy and positive (including zero) is truthy

use interpreter::lexer::Token;
use interpreter::lexer;
use std::collections::HashMap;
use std::collections::hashmap::{Occupied, Vacant};
use std::num;

#[deriving(Show)]
enum ExprToken {
	Op(Operator),
	Value(f32),
	Var(uint),
	LParen,
	RParen,
}

#[deriving(Show)]
enum Operator {
	Add,
	Sub,
	Mul,
	Div,
	Exp,
	Mod,
	Neg,
	Less,
	Greater,
	Equ,
	NotEqu,
	ApproxEqu,
	Not,
	And,
	Or,
	Xor,
	GreaterEqual,
	LessEqual,
}

fn precedence(op: Operator) -> int {
	match op {
		Equ | NotEqu | ApproxEqu => 1,
		Less | Greater | GreaterEqual | LessEqual => 2,
		And | Or | Xor => 0,
		Add => 3,
		Sub => 3,
		Mul => 4,
		Div => 4,
		Mod => 4,
		Neg => 6,
		Not => 6,
		Exp => 6,
	}
}

fn nargs(op: Operator) -> uint {
	match op {
		Neg | Not => 1,
		_ => 2,
	}
}

#[deriving(PartialEq)]
enum Associativity {
	Left,
	Right,
}

fn associativity(op: Operator) -> Associativity {
	match op {
		Exp => Right,
		_ => Left,
	}
}

#[deriving(Show)]
pub struct Expression<'a> {
	rpn: Vec<ExprToken>, // reverse polish notation
	var_values: Vec<f32>, // values of vars by id
	vars: HashMap<&'a str, uint>, // map of var name -> var id
	value: Option<f32>, // holds the last value of the expression, if it's been evaluated and vars have not been updated
}

impl<'a> Expression<'a> {
	// converts a token slice from the lexer into an expression that can be evaluated
	pub fn new<'a>(tok: Vec<Token<'a>>) -> Expression {
		let mut vars: HashMap<&'a str, uint> = HashMap::new();
		let out: Vec<ExprToken> = tok.iter().filter_map(|&t| {
			match t {
				// Try to parse constants as f32. subject to maybe change but probably not.
				lexer::Const(v) => {
					if let Some(x) = from_str::<f32>(v) {
						Some(Value(x))
					} else {
						fail!("invalid const, regex must be broken!");
					}
				},
				lexer::Operator(v) => {
					Some(Op(match v {
						"+" => Add,
						"-" => Sub,
						"*" => Mul,
						"/" => Div,
						"^" => Exp,
						"%" => Mod,
						"==" => Equ,
						"!=" => NotEqu,
						"~=" => ApproxEqu,
						"<" => Less,
						">" => Greater,
						"<=" => LessEqual,
						">=" => GreaterEqual,
						"!" => Not,
						"&&" => And,
						"||" => Or,
						"^^" => Xor,
						_ => fail!("unexpected operator"),
					}))
				},
				lexer::Paren(v) => {
					if v == "(" {
						Some(LParen)
					} else if v == ")" {
						Some(RParen)
					} else {
						None
					}
				},
				// An identifier in the context of an expression is always a variable (TODO FALSE!!)
				lexer::Ident(v) => {
					let nv = vars.len();
					match vars.entry(v) {
						Occupied(entry) => {
							Some(Var(*entry.get()))
						},
						Vacant(entry) => {
							entry.set(nv);
							Some(Var(nv))
						},
					}
				},
				// Discard whitesapce
				lexer::Whitespace(_) | lexer::Newline => {
					None
				},
				_ => {
					fail!("not expression!");
				}
			}
		}).collect();

		// Handle special case with unary minus
		// If an subtraction operator is preceded by another operator, left paren, or the start of the
		// expression, make it a negation operator.
		let out = out.iter().enumerate().map(|(i, &v)| {
			match v {
				Op(Sub) => {
					if i == 0 || match out[i-1] { Op(_) | LParen => true, _ => false } {
						Op(Neg)
					} else {
						v
					}
				},
				_ => v,
			}
		}).collect();

		Expression {
			rpn: shunting_yard(out),
			var_values: Vec::from_elem(vars.len(), 0f32),
			vars: vars,
			value: None,
		}
	}

	// http://en.wikipedia.org/wiki/Reverse_Polish_notation
	pub fn eval(&mut self) -> f32 {
		// If the expression is constant, return it's value
		if let Some(v) = self.value {
			return v;
		}

		let mut stack: Vec<f32> = Vec::new();
		for &t in self.rpn.iter() {
			match t {
				Value(v) => {
					stack.push(v);
				},
				Op(op) => {
					let n = nargs(op);
					if stack.len() < n {
						fail!("invalid expression: not enough args for operator {}", op);
					}
					let args: Vec<f32> = range(0, n).map(|_| stack.pop().unwrap()).collect();
					stack.push(match op {
						Add => {
							args[1] + args[0]
						},
						Sub => {
							args[1] - args[0]
						},
						Mul => {
							args[1] * args[0]
						},
						Div => {
							args[1] / args[0]
						},
						Exp => {
							args[1].powf(args[0])
						},
						Mod => {
							let c = num::abs(args[1]/args[0]).fract()*num::abs(args[0]);
							if args[1] < 0f32 { -c } else { c }
						},
						Neg => {
							-args[0]
						},
						Not => {
							if args[0] > 0f32 { -1f32 } else { 1f32 }
						},
						Less => {
							if args[1] < args[0] { 1f32 } else { -1f32 }
						},
						Greater => {
							if args[1] > args[0] { 1f32 } else { -1f32 }
						},
						LessEqual => {
							if args[1] <= args[0] { 1f32 } else { -1f32 }
						},
						GreaterEqual => {
							if args[1] >= args[0] { 1f32 } else { -1f32 }
						},
						Equ => {
							if args[1] == args[0] { 1f32 } else { -1f32 }
						},
						NotEqu => {
							if args[1] != args[0] { 1f32 } else { -1f32 }
						},
						ApproxEqu => {
							if num::abs(args[1] - args[0]) < 0.0001 { 1f32 } else { -1f32 }
						},
						And => {
							if args[1] > 0f32 && args[0] > 0f32 { 1f32 } else { -1f32 }
						},
						Or => {
							if args[1] > 0f32 || args[0] > 0f32 { 1f32 } else { -1f32 }
						},
						Xor => {
							if (args[1] > 0f32) ^ (args[0] > 0f32) { 1f32 } else { -1f32 }
						},
					});
				},
				Var(id) => {
					stack.push(self.var_values[id]);
				},
				_ => fail!("unexpected token in expression"),
			};
		}
		match stack.len() {
			1 => {
				let val = stack.pop().unwrap();
				self.value = Some(val);
				val
			},
			0 => {
				fail!("zero values in expression");
			},
			_ => {
				fail!("to many values in expression");
			},
		}
	}

	// Returns a uint identifier which can be passed to set_var to quickly update the value of a
	// variable.
	pub fn var_id(&self, var: &'a str) -> Option<uint> {
		match self.vars.find(&var) {
			Some(v) => Some(*v),
			None => None,
		}
	}

	// Sets a variable to an id retrieved from var_id. Return value indicates whether it was set
	// sucessfully or not.
	pub fn set_var(&mut self, var_id: uint, value: f32) -> bool {
		if var_id > self.var_values.len() {
			return false;
		}
		self.var_values[var_id] = value;
		self.value = None; // Setting a var clears evaled value
		true
	}

	// Attempts to set a variable by name. If the variable does not exist, nothing is done.
	pub fn try_set_var(&mut self, var: &'a str, value: f32) {
		if let Some(id) = self.var_id(var) {
			self.set_var(id, value);
		}
	}
}

// http://en.wikipedia.org/wiki/Shunting-yard_algorithm
fn shunting_yard(tok: Vec<ExprToken>) -> Vec<ExprToken> {
	let mut out = Vec::new();
	let mut stack: Vec<ExprToken> = Vec::new();
	for &t in tok.iter() {
		match t {
			Value(_) | Var(_) => {
				out.push(t);
			}
			Op(op1) => {
				while stack.len() > 0 {
					let top = stack[stack.len()-1];
					match top {
						Op(op2) => {
							if (associativity(op1) == Left && precedence(op1) <= precedence(op2)) ||
								(precedence(op1) < precedence(op2)) {
								stack.pop();
								out.push(top);
							} else {
								break;
							}
						},
						_ => break,
					}
				}
				stack.push(t);
			},
			LParen => {
				stack.push(t);
			},
			RParen => {
				let mut foundleft = false;
				while stack.len() > 0 {
					let op = stack.pop().unwrap();
					match op {
						Op(_) => {
							out.push(op);
						},
						LParen => {
							foundleft = true;
							break;
						},
						_ => fail!("not sure how this can happen, but maybe"),
					}
				}
				if !foundleft {
					fail!("mismatched parens");
				}
			},
		};
	}
	while stack.len() > 0 {
		let top = stack[stack.len()-1];
		match top {
			Op(_) => {
				stack.pop();
				out.push(top);
			},
			LParen | RParen => fail!("mismatched parens"),
			_ => fail!("non operator on stack!"),
		}
	}
	out
}
<commit_msg>Convert some ifs and elses to a nice concise match<commit_after>// Used internally for evalulating expressions
// Note: for now, negative is falsy and positive (including zero) is truthy

use interpreter::lexer::Token;
use interpreter::lexer;
use std::collections::HashMap;
use std::collections::hashmap::{Occupied, Vacant};
use std::num;

#[deriving(Show)]
enum ExprToken {
	Op(Operator),
	Value(f32),
	Var(uint),
	LParen,
	RParen,
}

#[deriving(Show)]
enum Operator {
	Add,
	Sub,
	Mul,
	Div,
	Exp,
	Mod,
	Neg,
	Less,
	Greater,
	Equ,
	NotEqu,
	ApproxEqu,
	Not,
	And,
	Or,
	Xor,
	GreaterEqual,
	LessEqual,
}

fn precedence(op: Operator) -> int {
	match op {
		Equ | NotEqu | ApproxEqu => 1,
		Less | Greater | GreaterEqual | LessEqual => 2,
		And | Or | Xor => 0,
		Add => 3,
		Sub => 3,
		Mul => 4,
		Div => 4,
		Mod => 4,
		Neg => 6,
		Not => 6,
		Exp => 6,
	}
}

fn nargs(op: Operator) -> uint {
	match op {
		Neg | Not => 1,
		_ => 2,
	}
}

#[deriving(PartialEq)]
enum Associativity {
	Left,
	Right,
}

fn associativity(op: Operator) -> Associativity {
	match op {
		Exp => Right,
		_ => Left,
	}
}

#[deriving(Show)]
pub struct Expression<'a> {
	rpn: Vec<ExprToken>, // reverse polish notation
	var_values: Vec<f32>, // values of vars by id
	vars: HashMap<&'a str, uint>, // map of var name -> var id
	value: Option<f32>, // holds the last value of the expression, if it's been evaluated and vars have not been updated
}

impl<'a> Expression<'a> {
	// converts a token slice from the lexer into an expression that can be evaluated
	pub fn new<'a>(tok: Vec<Token<'a>>) -> Expression {
		let mut vars: HashMap<&'a str, uint> = HashMap::new();
		let out: Vec<ExprToken> = tok.iter().filter_map(|&t| {
			match t {
				// Try to parse constants as f32. subject to maybe change but probably not.
				lexer::Const(v) => {
					if let Some(x) = from_str::<f32>(v) {
						Some(Value(x))
					} else {
						fail!("invalid const, regex must be broken!");
					}
				},
				lexer::Operator(v) => {
					Some(Op(match v {
						"+" => Add,
						"-" => Sub,
						"*" => Mul,
						"/" => Div,
						"^" => Exp,
						"%" => Mod,
						"==" => Equ,
						"!=" => NotEqu,
						"~=" => ApproxEqu,
						"<" => Less,
						">" => Greater,
						"<=" => LessEqual,
						">=" => GreaterEqual,
						"!" => Not,
						"&&" => And,
						"||" => Or,
						"^^" => Xor,
						_ => fail!("unexpected operator"),
					}))
				},
				lexer::Paren(v) => {
                    match v {
                        "(" => Some(LParen),
                        ")" => Some(RParen),
                        _ => None,
                    }
				},
				// An identifier in the context of an expression is always a variable (TODO FALSE!!)
				lexer::Ident(v) => {
					let nv = vars.len();
					match vars.entry(v) {
						Occupied(entry) => {
							Some(Var(*entry.get()))
						},
						Vacant(entry) => {
							entry.set(nv);
							Some(Var(nv))
						},
					}
				},
				// Discard whitesapce
				lexer::Whitespace(_) | lexer::Newline => {
					None
				},
				_ => {
					fail!("not expression!");
				}
			}
		}).collect();

		// Handle special case with unary minus
		// If an subtraction operator is preceded by another operator, left paren, or the start of the
		// expression, make it a negation operator.
		let out = out.iter().enumerate().map(|(i, &v)| {
			match v {
				Op(Sub) => {
					if i == 0 || match out[i-1] { Op(_) | LParen => true, _ => false } {
						Op(Neg)
					} else {
						v
					}
				},
				_ => v,
			}
		}).collect();

		Expression {
			rpn: shunting_yard(out),
			var_values: Vec::from_elem(vars.len(), 0f32),
			vars: vars,
			value: None,
		}
	}

	// http://en.wikipedia.org/wiki/Reverse_Polish_notation
	pub fn eval(&mut self) -> f32 {
		// If the expression is constant, return it's value
		if let Some(v) = self.value {
			return v;
		}

		let mut stack: Vec<f32> = Vec::new();
		for &t in self.rpn.iter() {
			match t {
				Value(v) => {
					stack.push(v);
				},
				Op(op) => {
					let n = nargs(op);
					if stack.len() < n {
						fail!("invalid expression: not enough args for operator {}", op);
					}
					let args: Vec<f32> = range(0, n).map(|_| stack.pop().unwrap()).collect();
					stack.push(match op {
						Add => {
							args[1] + args[0]
						},
						Sub => {
							args[1] - args[0]
						},
						Mul => {
							args[1] * args[0]
						},
						Div => {
							args[1] / args[0]
						},
						Exp => {
							args[1].powf(args[0])
						},
						Mod => {
							let c = num::abs(args[1]/args[0]).fract()*num::abs(args[0]);
							if args[1] < 0f32 { -c } else { c }
						},
						Neg => {
							-args[0]
						},
						Not => {
							if args[0] > 0f32 { -1f32 } else { 1f32 }
						},
						Less => {
							if args[1] < args[0] { 1f32 } else { -1f32 }
						},
						Greater => {
							if args[1] > args[0] { 1f32 } else { -1f32 }
						},
						LessEqual => {
							if args[1] <= args[0] { 1f32 } else { -1f32 }
						},
						GreaterEqual => {
							if args[1] >= args[0] { 1f32 } else { -1f32 }
						},
						Equ => {
							if args[1] == args[0] { 1f32 } else { -1f32 }
						},
						NotEqu => {
							if args[1] != args[0] { 1f32 } else { -1f32 }
						},
						ApproxEqu => {
							if num::abs(args[1] - args[0]) < 0.0001 { 1f32 } else { -1f32 }
						},
						And => {
							if args[1] > 0f32 && args[0] > 0f32 { 1f32 } else { -1f32 }
						},
						Or => {
							if args[1] > 0f32 || args[0] > 0f32 { 1f32 } else { -1f32 }
						},
						Xor => {
							if (args[1] > 0f32) ^ (args[0] > 0f32) { 1f32 } else { -1f32 }
						},
					});
				},
				Var(id) => {
					stack.push(self.var_values[id]);
				},
				_ => fail!("unexpected token in expression"),
			};
		}
		match stack.len() {
			1 => {
				let val = stack.pop().unwrap();
				self.value = Some(val);
				val
			},
			0 => {
				fail!("zero values in expression");
			},
			_ => {
				fail!("to many values in expression");
			},
		}
	}

	// Returns a uint identifier which can be passed to set_var to quickly update the value of a
	// variable.
	pub fn var_id(&self, var: &'a str) -> Option<uint> {
		match self.vars.find(&var) {
			Some(v) => Some(*v),
			None => None,
		}
	}

	// Sets a variable to an id retrieved from var_id. Return value indicates whether it was set
	// sucessfully or not.
	pub fn set_var(&mut self, var_id: uint, value: f32) -> bool {
		if var_id > self.var_values.len() {
			return false;
		}
		self.var_values[var_id] = value;
		self.value = None; // Setting a var clears evaled value
		true
	}

	// Attempts to set a variable by name. If the variable does not exist, nothing is done.
	pub fn try_set_var(&mut self, var: &'a str, value: f32) {
		if let Some(id) = self.var_id(var) {
			self.set_var(id, value);
		}
	}
}

// http://en.wikipedia.org/wiki/Shunting-yard_algorithm
fn shunting_yard(tok: Vec<ExprToken>) -> Vec<ExprToken> {
	let mut out = Vec::new();
	let mut stack: Vec<ExprToken> = Vec::new();
	for &t in tok.iter() {
		match t {
			Value(_) | Var(_) => {
				out.push(t);
			}
			Op(op1) => {
				while stack.len() > 0 {
					let top = stack[stack.len()-1];
					match top {
						Op(op2) => {
							if (associativity(op1) == Left && precedence(op1) <= precedence(op2)) ||
								(precedence(op1) < precedence(op2)) {
								stack.pop();
								out.push(top);
							} else {
								break;
							}
						},
						_ => break,
					}
				}
				stack.push(t);
			},
			LParen => {
				stack.push(t);
			},
			RParen => {
				let mut foundleft = false;
				while stack.len() > 0 {
					let op = stack.pop().unwrap();
					match op {
						Op(_) => {
							out.push(op);
						},
						LParen => {
							foundleft = true;
							break;
						},
						_ => fail!("not sure how this can happen, but maybe"),
					}
				}
				if !foundleft {
					fail!("mismatched parens");
				}
			},
		};
	}
	while stack.len() > 0 {
		let top = stack[stack.len()-1];
		match top {
			Op(_) => {
				stack.pop();
				out.push(top);
			},
			LParen | RParen => fail!("mismatched parens"),
			_ => fail!("non operator on stack!"),
		}
	}
	out
}
<|endoftext|>


<commit_before>use parser;
use interpreter::Interpreter;
use interpreter::StatementResult;
use value::Value;
use value::Number;

fn run_and_get_last_value(code: &str) -> Value {
    match run_and_get_last_result(code) {
        StatementResult::Value(ref v) => v.clone(),
        _ => panic!("Cannot unwrap value"),
    }
}

fn run_and_get_last_result(code: &str) -> StatementResult {
    let ast = parser::program(code);
    match ast {
        Ok(ast) => {
            let mut machine = Interpreter::new();
            machine.run_ast_as_program(&ast).unwrap().unwrap()
        }
        Err(_) => panic!("{:?}", ast),
    }
}

#[test]
fn literal_value_int() {
    assert_eq!(run_and_get_last_value("7;"),
               Value::Number(Number::Integer(7)));
}

#[test]
fn literal_value_float() {
    assert_eq!(run_and_get_last_value("7.0;"),
               Value::Number(Number::Float(7.0)));
}

#[test]
fn literal_value_bool_true() {
    assert_eq!(run_and_get_last_value("true;"), Value::Bool(true));
}

#[test]
fn literal_value_bool_false() {
    assert_eq!(run_and_get_last_value("false;"), Value::Bool(false));
}

#[test]
fn variable_reassignment_with_type_change() {
    assert_eq!(run_and_get_last_value("var x = 10; x = false; x;"),
               Value::Bool(false));
}

#[test]
fn arithmetic_1() {
    assert_eq!(run_and_get_last_value("(1 + 3 / (1 + 1)) * 12 - 1;"),
               Value::Number(Number::Float(29.0)));
}

#[test]
fn arithmetic_2() {
    assert_eq!(run_and_get_last_value("3 - 5 + 12 / 4 - 1;"),
               Value::Number(Number::Float(0.0)));
}

#[test]
fn arithmetic_3() {
    assert_eq!(run_and_get_last_value("10 // 3 + 5 - 12;"),
               Value::Number(Number::Integer(-4)));
}

#[test]
fn arithmetic_4() {
    assert_eq!(run_and_get_last_value("-10 / 4 + -5 - 12;"),
               Value::Number(Number::Float(-19.5)));
}

#[test]
fn i64_support() {
    assert_eq!(run_and_get_last_value("9223372036854775807;"),
               Value::Number(Number::Integer(9223372036854775807)));
}

#[test]
fn f64_support() {
    assert_eq!(run_and_get_last_value("1234567890.012345678;"),
               Value::Number(Number::Float(1234567890.012345678)));
}

#[test]
fn logical_and() {
    assert_eq!(run_and_get_last_value("true and true;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("false and true;"),
               Value::Bool(false));
    assert_eq!(run_and_get_last_value("true and false;"),
               Value::Bool(false));
    assert_eq!(run_and_get_last_value("false and false;"),
               Value::Bool(false));

    assert_eq!(run_and_get_last_value("2 and 1;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("3.0 and 22.5;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("5 and 0;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("5.6 and 0.0;"), Value::Bool(false));
}

#[test]
fn logical_or() {
    assert_eq!(run_and_get_last_value("true or true;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("false or true;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("true or false;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("false or false;"),
               Value::Bool(false));

    assert_eq!(run_and_get_last_value("2 or 1;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("3.0 or 22.5;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("5 or 0;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("0 or 0.0;"), Value::Bool(false));
}

#[test]
fn logical_not() {
    assert_eq!(run_and_get_last_value("not true;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("not false;"), Value::Bool(true));

    assert_eq!(run_and_get_last_value("not 5;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("not 0;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("not 0.0;"), Value::Bool(true));
}

#[test]
fn comparisons() {
    assert_eq!(run_and_get_last_value("1 < 2;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("1 > 2;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("1.0 < 2.0;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("1.0 > 2.0;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("4 <= 4;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("2 >= 2;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("3 == 3;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("7 == 7.0;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("7 == 7.1;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("true == 1;"), Value::Bool(false));
}

#[test]
fn variable_declaration() {
    assert_eq!(run_and_get_last_value("var x = 5; x;"),
               Value::Number(Number::Integer(5)));
}

#[test]
fn variable_reassignment() {
    assert_eq!(run_and_get_last_value("var x = 10; x = 42; x;"),
               Value::Number(Number::Integer(42)));
}

#[test]
fn id_in_expr() {
    assert_eq!(run_and_get_last_value("var x = 5; x / 2 + x;"),
               Value::Number(Number::Float(7.5)));
}

#[test]
fn if_expr_true() {
    assert_eq!(run_and_get_last_value("var x = 5; if 1 < 2 { x = 6; } else { x = 7; } x;"),
               Value::Number(Number::Integer(6)));
}

#[test]
fn if_expr_false() {
    assert_eq!(run_and_get_last_value("var x = 5; if 1 > 2 { x = 6; } else { x = 7; } x;"),
               Value::Number(Number::Integer(7)));
}

#[test]
fn loop_and_break() {
    assert_eq!(run_and_get_last_value("var x = 5; loop { if x > 20 { break; } x = x + 5; } x;"),
               Value::Number(Number::Integer(25)));
}

#[test]
fn block_env() {
    assert_eq!(run_and_get_last_value("var x = 5; { var x = 10; x = 20; } x;"),
               Value::Number(Number::Integer(5)));
    assert_eq!(run_and_get_last_value("var x = 5; { var x = 10; x = 20; x; }"),
               Value::Number(Number::Integer(20)));
}

#[test]
fn test_println_runs() {
    assert_eq!(run_and_get_last_result("println();"), StatementResult::None);
    assert_eq!(run_and_get_last_result("println(5, true,);"),
               StatementResult::None);
}

#[test]
fn test_factorial() {
    assert_eq!(
        run_and_get_last_value("fn factorial(n) { if n < 2 { return 1; } return n * factorial(n - 1); } factorial(5); "),
        Value::Number(Number::Integer(120))
    );
    assert_eq!(
        run_and_get_last_value("fn factorial(n) { if n < 2 { return 1; } return n * factorial(n - 1); } factorial(10); "),
        Value::Number(Number::Integer(3628800))
    );
}
<commit_msg>Add curried_add test<commit_after>use parser;
use interpreter::Interpreter;
use interpreter::StatementResult;
use value::Value;
use value::Number;

fn run_and_get_last_value(code: &str) -> Value {
    match run_and_get_last_result(code) {
        StatementResult::Value(ref v) => v.clone(),
        _ => panic!("Cannot unwrap value"),
    }
}

fn run_and_get_last_result(code: &str) -> StatementResult {
    let ast = parser::program(code);
    match ast {
        Ok(ast) => {
            let mut machine = Interpreter::new();
            machine.run_ast_as_program(&ast).unwrap().unwrap()
        }
        Err(_) => panic!("{:?}", ast),
    }
}

#[test]
fn literal_value_int() {
    assert_eq!(run_and_get_last_value("7;"),
               Value::Number(Number::Integer(7)));
}

#[test]
fn literal_value_float() {
    assert_eq!(run_and_get_last_value("7.0;"),
               Value::Number(Number::Float(7.0)));
}

#[test]
fn literal_value_bool_true() {
    assert_eq!(run_and_get_last_value("true;"), Value::Bool(true));
}

#[test]
fn literal_value_bool_false() {
    assert_eq!(run_and_get_last_value("false;"), Value::Bool(false));
}

#[test]
fn variable_reassignment_with_type_change() {
    assert_eq!(run_and_get_last_value("var x = 10; x = false; x;"),
               Value::Bool(false));
}

#[test]
fn arithmetic_1() {
    assert_eq!(run_and_get_last_value("(1 + 3 / (1 + 1)) * 12 - 1;"),
               Value::Number(Number::Float(29.0)));
}

#[test]
fn arithmetic_2() {
    assert_eq!(run_and_get_last_value("3 - 5 + 12 / 4 - 1;"),
               Value::Number(Number::Float(0.0)));
}

#[test]
fn arithmetic_3() {
    assert_eq!(run_and_get_last_value("10 // 3 + 5 - 12;"),
               Value::Number(Number::Integer(-4)));
}

#[test]
fn arithmetic_4() {
    assert_eq!(run_and_get_last_value("-10 / 4 + -5 - 12;"),
               Value::Number(Number::Float(-19.5)));
}

#[test]
fn i64_support() {
    assert_eq!(run_and_get_last_value("9223372036854775807;"),
               Value::Number(Number::Integer(9223372036854775807)));
}

#[test]
fn f64_support() {
    assert_eq!(run_and_get_last_value("1234567890.012345678;"),
               Value::Number(Number::Float(1234567890.012345678)));
}

#[test]
fn logical_and() {
    assert_eq!(run_and_get_last_value("true and true;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("false and true;"),
               Value::Bool(false));
    assert_eq!(run_and_get_last_value("true and false;"),
               Value::Bool(false));
    assert_eq!(run_and_get_last_value("false and false;"),
               Value::Bool(false));

    assert_eq!(run_and_get_last_value("2 and 1;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("3.0 and 22.5;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("5 and 0;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("5.6 and 0.0;"), Value::Bool(false));
}

#[test]
fn logical_or() {
    assert_eq!(run_and_get_last_value("true or true;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("false or true;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("true or false;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("false or false;"),
               Value::Bool(false));

    assert_eq!(run_and_get_last_value("2 or 1;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("3.0 or 22.5;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("5 or 0;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("0 or 0.0;"), Value::Bool(false));
}

#[test]
fn logical_not() {
    assert_eq!(run_and_get_last_value("not true;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("not false;"), Value::Bool(true));

    assert_eq!(run_and_get_last_value("not 5;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("not 0;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("not 0.0;"), Value::Bool(true));
}

#[test]
fn comparisons() {
    assert_eq!(run_and_get_last_value("1 < 2;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("1 > 2;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("1.0 < 2.0;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("1.0 > 2.0;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("4 <= 4;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("2 >= 2;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("3 == 3;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("7 == 7.0;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("7 == 7.1;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("true == 1;"), Value::Bool(false));
}

#[test]
fn variable_declaration() {
    assert_eq!(run_and_get_last_value("var x = 5; x;"),
               Value::Number(Number::Integer(5)));
}

#[test]
fn variable_reassignment() {
    assert_eq!(run_and_get_last_value("var x = 10; x = 42; x;"),
               Value::Number(Number::Integer(42)));
}

#[test]
fn id_in_expr() {
    assert_eq!(run_and_get_last_value("var x = 5; x / 2 + x;"),
               Value::Number(Number::Float(7.5)));
}

#[test]
fn if_expr_true() {
    assert_eq!(run_and_get_last_value("var x = 5; if 1 < 2 { x = 6; } else { x = 7; } x;"),
               Value::Number(Number::Integer(6)));
}

#[test]
fn if_expr_false() {
    assert_eq!(run_and_get_last_value("var x = 5; if 1 > 2 { x = 6; } else { x = 7; } x;"),
               Value::Number(Number::Integer(7)));
}

#[test]
fn loop_and_break() {
    assert_eq!(run_and_get_last_value("var x = 5; loop { if x > 20 { break; } x = x + 5; } x;"),
               Value::Number(Number::Integer(25)));
}

#[test]
fn block_env() {
    assert_eq!(run_and_get_last_value("var x = 5; { var x = 10; x = 20; } x;"),
               Value::Number(Number::Integer(5)));
    assert_eq!(run_and_get_last_value("var x = 5; { var x = 10; x = 20; x; }"),
               Value::Number(Number::Integer(20)));
}

#[test]
fn test_println_runs() {
    assert_eq!(run_and_get_last_result("println();"), StatementResult::None);
    assert_eq!(run_and_get_last_result("println(5, true,);"),
               StatementResult::None);
}

#[test]
fn test_factorial() {
    assert_eq!(
        run_and_get_last_value("fn factorial(n) { if n < 2 { return 1; } return n * factorial(n - 1); } factorial(5); "),
        Value::Number(Number::Integer(120))
    );
    assert_eq!(
        run_and_get_last_value("fn factorial(n) { if n < 2 { return 1; } return n * factorial(n - 1); } factorial(10); "),
        Value::Number(Number::Integer(3628800))
    );
}

#[test]
fn test_curried_add() {
    let code = "fn addX(x) {
    return fn addXY(y) {
        return x + y;
    };
}

(addX(10))(20);
";
    assert_eq!(
        run_and_get_last_value(code), Value::Number(Number::Integer(30))
    );
}
<|endoftext|>


<commit_before>//! Useful stuff related to the spaceapi.

/// An ``Optional`` can contain ``Optional::Value<T>`` or ``Optional::Absent``.
/// It is similar to an ``Option``, but ``Optional::Absent`` means it will be
/// omitted when serialized, while ``None`` will be serialized to ``null``.
#[derive(Debug, Copy, Clone)]
pub enum Optional<T> {
    Value(T),
    Absent,
}

impl<T> Optional<T> {

    /// Applies a function to the contained value or returns a default.  see
    /// [`std::option::Option<T>::map_or`](http://doc.rust-lang.org/std/option/enum.Option.html#method.map_or)
    pub fn map_or<U, F: FnOnce(T) -> U>(self, def: U, f: F) -> U {
        match self {
            Optional::Value(v) => f(v),
            Optional::Absent => def,
        }
    }

    pub fn as_mut<'r>(&'r mut self) -> Optional<&'r mut T> {
        match *self {
            Optional::Value(ref mut x) => Optional::Value(x),
            Optional::Absent => Optional::Absent
        }
    }

    pub fn as_ref<'r>(&'r self) -> Optional<&'r T> {
        match *self {
            Optional::Value(ref x) => Optional::Value(x),
            Optional::Absent => Optional::Absent
        }
    }

    pub fn is_absent(&self) -> bool {
        match *self {
            Optional::Absent => true,
            _ => false
        }
    }

}
<commit_msg>spaceapi: Add documentation to `Optional` methods<commit_after>//! Useful stuff related to the spaceapi.

/// An ``Optional`` can contain ``Optional::Value<T>`` or ``Optional::Absent``.
/// It is similar to an ``Option``, but ``Optional::Absent`` means it will be
/// omitted when serialized, while ``None`` will be serialized to ``null``.
#[derive(Debug, Copy, Clone)]
pub enum Optional<T> {
    Value(T),
    Absent,
}

impl<T> Optional<T> {

    /// Applies a function to the contained value or returns a default.  see
    /// [`std::option::Option<T>::map_or`](http://doc.rust-lang.org/std/option/enum.Option.html#method.map_or)
    pub fn map_or<U, F: FnOnce(T) -> U>(self, def: U, f: F) -> U {
        match self {
            Optional::Value(v) => f(v),
            Optional::Absent => def,
        }
    }

    /// Converts from `Optional<T>` to `Optional<&mut T>`
    pub fn as_mut<'r>(&'r mut self) -> Optional<&'r mut T> {
        match *self {
            Optional::Value(ref mut x) => Optional::Value(x),
            Optional::Absent => Optional::Absent
        }
    }

    /// Converts from `Optional<T>` to `Optional<&T>`
    pub fn as_ref<'r>(&'r self) -> Optional<&'r T> {
        match *self {
            Optional::Value(ref x) => Optional::Value(x),
            Optional::Absent => Optional::Absent
        }
    }

    /// Returns `true` if the optional is a `Absent` value
    ///
    /// # Examples
    ///
    /// ```
    /// # use spaceapi::utils::Optional;
    /// # use spaceapi::utils::Optional::{Value,Absent};
    /// let x: Optional<u32> = Value(2);
    /// assert_eq!(x.is_absent(), false);
    ///
    /// let x: Optional<u32> = Absent;
    /// assert_eq!(x.is_absent(), true);
    /// ```
    pub fn is_absent(&self) -> bool {
        match *self {
            Optional::Absent => true,
            _ => false
        }
    }

}
<|endoftext|>


<commit_before>#![cfg(feature = "rustls-tls")]

use std::{convert::TryInto, sync::Arc};

use rustls::{
    client::{ServerCertVerifier, WebPkiVerifier},
    Certificate, ClientConfig, OwnedTrustAnchor, RootCertStore,
};

use tokio::{fs::File, io::AsyncReadExt};

use rustls_pemfile::certs;
use tokio_rustls::TlsConnector;

use crate::{io::Endpoint, Result, SslOpts};

impl Endpoint {
    pub async fn make_secure(&mut self, domain: String, ssl_opts: SslOpts) -> Result<()> {
        if self.is_socket() {
            // won't secure socket connection
            return Ok(());
        }

        let mut root_store = RootCertStore::empty();
        root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| {
            OwnedTrustAnchor::from_subject_spki_name_constraints(
                ta.subject,
                ta.spki,
                ta.name_constraints,
            )
        }));

        if let Some(root_cert_path) = ssl_opts.root_cert_path() {
            let mut root_cert_data = vec![];
            let mut root_cert_file = File::open(root_cert_path).await?;
            root_cert_file.read_to_end(&mut root_cert_data).await?;

            let mut root_certs = Vec::new();
            for cert in certs(&mut &*root_cert_data)? {
                root_certs.push(Certificate(cert));
            }

            if root_certs.is_empty() && !root_cert_data.is_empty() {
                root_certs.push(Certificate(root_cert_data));
            }

            for cert in &root_certs {
                root_store.add(cert)?;
            }
        }

        let config_builder = ClientConfig::builder()
            .with_safe_defaults()
            .with_root_certificates(root_store.clone());

        let mut config = if let Some(identity) = ssl_opts.client_identity() {
            let (cert_chain, priv_key) = identity.load()?;
            config_builder.with_single_cert(cert_chain, priv_key)?
        } else {
            config_builder.with_no_client_auth()
        };

        let server_name = domain
            .as_str()
            .try_into()
            .map_err(|_| webpki::InvalidDnsNameError)?;
        let mut dangerous = config.dangerous();
        let web_pki_verifier = WebPkiVerifier::new(root_store, None);
        let dangerous_verifier = DangerousVerifier::new(
            ssl_opts.accept_invalid_certs(),
            ssl_opts.skip_domain_validation(),
            web_pki_verifier,
        );
        dangerous.set_certificate_verifier(Arc::new(dangerous_verifier));

        *self = match self {
            Endpoint::Plain(ref mut stream) => {
                let stream = stream.take().unwrap();

                let client_config = Arc::new(config);
                let tls_connector = TlsConnector::from(client_config);
                let connection = tls_connector.connect(server_name, stream).await?;

                Endpoint::Secure(connection)
            }
            Endpoint::Secure(_) => unreachable!(),
            #[cfg(unix)]
            Endpoint::Socket(_) => unreachable!(),
        };

        Ok(())
    }
}

struct DangerousVerifier {
    accept_invalid_certs: bool,
    skip_domain_validation: bool,
    verifier: WebPkiVerifier,
}

impl DangerousVerifier {
    fn new(
        accept_invalid_certs: bool,
        skip_domain_validation: bool,
        verifier: WebPkiVerifier,
    ) -> Self {
        Self {
            accept_invalid_certs,
            skip_domain_validation,
            verifier,
        }
    }
}

impl ServerCertVerifier for DangerousVerifier {
    fn verify_server_cert(
        &self,
        end_entity: &Certificate,
        intermediates: &[Certificate],
        server_name: &rustls::ServerName,
        scts: &mut dyn Iterator<Item = &[u8]>,
        ocsp_response: &[u8],
        now: std::time::SystemTime,
    ) -> std::result::Result<rustls::client::ServerCertVerified, rustls::Error> {
        if self.accept_invalid_certs {
            Ok(rustls::client::ServerCertVerified::assertion())
        } else {
            match self.verifier.verify_server_cert(
                end_entity,
                intermediates,
                server_name,
                scts,
                ocsp_response,
                now,
            ) {
                Ok(assertion) => Ok(assertion),
                Err(ref e)
                    if e.to_string().contains("CertNotValidForName")
                        && self.skip_domain_validation =>
                {
                    Ok(rustls::client::ServerCertVerified::assertion())
                }
                Err(e) => Err(e),
            }
        }
    }
}
<commit_msg>Fix rustls windows build<commit_after>#![cfg(feature = "rustls-tls")]

use std::{convert::TryInto, sync::Arc};

use rustls::{
    client::{ServerCertVerifier, WebPkiVerifier},
    Certificate, ClientConfig, OwnedTrustAnchor, RootCertStore,
};

use tokio::{fs::File, io::AsyncReadExt};

use rustls_pemfile::certs;
use tokio_rustls::TlsConnector;

use crate::{io::Endpoint, Result, SslOpts};

impl Endpoint {
    pub async fn make_secure(&mut self, domain: String, ssl_opts: SslOpts) -> Result<()> {
        #[cfg(unix)]
        if self.is_socket() {
            // won't secure socket connection
            return Ok(());
        }

        let mut root_store = RootCertStore::empty();
        root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| {
            OwnedTrustAnchor::from_subject_spki_name_constraints(
                ta.subject,
                ta.spki,
                ta.name_constraints,
            )
        }));

        if let Some(root_cert_path) = ssl_opts.root_cert_path() {
            let mut root_cert_data = vec![];
            let mut root_cert_file = File::open(root_cert_path).await?;
            root_cert_file.read_to_end(&mut root_cert_data).await?;

            let mut root_certs = Vec::new();
            for cert in certs(&mut &*root_cert_data)? {
                root_certs.push(Certificate(cert));
            }

            if root_certs.is_empty() && !root_cert_data.is_empty() {
                root_certs.push(Certificate(root_cert_data));
            }

            for cert in &root_certs {
                root_store.add(cert)?;
            }
        }

        let config_builder = ClientConfig::builder()
            .with_safe_defaults()
            .with_root_certificates(root_store.clone());

        let mut config = if let Some(identity) = ssl_opts.client_identity() {
            let (cert_chain, priv_key) = identity.load()?;
            config_builder.with_single_cert(cert_chain, priv_key)?
        } else {
            config_builder.with_no_client_auth()
        };

        let server_name = domain
            .as_str()
            .try_into()
            .map_err(|_| webpki::InvalidDnsNameError)?;
        let mut dangerous = config.dangerous();
        let web_pki_verifier = WebPkiVerifier::new(root_store, None);
        let dangerous_verifier = DangerousVerifier::new(
            ssl_opts.accept_invalid_certs(),
            ssl_opts.skip_domain_validation(),
            web_pki_verifier,
        );
        dangerous.set_certificate_verifier(Arc::new(dangerous_verifier));

        *self = match self {
            Endpoint::Plain(ref mut stream) => {
                let stream = stream.take().unwrap();

                let client_config = Arc::new(config);
                let tls_connector = TlsConnector::from(client_config);
                let connection = tls_connector.connect(server_name, stream).await?;

                Endpoint::Secure(connection)
            }
            Endpoint::Secure(_) => unreachable!(),
            #[cfg(unix)]
            Endpoint::Socket(_) => unreachable!(),
        };

        Ok(())
    }
}

struct DangerousVerifier {
    accept_invalid_certs: bool,
    skip_domain_validation: bool,
    verifier: WebPkiVerifier,
}

impl DangerousVerifier {
    fn new(
        accept_invalid_certs: bool,
        skip_domain_validation: bool,
        verifier: WebPkiVerifier,
    ) -> Self {
        Self {
            accept_invalid_certs,
            skip_domain_validation,
            verifier,
        }
    }
}

impl ServerCertVerifier for DangerousVerifier {
    fn verify_server_cert(
        &self,
        end_entity: &Certificate,
        intermediates: &[Certificate],
        server_name: &rustls::ServerName,
        scts: &mut dyn Iterator<Item = &[u8]>,
        ocsp_response: &[u8],
        now: std::time::SystemTime,
    ) -> std::result::Result<rustls::client::ServerCertVerified, rustls::Error> {
        if self.accept_invalid_certs {
            Ok(rustls::client::ServerCertVerified::assertion())
        } else {
            match self.verifier.verify_server_cert(
                end_entity,
                intermediates,
                server_name,
                scts,
                ocsp_response,
                now,
            ) {
                Ok(assertion) => Ok(assertion),
                Err(ref e)
                    if e.to_string().contains("CertNotValidForName")
                        && self.skip_domain_validation =>
                {
                    Ok(rustls::client::ServerCertVerified::assertion())
                }
                Err(e) => Err(e),
            }
        }
    }
}
<|endoftext|>