<commit_before>extern crate rand;

use config::*;
use rand::*;
use rand::distributions::{Weighted, WeightedChoice, IndependentSample};

pub struct Word {
    pub syllables : String,
    pub graphemes : String,
    pub syllable_rewrite_history : Vec<(String, String)>,
    pub grapheme_rewrite_history : Vec<(String, String)>,
    pub syllable_rejects : Vec<String>,
    pub grapheme_rejects : Vec<String>,
}

pub struct WordFactory {
    pub first_syllable_list : Vec<Weighted<String>>,
    pub last_syllable_list : Vec<Weighted<String>>,
    pub normal_syllable_list : Vec<Weighted<String>>,

    pub graphemes : Vec<(String, Vec<Weighted<String>>)>,

    pub rewrites : RewriteGroup,

    pub rejects : RejectGroup,

    pub settings : GenerateSettings,
}

pub trait WordGenerator {
//    fn new(
//        &mut self,
//        first_syllables : &Vec<Weighted<String>>,
//        last_syllables : &Vec<Weighted<String>>,
//        normal_syllables : &Vec<Weighted<String>>,
//        graphemes : &Vec<(String, Vec<Weighted<String>>)>,
//        settings: &GenerateSettings);
    fn generate_syllables(&self, word: &mut Word);
    fn generate_graphemes(&self, word: &mut Word);
    fn rewrite_syllables(&self, word: &mut Word);
    fn rewrite_graphemes(&self, word: &mut Word);
    fn mark_syllable_rejects(&self, word: &mut Word);
    fn mark_grapheme_rejects(&self, word: &mut Word);
}

impl WordGenerator for WordFactory {
    fn generate_syllables(&self, word: &mut Word) {
        let mut rng = rand::thread_rng();

        let mut current_chance_for_syllable : f32 = 1.0;
        let mut syllable_count : usize = 0;
        let mut syllable_loop_iter = rng.gen_iter::<f32>();

        loop {
            let result = syllable_loop_iter.next();
            match result {
                Some(x) => {
                    if x < current_chance_for_syllable && syllable_count < self.settings.max_syllables {
                        syllable_count = syllable_count + 1;
                        current_chance_for_syllable *= 1.0-self.settings.syllable_decay_rate;
                    } else {
                        break
                    }
                }
                None => { break }
            }
        }

        word.syllables = match syllable_count {
            1 => {
                get_random_from_weighted(&self.first_syllable_list)
            },
            2 => {
                let mut temp_word = get_random_from_weighted(&self.first_syllable_list);
                temp_word.push_str(&get_random_from_weighted(&self.last_syllable_list));
                temp_word
            },
            _ => {
                let mut temp_word = get_random_from_weighted(&self.first_syllable_list);
                for _ in 1..(syllable_count - 1) {
                    temp_word.push_str(&get_random_from_weighted(&self.normal_syllable_list));
                }
                temp_word.push_str(&get_random_from_weighted(&self.last_syllable_list));
                temp_word
            },
        };
    }

    fn generate_graphemes(&self, word: &mut Word) {
        assert!(!word.syllables.is_empty(), "Cannot call generate_graphemes on a word without a syllable string");

        let mut grapheme_vector = Vec::new();
        for grapheme in word.syllables.graphemes(false) {
            grapheme_vector.push(syllable_element_to_random_grapheme(&self.graphemes, &String::from_str(&grapheme)));
        }

        word.graphemes = grapheme_vector.iter().fold(String::new(), |accumulator : String, character| { let mut new_str = String::from_str(&accumulator); new_str.push_str(&character); new_str});

        for grapheme in word.syllables.graphemes(false) {
            //currently using .last() because its easy and i don't care about multiple matches. Will either implement filtering elsewhere, throw errors on redefining a group, or find a way to pull the first element

        }
    }
    fn rewrite_syllables(&self, word: &mut Word) {

    }
    fn rewrite_graphemes(&self, word: &mut Word) {

    }
    fn mark_syllable_rejects(&self, word: &mut Word) {

    }
    fn mark_grapheme_rejects(&self, word: &mut Word) {

    }
}

fn syllable_element_to_random_grapheme(grapheme_groups : &Vec<(String, Vec<Weighted<String>>)>, syllable_element : &String) -> String {
    let matching_grapheme_group : Vec<Weighted<String>> = match grapheme_groups.iter().filter(|ref i| i.0 == *syllable_element).map(|ref i| i.1.clone()).last() {
        Some(res) => res,

        None => panic!("No grapheme group found for syllable element {}, check your syllable sets", &syllable_element)
    };
    get_random_from_weighted(&matching_grapheme_group)
}

fn get_random_from_weighted(values : &Vec<Weighted<String>>) -> String {

    let mut local_values : Vec<Weighted<String>> = values.clone();

    let selector : WeightedChoice<String> = WeightedChoice::new(&mut local_values);
    let mut rng = rand::thread_rng();

    selector.ind_sample(&mut rng)
}
<commit_msg>removed superfluous for loop in generate_graphemes<commit_after>extern crate rand;

use config::*;
use rand::*;
use rand::distributions::{Weighted, WeightedChoice, IndependentSample};
use regex::Regex;

pub struct Word {
    pub syllables : String,
    pub graphemes : String,
    pub syllable_rewrite_history : Vec<(String, String)>,
    pub grapheme_rewrite_history : Vec<(String, String)>,
    pub syllable_rejects : Vec<String>,
    pub grapheme_rejects : Vec<String>,
}

pub struct WordFactory {
    pub first_syllable_list : Vec<Weighted<String>>,
    pub last_syllable_list : Vec<Weighted<String>>,
    pub normal_syllable_list : Vec<Weighted<String>>,

    pub graphemes : Vec<(String, Vec<Weighted<String>>)>,

    pub rewrites : RewriteGroup,

    pub rejects : RejectGroup,

    pub settings : GenerateSettings,
}

pub trait WordGenerator {
//    fn new(
//        &mut self,
//        first_syllables : &Vec<Weighted<String>>,
//        last_syllables : &Vec<Weighted<String>>,
//        normal_syllables : &Vec<Weighted<String>>,
//        graphemes : &Vec<(String, Vec<Weighted<String>>)>,
//        settings: &GenerateSettings);
    fn generate_syllables(&self, word: &mut Word);
    fn generate_graphemes(&self, word: &mut Word);
    fn rewrite_syllables(&self, word: &mut Word);
    fn rewrite_graphemes(&self, word: &mut Word);
    fn mark_syllable_rejects(&self, word: &mut Word);
    fn mark_grapheme_rejects(&self, word: &mut Word);
}

impl WordGenerator for WordFactory {
    fn generate_syllables(&self, word: &mut Word) {
        let mut rng = rand::thread_rng();

        let mut current_chance_for_syllable : f32 = 1.0;
        let mut syllable_count : usize = 0;
        let mut syllable_loop_iter = rng.gen_iter::<f32>();

        loop {
            let result = syllable_loop_iter.next();
            match result {
                Some(x) => {
                    if x < current_chance_for_syllable && syllable_count < self.settings.max_syllables {
                        syllable_count = syllable_count + 1;
                        current_chance_for_syllable *= 1.0-self.settings.syllable_decay_rate;
                    } else {
                        break
                    }
                }
                None => { break }
            }
        }

        word.syllables = match syllable_count {
            1 => {
                get_random_from_weighted(&self.first_syllable_list)
            },
            2 => {
                let mut temp_word = get_random_from_weighted(&self.first_syllable_list);
                temp_word.push_str(&get_random_from_weighted(&self.last_syllable_list));
                temp_word
            },
            _ => {
                let mut temp_word = get_random_from_weighted(&self.first_syllable_list);
                for _ in 1..(syllable_count - 1) {
                    temp_word.push_str(&get_random_from_weighted(&self.normal_syllable_list));
                }
                temp_word.push_str(&get_random_from_weighted(&self.last_syllable_list));
                temp_word
            },
        };
    }

    fn generate_graphemes(&self, word: &mut Word) {
        assert!(!word.syllables.is_empty(), "Cannot call generate_graphemes on a word without a syllable string");

        let mut grapheme_vector = Vec::new();
        for grapheme in word.syllables.graphemes(false) {
            grapheme_vector.push(syllable_element_to_random_grapheme(&self.graphemes, &String::from_str(&grapheme)));
        }

        word.graphemes = grapheme_vector.iter().fold(String::new(), |accumulator : String, character| { let mut new_str = String::from_str(&accumulator); new_str.push_str(&character); new_str});
    }
    fn rewrite_syllables(&self, word: &mut Word) {

    }
    fn rewrite_graphemes(&self, word: &mut Word) {

    }
    fn mark_syllable_rejects(&self, word: &mut Word) {

    }
    fn mark_grapheme_rejects(&self, word: &mut Word) {

    }
}

fn syllable_element_to_random_grapheme(grapheme_groups : &Vec<(String, Vec<Weighted<String>>)>, syllable_element : &String) -> String {
    let matching_grapheme_group : Vec<Weighted<String>> = match grapheme_groups.iter().filter(|ref i| i.0 == *syllable_element).map(|ref i| i.1.clone()).last() {
        Some(res) => res,

        None => panic!("No grapheme group found for syllable element {}, check your syllable sets", &syllable_element)
    };
    get_random_from_weighted(&matching_grapheme_group)
}

fn get_random_from_weighted(values : &Vec<Weighted<String>>) -> String {

    let mut local_values : Vec<Weighted<String>> = values.clone();

    let selector : WeightedChoice<String> = WeightedChoice::new(&mut local_values);
    let mut rng = rand::thread_rng();

    selector.ind_sample(&mut rng)
}
<|endoftext|>


<commit_before>// parse output of cargo build --message-format json,
// caching the results. Can get the exact name of the .rlib
// for the latest available version in the static cache.
extern crate json;
use std::path::{Path,PathBuf};
use std::fs::File;
use std::io::Write;

use super::es;
use es::traits::*;
use super::crate_utils::proper_crate_name;

use semver::Version;

fn as_str(v: &json::JsonValue) -> &str {
    v.as_str().unwrap()
}

fn read_entry(line: &str) -> Option<(String,String,Version,String,String,String)> {
    use strutil::next_2;

    if let Ok(doc) = json::parse(line) {
        let features = doc["features"].members().map(as_str).join(' ');
        let path = Path::new(as_str(&doc["filenames"][0]));

        let filename = path.file_name().unwrap();
        let ext = path.extension();
        if ! (ext.is_none() || ext.unwrap() == "exe") { // ignore build artifacts
            // package_id has version
            let package_id = as_str(&doc["package_id"]).split_whitespace();
            let (package,vs) = next_2(package_id);

            // but look for _crate name_ in name field
            let name = as_str(&doc["target"]["name"]);

            // get the cached source path
            let path = Path::new(as_str(&doc["target"]["src_path"]));

            let vs = Version::parse(vs).or_die("bad semver");
            let filename = filename.to_str().or_die("filename not valid Unicode");
            let src_path = path.to_str().or_die("cached path not valid Unicode");
            Some((package.into(),name.into(),vs,features,filename.into(),src_path.into()))
        } else {
            None
        }
    } else {
        None
    }
 }

fn file_name(cache: &Path) -> PathBuf {
    cache.join("cargo.meta")
}

#[derive(Debug)]
pub struct MetaEntry {
    pub package: String,
    pub crate_name: String,
    pub version: Version,
    pub features: String,
    debug_name: String,
    release_name: String,
    pub path: PathBuf,
}

pub struct Meta {
    entries: Vec<MetaEntry>
}

impl Meta {

    pub fn new() -> Meta {
        Meta {
            entries: Vec::new()
        }
    }

    pub fn exists(cache: &Path) -> bool {
        file_name(cache).exists()
    }

    pub fn new_from_file(cache: &Path) -> Meta {

        fn opt_field(fields: &[&str], idx: usize) -> String {
            if idx >= fields.len() {
                ""
            } else {
                fields[idx]
            }.into()
        }

        let mut v = Vec::new();
        let meta_f = file_name(cache);
        for line in es::lines(es::open(&meta_f)) {
            let parts = line.split(',').to_vec();
            v.push(MetaEntry{
                package: parts[0].into(),
                crate_name: parts[1].into(),
                version: Version::parse(parts[2]).unwrap(),
                features: parts[3].into(),
                debug_name: parts[4].into(),
                release_name: parts[5].into(),
                path: PathBuf::from(opt_field(&parts,6)),
            });
        }
        Meta {
            entries: v
        }
    }

    pub fn get_meta_entry<'a>(&'a self, name: &str) -> Option<&'a MetaEntry> {
        let mut v = self.entries.iter()
            .filter(|e| e.package == name || e.crate_name == name)
            .to_vec();
        if v.len() == 0 {
            return None;
        }
        if v.len() > 1 { // sort by semver in ascending order...
            v.sort_by(|a,b| a.version.cmp(&b.version));
        }
        Some(v[v.len()-1])
    }

    pub fn get_full_crate_name(&self, name: &str, debug: bool) -> Option<String> {
        self.get_meta_entry(name)
            .map(|e| if debug {e.debug_name.clone()} else {e.release_name.clone()})
    }

    pub fn dump_crates (&mut self, maybe_name: Option<String>) {
        if let Some(name) = maybe_name {
            if let Some(e) = self.get_meta_entry(&name) {
                println!("{}\t{}",e.crate_name,e.version);
            } else {
                es::quit("no such crate");
            }
        } else {
            self.entries.sort_by(|a,b| a.crate_name.cmp(&b.crate_name));
            for e in self.entries.iter() {
                println!("{}\t{}",e.crate_name,e.version);
            }
        }
    }

    // constructing from output of 'cargo build'

    pub fn debug(&mut self, txt: String) {
        for line in txt.lines() {
            // note that features is in form '"foo","bar"' which we
            // store as 'foo bar'
            if let Some((package,crate_name,vs,features,filename,path)) = read_entry(line) {
                let crate_name = proper_crate_name(&crate_name);
                self.entries.push(MetaEntry{
                    package: package,
                    crate_name: crate_name,
                    version: vs,
                    features: features,
                    debug_name: filename,
                    release_name: String::new(),
                    path: PathBuf::from(path),
                });
            }
        }
    }

    pub fn release(&mut self, txt: String) {
        for line in txt.lines() {
            if let Some((name,_,vs,_,filename,_)) = read_entry(line) {
                if let Some(entry) = self.entries.iter_mut()
                    .find(|e| e.package == name && e.version == vs) {
                        entry.release_name = filename;
                } else {
                    eprintln!("cannot find {} in release build",name);
                }
            }
        }
    }

    pub fn update(self, cache: &Path) {
        let meta_f = file_name(cache);
        let mut f = File::create(&meta_f).or_die("cannot create cargo.meta");
        for e in self.entries {
            write!(f,"{},{},{},{},{},{},{}\n",
                e.package,e.crate_name,e.version,e.features,
                e.debug_name,e.release_name,
                e.path.display()
            ).or_die("i/o?");
        }
    }

}
<commit_msg>some crates break expected --json-message output<commit_after>// parse output of cargo build --message-format json,
// caching the results. Can get the exact name of the .rlib
// for the latest available version in the static cache.
extern crate json;
use std::path::{Path,PathBuf};
use std::fs::File;
use std::io::Write;

use super::es;
use es::traits::*;
use super::crate_utils::proper_crate_name;

use semver::Version;

fn as_str(v: &json::JsonValue) -> &str {
    v.as_str().unwrap()
}

fn read_entry(line: &str) -> Option<(String,String,Version,String,String,String)> {
    use strutil::next_2;

    if let Ok(doc) = json::parse(line) {
        let features = doc["features"].members().map(as_str).join(' ');
        let filenames = &doc["filenames"][0];
        if ! filenames.is_string() {
            println!("note: no filenames {}",line);
            return None;
        }
        let path = Path::new(as_str(filenames));

        let filename = path.file_name().unwrap();
        let ext = path.extension();
        if ! (ext.is_none() || ext.unwrap() == "exe") { // ignore build artifacts
            // package_id has version
            let package_id = as_str(&doc["package_id"]).split_whitespace();
            let (package,vs) = next_2(package_id);

            // but look for _crate name_ in name field
            let name = as_str(&doc["target"]["name"]);

            // get the cached source path
            let path = Path::new(as_str(&doc["target"]["src_path"]));

            let vs = Version::parse(vs).or_die("bad semver");
            let filename = filename.to_str().or_die("filename not valid Unicode");
            let src_path = path.to_str().or_die("cached path not valid Unicode");
            Some((package.into(),name.into(),vs,features,filename.into(),src_path.into()))
        } else {
            None
        }
    } else {
        None
    }
 }

fn file_name(cache: &Path) -> PathBuf {
    cache.join("cargo.meta")
}

#[derive(Debug)]
pub struct MetaEntry {
    pub package: String,
    pub crate_name: String,
    pub version: Version,
    pub features: String,
    debug_name: String,
    release_name: String,
    pub path: PathBuf,
}

pub struct Meta {
    entries: Vec<MetaEntry>
}

impl Meta {

    pub fn new() -> Meta {
        Meta {
            entries: Vec::new()
        }
    }

    pub fn exists(cache: &Path) -> bool {
        file_name(cache).exists()
    }

    pub fn new_from_file(cache: &Path) -> Meta {

        fn opt_field(fields: &[&str], idx: usize) -> String {
            if idx >= fields.len() {
                ""
            } else {
                fields[idx]
            }.into()
        }

        let mut v = Vec::new();
        let meta_f = file_name(cache);
        for line in es::lines(es::open(&meta_f)) {
            let parts = line.split(',').to_vec();
            v.push(MetaEntry{
                package: parts[0].into(),
                crate_name: parts[1].into(),
                version: Version::parse(parts[2]).unwrap(),
                features: parts[3].into(),
                debug_name: parts[4].into(),
                release_name: parts[5].into(),
                path: PathBuf::from(opt_field(&parts,6)),
            });
        }
        Meta {
            entries: v
        }
    }

    pub fn get_meta_entry<'a>(&'a self, name: &str) -> Option<&'a MetaEntry> {
        let mut v = self.entries.iter()
            .filter(|e| e.package == name || e.crate_name == name)
            .to_vec();
        if v.len() == 0 {
            return None;
        }
        if v.len() > 1 { // sort by semver in ascending order...
            v.sort_by(|a,b| a.version.cmp(&b.version));
        }
        Some(v[v.len()-1])
    }

    pub fn get_full_crate_name(&self, name: &str, debug: bool) -> Option<String> {
        self.get_meta_entry(name)
            .map(|e| if debug {e.debug_name.clone()} else {e.release_name.clone()})
    }

    pub fn dump_crates (&mut self, maybe_name: Option<String>) {
        if let Some(name) = maybe_name {
            if let Some(e) = self.get_meta_entry(&name) {
                println!("{}\t{}",e.crate_name,e.version);
            } else {
                es::quit("no such crate");
            }
        } else {
            self.entries.sort_by(|a,b| a.crate_name.cmp(&b.crate_name));
            for e in self.entries.iter() {
                println!("{}\t{}",e.crate_name,e.version);
            }
        }
    }

    // constructing from output of 'cargo build'

    pub fn debug(&mut self, txt: String) {
        for line in txt.lines() {
            // note that features is in form '"foo","bar"' which we
            // store as 'foo bar'
            if let Some((package,crate_name,vs,features,filename,path)) = read_entry(line) {
                let crate_name = proper_crate_name(&crate_name);
                self.entries.push(MetaEntry{
                    package: package,
                    crate_name: crate_name,
                    version: vs,
                    features: features,
                    debug_name: filename,
                    release_name: String::new(),
                    path: PathBuf::from(path),
                });
            }
        }
    }

    pub fn release(&mut self, txt: String) {
        for line in txt.lines() {
            if let Some((name,_,vs,_,filename,_)) = read_entry(line) {
                if let Some(entry) = self.entries.iter_mut()
                    .find(|e| e.package == name && e.version == vs) {
                        entry.release_name = filename;
                } else {
                    eprintln!("cannot find {} in release build",name);
                }
            }
        }
    }

    pub fn update(self, cache: &Path) {
        let meta_f = file_name(cache);
        let mut f = File::create(&meta_f).or_die("cannot create cargo.meta");
        for e in self.entries {
            write!(f,"{},{},{},{},{},{},{}\n",
                e.package,e.crate_name,e.version,e.features,
                e.debug_name,e.release_name,
                e.path.display()
            ).or_die("i/o?");
        }
    }

}
<|endoftext|>


<commit_before>use std::cell::Cell;

use cairo::{self, MatrixTrait};

use crate::attributes::Attribute;
use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
use crate::drawing_ctx::DrawingCtx;
use crate::error::RenderingError;
use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers::ParseValue;
use crate::property_bag::PropertyBag;

coord_units!(ClipPathUnits, CoordUnits::UserSpaceOnUse);

pub struct NodeClipPath {
    units: Cell<ClipPathUnits>,
}

impl NodeClipPath {
    pub fn new() -> NodeClipPath {
        NodeClipPath {
            units: Cell::new(ClipPathUnits::default()),
        }
    }

    pub fn get_units(&self) -> ClipPathUnits {
        self.units.get()
    }

    pub fn to_cairo_context(
        &self,
        node: &RsvgNode,
        affine_before_clip: &cairo::Matrix,
        draw_ctx: &mut DrawingCtx,
        bbox: &BoundingBox,
    ) -> Result<(), RenderingError> {
        let cascaded = node.get_cascaded_values();

        let clip_units = self.units.get();

        let child_matrix = if clip_units == ClipPathUnits(CoordUnits::ObjectBoundingBox) {
            if bbox.rect.is_none() {
                // The node being clipped is empty / doesn't have a
                // bounding box, so there's nothing to clip!
                return Ok(());
            }

            let rect = bbox.rect.as_ref().unwrap();

            let bbtransform = cairo::Matrix::new(rect.width, 0.0, 0.0, rect.height, rect.x, rect.y);
            cairo::Matrix::multiply(&bbtransform, affine_before_clip)
        } else {
            *affine_before_clip
        };

        let cr = draw_ctx.get_cairo_context();
        let save_affine = cr.get_matrix();
        cr.set_matrix(child_matrix);

        // here we don't push a layer because we are clipping
        let res = node.draw_children(&cascaded, draw_ctx, true);

        cr.set_matrix(save_affine);

        cr.clip();

        res
    }
}

impl NodeTrait for NodeClipPath {
    fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            match attr {
                Attribute::ClipPathUnits => self.units.set(attr.parse(value)?),
                _ => (),
            }
        }

        Ok(())
    }
}
<commit_msg>NodeClipPath.to_cairo_context() - reorder code a bit<commit_after>use std::cell::Cell;

use cairo::{self, MatrixTrait};

use crate::attributes::Attribute;
use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
use crate::drawing_ctx::DrawingCtx;
use crate::error::RenderingError;
use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers::ParseValue;
use crate::property_bag::PropertyBag;

coord_units!(ClipPathUnits, CoordUnits::UserSpaceOnUse);

pub struct NodeClipPath {
    units: Cell<ClipPathUnits>,
}

impl NodeClipPath {
    pub fn new() -> NodeClipPath {
        NodeClipPath {
            units: Cell::new(ClipPathUnits::default()),
        }
    }

    pub fn get_units(&self) -> ClipPathUnits {
        self.units.get()
    }

    pub fn to_cairo_context(
        &self,
        node: &RsvgNode,
        affine_before_clip: &cairo::Matrix,
        draw_ctx: &mut DrawingCtx,
        bbox: &BoundingBox,
    ) -> Result<(), RenderingError> {
        let cascaded = node.get_cascaded_values();

        let clip_units = self.units.get();

        let cr = draw_ctx.get_cairo_context();
        let save_affine = cr.get_matrix();

        let child_matrix = if clip_units == ClipPathUnits(CoordUnits::ObjectBoundingBox) {
            if bbox.rect.is_none() {
                // The node being clipped is empty / doesn't have a
                // bounding box, so there's nothing to clip!
                return Ok(());
            }

            let rect = bbox.rect.as_ref().unwrap();

            let bbtransform = cairo::Matrix::new(rect.width, 0.0, 0.0, rect.height, rect.x, rect.y);
            cairo::Matrix::multiply(&bbtransform, affine_before_clip)
        } else {
            *affine_before_clip
        };

        cr.set_matrix(child_matrix);

        // here we don't push a layer because we are clipping
        let res = node.draw_children(&cascaded, draw_ctx, true);

        cr.set_matrix(save_affine);

        cr.clip();

        res
    }
}

impl NodeTrait for NodeClipPath {
    fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            match attr {
                Attribute::ClipPathUnits => self.units.set(attr.parse(value)?),
                _ => (),
            }
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>use super::arch::{Arch};

#[derive(Debug, Copy, Clone)]
pub enum XrefType {
    // mov eax, eax
    // push ebp
    Fallthrough,
    // call [0x401000]
    Call,
    // call [eax]
    //IndirectCall { src: Rva },
    // jmp 0x401000
    UnconditionalJump,
    // jmp eax
    //UnconditionalIndirectJump { src: Rva, dst: Rva },
    // jnz 0x401000
    ConditionalJump,
    // jnz eax
    //ConditionalIndirectJump { src: Rva },
    // cmov 0x1
    ConditionalMove,
}

#[derive(Debug)]
pub struct Xref<A: Arch> {
    src: A::RVA,
    dst: A::RVA,
    typ: XrefType,
}

// we have to implement `Clone` manually, as described in
// https://github.com/rust-lang/rust/issues/41481
// and https://github.com/rust-lang/rust/issues/26925
impl<A: Arch> Clone for Xref<A> {
    fn clone(&self) -> Self {
        Xref{
            ..*self
        }
    }
}
impl<A: Arch> Copy for Xref<A> {}
<commit_msg>xref: publish fields<commit_after>use super::arch::{Arch};

#[derive(Debug, Copy, Clone)]
pub enum XrefType {
    // mov eax, eax
    // push ebp
    Fallthrough,
    // call [0x401000]
    Call,
    // call [eax]
    //IndirectCall { src: Rva },
    // jmp 0x401000
    UnconditionalJump,
    // jmp eax
    //UnconditionalIndirectJump { src: Rva, dst: Rva },
    // jnz 0x401000
    ConditionalJump,
    // jnz eax
    //ConditionalIndirectJump { src: Rva },
    // cmov 0x1
    ConditionalMove,
}

#[derive(Debug)]
pub struct Xref<A: Arch> {
    pub src: A::RVA,
    pub dst: A::RVA,
    pub typ: XrefType,
}

// we have to implement `Clone` manually, as described in
// https://github.com/rust-lang/rust/issues/41481
// and https://github.com/rust-lang/rust/issues/26925
impl<A: Arch> Clone for Xref<A> {
    fn clone(&self) -> Self {
        Xref{
            ..*self
        }
    }
}
impl<A: Arch> Copy for Xref<A> {}
<|endoftext|>


<commit_before>//! LLVM's simd platform intrinsics
//!
//! TODO: should use `link_llvm_intrinsic` instead: issue #112

extern "platform-intrinsic" {
    //pub fn simd_select_bitmask
    pub fn simd_eq<T, U>(x: T, y: T) -> U;
    pub fn simd_ne<T, U>(x: T, y: T) -> U;
    pub fn simd_lt<T, U>(x: T, y: T) -> U;
    pub fn simd_le<T, U>(x: T, y: T) -> U;
    pub fn simd_gt<T, U>(x: T, y: T) -> U;
    pub fn simd_ge<T, U>(x: T, y: T) -> U;

    #[rustc_args_required_const(2)]
    pub fn simd_shuffle2<T, U>(x: T, y: T, idx: [u32; 2]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle4<T, U>(x: T, y: T, idx: [u32; 4]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle8<T, U>(x: T, y: T, idx: [u32; 8]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle16<T, U>(x: T, y: T, idx: [u32; 16]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle32<T, U>(x: T, y: T, idx: [u32; 32]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle64<T, U>(x: T, y: T, idx: [u32; 64]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle128<T, U>(x: T, y: T, idx: [u32; 128]) -> U;

    #[rustc_const_unstable(feature = "const_simd_insert", issue = "0")]
    pub fn simd_insert<T, U>(x: T, idx: u32, val: U) -> T;
    #[rustc_const_unstable(feature = "const_simd_extract", issue = "0")]
    pub fn simd_extract<T, U>(x: T, idx: u32) -> U;
    //pub fn simd_select
    pub fn simd_bitmask<T, U>(x: T) -> U;

    pub fn simd_cast<T, U>(x: T) -> U;

    pub fn simd_add<T>(x: T, y: T) -> T;
    pub fn simd_sub<T>(x: T, y: T) -> T;
    pub fn simd_mul<T>(x: T, y: T) -> T;
    pub fn simd_div<T>(x: T, y: T) -> T;
    pub fn simd_shl<T>(x: T, y: T) -> T;
    pub fn simd_shr<T>(x: T, y: T) -> T;
    pub fn simd_and<T>(x: T, y: T) -> T;
    pub fn simd_or<T>(x: T, y: T) -> T;
    pub fn simd_xor<T>(x: T, y: T) -> T;

    pub fn simd_saturating_add<T>(x: T, y: T) -> T;
    pub fn simd_saturating_sub<T>(x: T, y: T) -> T;

    pub fn simd_gather<T, U, V>(values: T, pointers: U, mask: V) -> T;
    pub fn simd_scatter<T, U, V>(values: T, pointers: U, mask: V);

    pub fn simd_reduce_add_unordered<T, U>(x: T) -> U;
    pub fn simd_reduce_mul_unordered<T, U>(x: T) -> U;
    pub fn simd_reduce_add_ordered<T, U>(x: T, acc: U) -> U;
    pub fn simd_reduce_mul_ordered<T, U>(x: T, acc: U) -> U;
    pub fn simd_reduce_min<T, U>(x: T) -> U;
    pub fn simd_reduce_max<T, U>(x: T) -> U;
    pub fn simd_reduce_min_nanless<T, U>(x: T) -> U;
    pub fn simd_reduce_max_nanless<T, U>(x: T) -> U;
    pub fn simd_reduce_and<T, U>(x: T) -> U;
    pub fn simd_reduce_or<T, U>(x: T) -> U;
    pub fn simd_reduce_xor<T, U>(x: T) -> U;
    pub fn simd_reduce_all<T>(x: T) -> bool;
    pub fn simd_reduce_any<T>(x: T) -> bool;

    pub fn simd_select<M, T>(m: M, a: T, b: T) -> T;
    pub fn simd_select_bitmask<M, T>(m: M, a: T, b: T) -> T;

    pub fn simd_fmin<T>(a: T, b: T) -> T;
    pub fn simd_fmax<T>(a: T, b: T) -> T;

    pub fn simd_fsqrt<T>(a: T) -> T;
    pub fn simd_fsin<T>(a: T) -> T;
    pub fn simd_fcos<T>(a: T) -> T;
    pub fn simd_fabs<T>(a: T) -> T;
    pub fn simd_floor<T>(a: T) -> T;
    pub fn simd_ceil<T>(a: T) -> T;
    pub fn simd_fexp<T>(a: T) -> T;
    pub fn simd_fexp2<T>(a: T) -> T;
    pub fn simd_flog10<T>(a: T) -> T;
    pub fn simd_flog2<T>(a: T) -> T;
    pub fn simd_flog<T>(a: T) -> T;
    //pub fn simd_fpowi
    //pub fn simd_fpow
    pub fn simd_fma<T>(a: T, b: T, c: T) -> T;
}
<commit_msg>Update simd_llvm.rs<commit_after>//! LLVM's simd platform intrinsics
//!
//! TODO: should use `link_llvm_intrinsic` instead: issue #112

extern "platform-intrinsic" {
    //pub fn simd_select_bitmask
    pub fn simd_eq<T, U>(x: T, y: T) -> U;
    pub fn simd_ne<T, U>(x: T, y: T) -> U;
    pub fn simd_lt<T, U>(x: T, y: T) -> U;
    pub fn simd_le<T, U>(x: T, y: T) -> U;
    pub fn simd_gt<T, U>(x: T, y: T) -> U;
    pub fn simd_ge<T, U>(x: T, y: T) -> U;

    #[rustc_args_required_const(2)]
    pub fn simd_shuffle2<T, U>(x: T, y: T, idx: [u32; 2]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle4<T, U>(x: T, y: T, idx: [u32; 4]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle8<T, U>(x: T, y: T, idx: [u32; 8]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle16<T, U>(x: T, y: T, idx: [u32; 16]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle32<T, U>(x: T, y: T, idx: [u32; 32]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle64<T, U>(x: T, y: T, idx: [u32; 64]) -> U;
    #[rustc_args_required_const(2)]
    pub fn simd_shuffle128<T, U>(x: T, y: T, idx: [u32; 128]) -> U;

    #[rustc_const_unstable(feature = "const_simd_insert", issue = "0")]
    pub fn simd_insert<T, U>(x: T, idx: u32, val: U) -> T;
    #[rustc_const_unstable(feature = "const_simd_extract", issue = "0")]
    pub fn simd_extract<T, U>(x: T, idx: u32) -> U;
    //pub fn simd_select
    pub fn simd_bitmask<T, U>(x: T) -> U;

    pub fn simd_cast<T, U>(x: T) -> U;

    pub fn simd_add<T>(x: T, y: T) -> T;
    pub fn simd_sub<T>(x: T, y: T) -> T;
    pub fn simd_mul<T>(x: T, y: T) -> T;
    pub fn simd_div<T>(x: T, y: T) -> T;
    pub fn simd_shl<T>(x: T, y: T) -> T;
    pub fn simd_shr<T>(x: T, y: T) -> T;
    pub fn simd_and<T>(x: T, y: T) -> T;
    pub fn simd_or<T>(x: T, y: T) -> T;
    pub fn simd_xor<T>(x: T, y: T) -> T;

    pub fn simd_saturating_add<T>(x: T, y: T) -> T;
    pub fn simd_saturating_sub<T>(x: T, y: T) -> T;

    pub fn simd_gather<T, U, V>(values: T, pointers: U, mask: V) -> T;
    pub fn simd_scatter<T, U, V>(values: T, pointers: U, mask: V);

    pub fn simd_reduce_add_unordered<T, U>(x: T) -> U;
    pub fn simd_reduce_mul_unordered<T, U>(x: T) -> U;
    pub fn simd_reduce_add_ordered<T, U>(x: T, acc: U) -> U;
    pub fn simd_reduce_mul_ordered<T, U>(x: T, acc: U) -> U;
    pub fn simd_reduce_min<T, U>(x: T) -> U;
    pub fn simd_reduce_max<T, U>(x: T) -> U;
    pub fn simd_reduce_min_nanless<T, U>(x: T) -> U;
    pub fn simd_reduce_max_nanless<T, U>(x: T) -> U;
    pub fn simd_reduce_and<T, U>(x: T) -> U;
    pub fn simd_reduce_or<T, U>(x: T) -> U;
    pub fn simd_reduce_xor<T, U>(x: T) -> U;
    pub fn simd_reduce_all<T>(x: T) -> bool;
    pub fn simd_reduce_any<T>(x: T) -> bool;

    pub fn simd_select<M, T>(m: M, a: T, b: T) -> T;
    pub fn simd_select_bitmask<M, T>(m: M, a: T, b: T) -> T;

    pub fn simd_fmin<T>(a: T, b: T) -> T;
    pub fn simd_fmax<T>(a: T, b: T) -> T;

    pub fn simd_fsqrt<T>(a: T) -> T;
    pub fn simd_fsin<T>(a: T) -> T;
    pub fn simd_fcos<T>(a: T) -> T;
    pub fn simd_fabs<T>(a: T) -> T;
    pub fn simd_floor<T>(a: T) -> T;
    pub fn simd_ceil<T>(a: T) -> T;
    pub fn simd_fexp<T>(a: T) -> T;
    pub fn simd_fexp2<T>(a: T) -> T;
    pub fn simd_flog10<T>(a: T) -> T;
    pub fn simd_flog2<T>(a: T) -> T;
    pub fn simd_flog<T>(a: T) -> T;
    //pub fn simd_fpowi
    //pub fn simd_fpow
    pub fn simd_fma<T>(a: T, b: T, c: T) -> T;
}
<|endoftext|>


<commit_before>//! Handles the `Enter` key press. At the momently, this only continues
//! comments, but should handle indent some time in the future as well.

use ide_db::base_db::{FilePosition, SourceDatabase};
use ide_db::RootDatabase;
use syntax::{
    ast::{self, AstToken},
    AstNode, SmolStr, SourceFile,
    SyntaxKind::*,
    SyntaxToken, TextRange, TextSize, TokenAtOffset,
};

use text_edit::TextEdit;

// Feature: On Enter
//
// rust-analyzer can override kbd:[Enter] key to make it smarter:
//
// - kbd:[Enter] inside triple-slash comments automatically inserts `///`
// - kbd:[Enter] in the middle or after a trailing space in `//` inserts `//`
//
// This action needs to be assigned to shortcut explicitly.
//
// VS Code::
//
// Add the following to `keybindings.json`:
// [source,json]
// ----
// {
//   "key": "Enter",
//   "command": "rust-analyzer.onEnter",
//   "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust"
// }
// ----
//
// image::https://user-images.githubusercontent.com/48062697/113065578-04c21800-91b1-11eb-82b8-22b8c481e645.gif[]
pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> {
    let parse = db.parse(position.file_id);
    let file = parse.tree();
    let comment = file
        .syntax()
        .token_at_offset(position.offset)
        .left_biased()
        .and_then(ast::Comment::cast)?;

    if comment.kind().shape.is_block() {
        return None;
    }

    let prefix = comment.prefix();
    let comment_range = comment.syntax().text_range();
    if position.offset < comment_range.start() + TextSize::of(prefix) {
        return None;
    }

    let mut remove_trailing_whitespace = false;
    // Continuing single-line non-doc comments (like this one :) ) is annoying
    if prefix == "//" && comment_range.end() == position.offset {
        if comment.text().ends_with(' ') {
            cov_mark::hit!(continues_end_of_line_comment_with_space);
            remove_trailing_whitespace = true;
        } else if !followed_by_comment(&comment) {
            return None;
        }
    }

    let indent = node_indent(&file, comment.syntax())?;
    let inserted = format!("\n{}{} $0", indent, prefix);
    let delete = if remove_trailing_whitespace {
        let trimmed_len = comment.text().trim_end().len() as u32;
        let trailing_whitespace_len = comment.text().len() as u32 - trimmed_len;
        TextRange::new(position.offset - TextSize::from(trailing_whitespace_len), position.offset)
    } else {
        TextRange::empty(position.offset)
    };
    let edit = TextEdit::replace(delete, inserted);
    Some(edit)
}

fn followed_by_comment(comment: &ast::Comment) -> bool {
    let ws = match comment.syntax().next_token().and_then(ast::Whitespace::cast) {
        Some(it) => it,
        None => return false,
    };
    if ws.spans_multiple_lines() {
        return false;
    }
    ws.syntax().next_token().and_then(ast::Comment::cast).is_some()
}

fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> {
    let ws = match file.syntax().token_at_offset(token.text_range().start()) {
        TokenAtOffset::Between(l, r) => {
            assert!(r == *token);
            l
        }
        TokenAtOffset::Single(n) => {
            assert!(n == *token);
            return Some("".into());
        }
        TokenAtOffset::None => unreachable!(),
    };
    if ws.kind() != WHITESPACE {
        return None;
    }
    let text = ws.text();
    let pos = text.rfind('\n').map(|it| it + 1).unwrap_or(0);
    Some(text[pos..].into())
}

#[cfg(test)]
mod tests {
    use stdx::trim_indent;
    use test_utils::assert_eq_text;

    use crate::fixture;

    fn apply_on_enter(before: &str) -> Option<String> {
        let (analysis, position) = fixture::position(&before);
        let result = analysis.on_enter(position).unwrap()?;

        let mut actual = analysis.file_text(position.file_id).unwrap().to_string();
        result.apply(&mut actual);
        Some(actual)
    }

    fn do_check(ra_fixture_before: &str, ra_fixture_after: &str) {
        let ra_fixture_after = &trim_indent(ra_fixture_after);
        let actual = apply_on_enter(ra_fixture_before).unwrap();
        assert_eq_text!(ra_fixture_after, &actual);
    }

    fn do_check_noop(ra_fixture_text: &str) {
        assert!(apply_on_enter(ra_fixture_text).is_none())
    }

    #[test]
    fn continues_doc_comment() {
        do_check(
            r"
/// Some docs$0
fn foo() {
}
",
            r"
/// Some docs
/// $0
fn foo() {
}
",
        );

        do_check(
            r"
impl S {
    /// Some$0 docs.
    fn foo() {}
}
",
            r"
impl S {
    /// Some
    /// $0 docs.
    fn foo() {}
}
",
        );

        do_check(
            r"
///$0 Some docs
fn foo() {
}
",
            r"
///
/// $0 Some docs
fn foo() {
}
",
        );
    }

    #[test]
    fn does_not_continue_before_doc_comment() {
        do_check_noop(r"$0//! docz");
    }

    #[test]
    fn continues_code_comment_in_the_middle_of_line() {
        do_check(
            r"
fn main() {
    // Fix$0 me
    let x = 1 + 1;
}
",
            r"
fn main() {
    // Fix
    // $0 me
    let x = 1 + 1;
}
",
        );
    }

    #[test]
    fn continues_code_comment_in_the_middle_several_lines() {
        do_check(
            r"
fn main() {
    // Fix$0
    // me
    let x = 1 + 1;
}
",
            r"
fn main() {
    // Fix
    // $0
    // me
    let x = 1 + 1;
}
",
        );
    }

    #[test]
    fn does_not_continue_end_of_line_comment() {
        do_check_noop(
            r"
fn main() {
    // Fix me$0
    let x = 1 + 1;
}
",
        );
    }

    #[test]
    fn continues_end_of_line_comment_with_space() {
        cov_mark::check!(continues_end_of_line_comment_with_space);
        do_check(
            r#"
fn main() {
    // Fix me $0
    let x = 1 + 1;
}
"#,
            r#"
fn main() {
    // Fix me
    // $0
    let x = 1 + 1;
}
"#,
        );
    }

    #[test]
    fn trims_all_trailing_whitespace() {
        do_check(
            "
fn main() {
    // Fix me  \t\t   $0
    let x = 1 + 1;
}
",
            "
fn main() {
    // Fix me
    // $0
    let x = 1 + 1;
}
",
        );
    }
}
<commit_msg>8425: Added documentation for on enter covering //! doc comments.<commit_after>//! Handles the `Enter` key press. At the momently, this only continues
//! comments, but should handle indent some time in the future as well.

use ide_db::base_db::{FilePosition, SourceDatabase};
use ide_db::RootDatabase;
use syntax::{
    ast::{self, AstToken},
    AstNode, SmolStr, SourceFile,
    SyntaxKind::*,
    SyntaxToken, TextRange, TextSize, TokenAtOffset,
};

use text_edit::TextEdit;

// Feature: On Enter
//
// rust-analyzer can override kbd:[Enter] key to make it smarter:
//
// - kbd:[Enter] inside triple-slash comments automatically inserts `///`
// - kbd:[Enter] in the middle or after a trailing space in `//` inserts `//`
// - kbd:[Enter] inside `//!` doc comments automatically inserts `//!`
//
// This action needs to be assigned to shortcut explicitly.
//
// VS Code::
//
// Add the following to `keybindings.json`:
// [source,json]
// ----
// {
//   "key": "Enter",
//   "command": "rust-analyzer.onEnter",
//   "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust"
// }
// ----
//
// image::https://user-images.githubusercontent.com/48062697/113065578-04c21800-91b1-11eb-82b8-22b8c481e645.gif[]
pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> {
    let parse = db.parse(position.file_id);
    let file = parse.tree();
    let comment = file
        .syntax()
        .token_at_offset(position.offset)
        .left_biased()
        .and_then(ast::Comment::cast)?;

    if comment.kind().shape.is_block() {
        return None;
    }

    let prefix = comment.prefix();
    let comment_range = comment.syntax().text_range();
    if position.offset < comment_range.start() + TextSize::of(prefix) {
        return None;
    }

    let mut remove_trailing_whitespace = false;
    // Continuing single-line non-doc comments (like this one :) ) is annoying
    if prefix == "//" && comment_range.end() == position.offset {
        if comment.text().ends_with(' ') {
            cov_mark::hit!(continues_end_of_line_comment_with_space);
            remove_trailing_whitespace = true;
        } else if !followed_by_comment(&comment) {
            return None;
        }
    }

    let indent = node_indent(&file, comment.syntax())?;
    let inserted = format!("\n{}{} $0", indent, prefix);
    let delete = if remove_trailing_whitespace {
        let trimmed_len = comment.text().trim_end().len() as u32;
        let trailing_whitespace_len = comment.text().len() as u32 - trimmed_len;
        TextRange::new(position.offset - TextSize::from(trailing_whitespace_len), position.offset)
    } else {
        TextRange::empty(position.offset)
    };
    let edit = TextEdit::replace(delete, inserted);
    Some(edit)
}

fn followed_by_comment(comment: &ast::Comment) -> bool {
    let ws = match comment.syntax().next_token().and_then(ast::Whitespace::cast) {
        Some(it) => it,
        None => return false,
    };
    if ws.spans_multiple_lines() {
        return false;
    }
    ws.syntax().next_token().and_then(ast::Comment::cast).is_some()
}

fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> {
    let ws = match file.syntax().token_at_offset(token.text_range().start()) {
        TokenAtOffset::Between(l, r) => {
            assert!(r == *token);
            l
        }
        TokenAtOffset::Single(n) => {
            assert!(n == *token);
            return Some("".into());
        }
        TokenAtOffset::None => unreachable!(),
    };
    if ws.kind() != WHITESPACE {
        return None;
    }
    let text = ws.text();
    let pos = text.rfind('\n').map(|it| it + 1).unwrap_or(0);
    Some(text[pos..].into())
}

#[cfg(test)]
mod tests {
    use stdx::trim_indent;
    use test_utils::assert_eq_text;

    use crate::fixture;

    fn apply_on_enter(before: &str) -> Option<String> {
        let (analysis, position) = fixture::position(&before);
        let result = analysis.on_enter(position).unwrap()?;

        let mut actual = analysis.file_text(position.file_id).unwrap().to_string();
        result.apply(&mut actual);
        Some(actual)
    }

    fn do_check(ra_fixture_before: &str, ra_fixture_after: &str) {
        let ra_fixture_after = &trim_indent(ra_fixture_after);
        let actual = apply_on_enter(ra_fixture_before).unwrap();
        assert_eq_text!(ra_fixture_after, &actual);
    }

    fn do_check_noop(ra_fixture_text: &str) {
        assert!(apply_on_enter(ra_fixture_text).is_none())
    }

    #[test]
    fn continues_doc_comment() {
        do_check(
            r"
/// Some docs$0
fn foo() {
}
",
            r"
/// Some docs
/// $0
fn foo() {
}
",
        );

        do_check(
            r"
impl S {
    /// Some$0 docs.
    fn foo() {}
}
",
            r"
impl S {
    /// Some
    /// $0 docs.
    fn foo() {}
}
",
        );

        do_check(
            r"
///$0 Some docs
fn foo() {
}
",
            r"
///
/// $0 Some docs
fn foo() {
}
",
        );
    }

    #[test]
    fn does_not_continue_before_doc_comment() {
        do_check_noop(r"$0//! docz");
    }

    #[test]
    fn continues_another_doc_comment() {
        do_check(
            r#"
fn main() {
    //! Documentation for$0 on enter
    let x = 1 + 1;
}
"#,
            r#"
fn main() {
    //! Documentation for
    //! $0 on enter
    let x = 1 + 1;
}
"#,
        );
    }

    #[test]
    fn continues_code_comment_in_the_middle_of_line() {
        do_check(
            r"
fn main() {
    // Fix$0 me
    let x = 1 + 1;
}
",
            r"
fn main() {
    // Fix
    // $0 me
    let x = 1 + 1;
}
",
        );
    }

    #[test]
    fn continues_code_comment_in_the_middle_several_lines() {
        do_check(
            r"
fn main() {
    // Fix$0
    // me
    let x = 1 + 1;
}
",
            r"
fn main() {
    // Fix
    // $0
    // me
    let x = 1 + 1;
}
",
        );
    }

    #[test]
    fn does_not_continue_end_of_line_comment() {
        do_check_noop(
            r"
fn main() {
    // Fix me$0
    let x = 1 + 1;
}
",
        );
    }

    #[test]
    fn continues_end_of_line_comment_with_space() {
        cov_mark::check!(continues_end_of_line_comment_with_space);
        do_check(
            r#"
fn main() {
    // Fix me $0
    let x = 1 + 1;
}
"#,
            r#"
fn main() {
    // Fix me
    // $0
    let x = 1 + 1;
}
"#,
        );
    }

    #[test]
    fn trims_all_trailing_whitespace() {
        do_check(
            "
fn main() {
    // Fix me  \t\t   $0
    let x = 1 + 1;
}
",
            "
fn main() {
    // Fix me
    // $0
    let x = 1 + 1;
}
",
        );
    }
}
<|endoftext|>


<commit_before>//! Shadowsocks service context

use std::{io, net::SocketAddr, sync::Arc};

use byte_string::ByteStr;
use log::warn;

use crate::{
    config::{ReplayAttackPolicy, ServerType},
    crypto::{v1::random_iv_or_salt, CipherKind},
    dns_resolver::DnsResolver,
    security::replay::ReplayProtector,
};

/// Service context
pub struct Context {
    // Protector against replay attack
    // The actual replay detection behavior is implemented in ReplayProtector
    replay_protector: ReplayProtector,
    // Policy against replay attack
    replay_policy: ReplayAttackPolicy,

    // trust-dns resolver, which supports REAL asynchronous resolving, and also customizable
    dns_resolver: Arc<DnsResolver>,

    // Connect IPv6 address first
    ipv6_first: bool,
}

/// `Context` for sharing between services
pub type SharedContext = Arc<Context>;

impl Context {
    /// Create a new `Context` for `Client` or `Server`
    pub fn new(config_type: ServerType) -> Context {
        Context {
            replay_protector: ReplayProtector::new(config_type),
            replay_policy: ReplayAttackPolicy::Default,
            dns_resolver: Arc::new(DnsResolver::system_resolver()),
            ipv6_first: false,
        }
    }

    /// Create a new `Context` shared
    pub fn new_shared(config_type: ServerType) -> SharedContext {
        SharedContext::new(Context::new(config_type))
    }

    /// Check if nonce exist or not
    ///
    /// If not, set into the current bloom filter
    #[inline(always)]
    fn check_nonce_and_set(&self, method: CipherKind, nonce: &[u8]) -> bool {
        match self.replay_policy {
            ReplayAttackPolicy::Ignore => false,
            _ => self.replay_protector.check_nonce_and_set(method, nonce),
        }
    }

    /// Generate nonce (IV or SALT)
    pub fn generate_nonce(&self, method: CipherKind, nonce: &mut [u8], unique: bool) {
        if nonce.is_empty() {
            return;
        }

        loop {
            random_iv_or_salt(nonce);

            // SECURITY: First 6 bytes of payload should be printable characters
            // Observation shows that prepending 6 bytes of printable characters to random payload will exempt it from blocking.
            // by 2022-01-13 gfw.report et al.
            #[cfg(feature = "security-iv-printable-prefix")]
            {
                const SECURITY_PRINTABLE_PREFIX_LEN: usize = 6;
                if nonce.len() >= SECURITY_PRINTABLE_PREFIX_LEN {
                    use rand::Rng;

                    // Printable characters follows definition of isprint in C/C++
                    static ASCII_PRINTABLE_CHARS: &[u8] = br##"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ "##;

                    let mut rng = rand::thread_rng();
                    for b in nonce.iter_mut().take(SECURITY_PRINTABLE_PREFIX_LEN) {
                        *b = ASCII_PRINTABLE_CHARS[rng.gen_range::<usize, _>(0..ASCII_PRINTABLE_CHARS.len())];
                    }
                }
            }

            // Salt already exists, generate a new one.
            if unique && self.check_nonce_and_set(method, nonce) {
                continue;
            }

            break;
        }
    }

    /// Check nonce replay
    pub fn check_nonce_replay(&self, method: CipherKind, nonce: &[u8]) -> io::Result<()> {
        if nonce.is_empty() {
            return Ok(());
        }

        match self.replay_policy {
            ReplayAttackPolicy::Default => {
                #[cfg(feature = "aead-cipher-2022")]
                if method.is_aead_2022() {
                    return if self.replay_protector.check_nonce_and_set(method, nonce) {
                        let err = io::Error::new(io::ErrorKind::Other, "detected repeated nonce (iv/salt)");
                        Err(err)
                    } else {
                        Ok(())
                    };
                }

                // AEAD, Stream should ignore by default
                Ok(())
            }
            ReplayAttackPolicy::Ignore => Ok(()),
            ReplayAttackPolicy::Detect => {
                if self.replay_protector.check_nonce_and_set(method, nonce) {
                    warn!("detected repeated nonce (iv/salt) {:?}", ByteStr::new(nonce));
                }
                Ok(())
            }
            ReplayAttackPolicy::Reject => {
                if self.replay_protector.check_nonce_and_set(method, nonce) {
                    let err = io::Error::new(io::ErrorKind::Other, "detected repeated nonce (iv/salt)");
                    Err(err)
                } else {
                    Ok(())
                }
            }
        }
    }

    /// Set a DNS resolver
    ///
    /// The resolver should be wrapped in an `Arc`, because it could be shared with the other servers
    pub fn set_dns_resolver(&mut self, resolver: Arc<DnsResolver>) {
        self.dns_resolver = resolver;
    }

    /// Get the DNS resolver
    pub fn dns_resolver(&self) -> &Arc<DnsResolver> {
        &self.dns_resolver
    }

    /// Resolves DNS address to `SocketAddr`s
    #[allow(clippy::needless_lifetimes)]
    pub async fn dns_resolve<'a>(&self, addr: &'a str, port: u16) -> io::Result<impl Iterator<Item = SocketAddr> + 'a> {
        self.dns_resolver.resolve(addr, port).await
    }

    /// Try to connect IPv6 addresses first if hostname could be resolved to both IPv4 and IPv6
    pub fn set_ipv6_first(&mut self, ipv6_first: bool) {
        self.ipv6_first = ipv6_first;
    }

    /// Try to connect IPv6 addresses first if hostname could be resolved to both IPv4 and IPv6
    pub fn ipv6_first(&self) -> bool {
        self.ipv6_first
    }

    /// Set policy against replay attack
    pub fn set_replay_attack_policy(&mut self, replay_policy: ReplayAttackPolicy) {
        self.replay_policy = replay_policy;
    }

    /// Get policy against replay attach
    pub fn replay_attack_policy(&self) -> ReplayAttackPolicy {
        self.replay_policy
    }
}
<commit_msg>replay policy must be "reject" for aead-2022<commit_after>//! Shadowsocks service context

use std::{io, net::SocketAddr, sync::Arc};

use byte_string::ByteStr;
use log::warn;

use crate::{
    config::{ReplayAttackPolicy, ServerType},
    crypto::{v1::random_iv_or_salt, CipherKind},
    dns_resolver::DnsResolver,
    security::replay::ReplayProtector,
};

/// Service context
pub struct Context {
    // Protector against replay attack
    // The actual replay detection behavior is implemented in ReplayProtector
    replay_protector: ReplayProtector,
    // Policy against replay attack
    replay_policy: ReplayAttackPolicy,

    // trust-dns resolver, which supports REAL asynchronous resolving, and also customizable
    dns_resolver: Arc<DnsResolver>,

    // Connect IPv6 address first
    ipv6_first: bool,
}

/// `Context` for sharing between services
pub type SharedContext = Arc<Context>;

impl Context {
    /// Create a new `Context` for `Client` or `Server`
    pub fn new(config_type: ServerType) -> Context {
        Context {
            replay_protector: ReplayProtector::new(config_type),
            replay_policy: ReplayAttackPolicy::Default,
            dns_resolver: Arc::new(DnsResolver::system_resolver()),
            ipv6_first: false,
        }
    }

    /// Create a new `Context` shared
    pub fn new_shared(config_type: ServerType) -> SharedContext {
        SharedContext::new(Context::new(config_type))
    }

    /// Check if nonce exist or not
    ///
    /// If not, set into the current bloom filter
    #[inline(always)]
    fn check_nonce_and_set(&self, method: CipherKind, nonce: &[u8]) -> bool {
        match self.replay_policy {
            ReplayAttackPolicy::Ignore => false,
            _ => self.replay_protector.check_nonce_and_set(method, nonce),
        }
    }

    /// Generate nonce (IV or SALT)
    pub fn generate_nonce(&self, method: CipherKind, nonce: &mut [u8], unique: bool) {
        if nonce.is_empty() {
            return;
        }

        loop {
            random_iv_or_salt(nonce);

            // SECURITY: First 6 bytes of payload should be printable characters
            // Observation shows that prepending 6 bytes of printable characters to random payload will exempt it from blocking.
            // by 2022-01-13 gfw.report et al.
            #[cfg(feature = "security-iv-printable-prefix")]
            {
                const SECURITY_PRINTABLE_PREFIX_LEN: usize = 6;
                if nonce.len() >= SECURITY_PRINTABLE_PREFIX_LEN {
                    use rand::Rng;

                    // Printable characters follows definition of isprint in C/C++
                    static ASCII_PRINTABLE_CHARS: &[u8] = br##"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ "##;

                    let mut rng = rand::thread_rng();
                    for b in nonce.iter_mut().take(SECURITY_PRINTABLE_PREFIX_LEN) {
                        *b = ASCII_PRINTABLE_CHARS[rng.gen_range::<usize, _>(0..ASCII_PRINTABLE_CHARS.len())];
                    }
                }
            }

            // Salt already exists, generate a new one.
            if unique && self.check_nonce_and_set(method, nonce) {
                continue;
            }

            break;
        }
    }

    /// Check nonce replay
    pub fn check_nonce_replay(&self, method: CipherKind, nonce: &[u8]) -> io::Result<()> {
        if nonce.is_empty() {
            return Ok(());
        }

        #[allow(unused_mut)]
        let mut replay_policy = self.replay_policy;

        #[cfg(feature = "aead-cipher-2022")]
        if method.is_aead_2022() {
            // AEAD-2022 can't be ignored.
            replay_policy = ReplayAttackPolicy::Reject;
        }

        match replay_policy {
            ReplayAttackPolicy::Default | ReplayAttackPolicy::Ignore => Ok(()),
            ReplayAttackPolicy::Detect => {
                if self.replay_protector.check_nonce_and_set(method, nonce) {
                    warn!("detected repeated nonce (iv/salt) {:?}", ByteStr::new(nonce));
                }
                Ok(())
            }
            ReplayAttackPolicy::Reject => {
                if self.replay_protector.check_nonce_and_set(method, nonce) {
                    let err = io::Error::new(io::ErrorKind::Other, "detected repeated nonce (iv/salt)");
                    Err(err)
                } else {
                    Ok(())
                }
            }
        }
    }

    /// Set a DNS resolver
    ///
    /// The resolver should be wrapped in an `Arc`, because it could be shared with the other servers
    pub fn set_dns_resolver(&mut self, resolver: Arc<DnsResolver>) {
        self.dns_resolver = resolver;
    }

    /// Get the DNS resolver
    pub fn dns_resolver(&self) -> &Arc<DnsResolver> {
        &self.dns_resolver
    }

    /// Resolves DNS address to `SocketAddr`s
    #[allow(clippy::needless_lifetimes)]
    pub async fn dns_resolve<'a>(&self, addr: &'a str, port: u16) -> io::Result<impl Iterator<Item = SocketAddr> + 'a> {
        self.dns_resolver.resolve(addr, port).await
    }

    /// Try to connect IPv6 addresses first if hostname could be resolved to both IPv4 and IPv6
    pub fn set_ipv6_first(&mut self, ipv6_first: bool) {
        self.ipv6_first = ipv6_first;
    }

    /// Try to connect IPv6 addresses first if hostname could be resolved to both IPv4 and IPv6
    pub fn ipv6_first(&self) -> bool {
        self.ipv6_first
    }

    /// Set policy against replay attack
    pub fn set_replay_attack_policy(&mut self, replay_policy: ReplayAttackPolicy) {
        self.replay_policy = replay_policy;
    }

    /// Get policy against replay attach
    pub fn replay_attack_policy(&self) -> ReplayAttackPolicy {
        self.replay_policy
    }
}
<|endoftext|>


<commit_before>use emailaddress::EmailAddress;
use iron::Url;
use serde_json::de::from_reader;
use serde_json::value::Value;
use super::error::{BrokerError, BrokerResult};
use super::hyper::client::Client as HttpClient;
use super::hyper::header::ContentType as HyContentType;
use super::hyper::header::Headers;
use super::{AppConfig, create_jwt};
use super::crypto::{session_id, verify_jws};
use super::store_cache::CacheKey;
use time::now_utc;
use url::percent_encoding::{utf8_percent_encode, QUERY_ENCODE_SET};

/// Macro used to extract a bunch of parameters from a JSON Value.
///
/// This macro will return from the contained method with a BrokerResult when
/// it fails to parse one of the params. The error will contain the description
/// string from the second parameter.
///
/// ```
/// extract_json_fields!(value, "example document", {
///     foo = as_str("foo"),
///     bar = as_i64("BAR"),
/// });
/// ```
macro_rules! extract_json_fields {
    ( $input:expr , $descr:expr , { $( $var:ident = $conv:ident ( $key:tt ) ),* } ) => {
        let descr = $descr;
        $(
            let $var = match $input.find($key).and_then(|v| v.$conv()) {
                None => {
                    return Err(BrokerError::Custom(format!("{} missing from {}", $key, descr)))
                },
                Some(value) => {
                    value
                }
            };
        )*
    }
}

/// Helper method to issue an OAuth authorization request.
///
/// When an authentication request comes in and matches one of the "famous"
/// identity providers configured in the `AppConfig`, we redirect the client
/// to an Authentication Request URL, which we discover by reading the
/// provider's configured Discovery URL. We pass in the client ID we received
/// when pre-registering for the provider, as well as a callback URL which the
/// user will be redirected back to after confirming (or denying) the
/// Authentication Request. Included in the request is a nonce which we can
/// later use to definitively match the callback to this request.
pub fn request(app: &AppConfig, email_addr: EmailAddress, client_id: &str, nonce: &str, redirect_uri: &str)
               -> BrokerResult<Url> {

    let session = session_id(&email_addr, client_id);

    // Store the nonce and the RP's `redirect_uri` in Redis for use in the
    // callback handler.
    try!(app.store.store_session(&session, &[
        ("type", "oidc"),
        ("email", &email_addr.to_string()),
        ("client_id", client_id),
        ("nonce", nonce),
        ("redirect", redirect_uri),
    ]));

    let client = HttpClient::new();

    // Retrieve the provider's Discovery document and extract the
    // `authorization_endpoint` from it.
    let domain = &email_addr.domain;
    let provider = &app.providers[domain];
    let config_obj: Value = try!(
        app.store.cache.fetch_json_url(
            &app.store,
            CacheKey::Discovery { domain: &email_addr.domain },
            &client,
            &provider.discovery
        )
    );
    extract_json_fields!(config_obj, format!("{} discovery document", domain), {
        authz_base = as_str("authorization_endpoint")
    });

    // Create the URL to redirect to, properly escaping all parameters.
    Url::parse(&vec![
        authz_base,
        "?",
        "client_id=",
        &utf8_percent_encode(&provider.client_id, QUERY_ENCODE_SET).to_string(),
        "&response_type=code",
        "&scope=",
        &utf8_percent_encode("openid email", QUERY_ENCODE_SET).to_string(),
        "&redirect_uri=",
        &utf8_percent_encode(&format!("{}/callback", &app.base_url),
                             QUERY_ENCODE_SET).to_string(),
        "&state=",
        &utf8_percent_encode(&session, QUERY_ENCODE_SET).to_string(),
        "&login_hint=",
        &utf8_percent_encode(&email_addr.to_string(), QUERY_ENCODE_SET).to_string(),
    ].join("")).map_err(|_| {
        BrokerError::Custom("authorization_endpoint is an invalid URL".to_string())
    })

}

pub fn canonicalize_google(email: String) -> String {
    let at = email.find("@").unwrap();
    let (user, domain) = email.split_at(at);
    let domain = &domain[1..];
    let user = &user.replace(".", ""); // Ignore dots

    // Trim plus addresses
    let user = match user.find("+") {
        Some(pos) => user.split_at(pos).0,
        None => user,
    };

    // Normalize googlemail.com to gmail.com
    let domain = if domain == "googlemail.com" {
        "gmail.com"
    } else {
        domain
    };
    user.to_string() + "@" + domain
}

pub fn canonicalized(email: &str) -> String {
    let normalized = email.to_lowercase();
    if normalized.ends_with("@gmail.com") || normalized.ends_with("@googlemail.com") {
        canonicalize_google(normalized)
    } else {
        normalized
    }
}

/// Helper method to verify OAuth authentication result.
///
/// Match the returned email address and nonce against our Redis data, then
/// extract the identity token returned by the provider and verify it. Return
/// an identity token for the RP if successful, or an error message otherwise.
pub fn verify(app: &AppConfig, session: &str, code: &str)
              -> BrokerResult<(String, String)> {

    // Validate that the callback matches an auth request in Redis.
    let stored = try!(app.store.get_session(&session));
    if &stored["type"] != "oidc" {
        return Err(BrokerError::Custom("invalid session".to_string()));
    }

    let email_addr = EmailAddress::new(&stored["email"]).unwrap();
    let origin = &stored["client_id"];
    let nonce = &stored["nonce"];

    let client = HttpClient::new();

    // Request the provider's Discovery document to get the
    // `token_endpoint` and `jwks_uri` values from it. TODO: save these
    // when requesting the Discovery document in the `oauth_request()`
    // function, and/or cache them by provider host.
    let domain = &email_addr.domain;
    let provider = &app.providers[domain];
    let config_obj: Value = try!(
        app.store.cache.fetch_json_url(
            &app.store,
            CacheKey::Discovery { domain: &email_addr.domain },
            &client,
            &provider.discovery
        )
    );
    extract_json_fields!(config_obj, format!("{} discovery document", domain), {
        token_url = as_str("token_endpoint"),
        jwks_url = as_str("jwks_uri")
    });

    // Create form data for the Token Request, where we exchange the code
    // received in this callback request for an identity token (while
    // proving our identity by passing the client secret value).
    let body: String = vec![
        "code=",
        &utf8_percent_encode(code, QUERY_ENCODE_SET).to_string(),
        "&client_id=",
        &utf8_percent_encode(&provider.client_id, QUERY_ENCODE_SET).to_string(),
        "&client_secret=",
        &utf8_percent_encode(&provider.secret, QUERY_ENCODE_SET).to_string(),
        "&redirect_uri=",
        &utf8_percent_encode(&format!("{}/callback", &app.base_url),
                             QUERY_ENCODE_SET).to_string(),
        "&grant_type=authorization_code",
    ].join("");

    // Send the Token Request and extract the `id_token` from the response.
    let token_obj: Value = {
        let mut headers = Headers::new();
        headers.set(HyContentType::form_url_encoded());
        let rsp = client.post(token_url).headers(headers).body(&body).send().unwrap();
        from_reader(rsp).unwrap()
    };
    extract_json_fields!(token_obj, format!("{} token response", domain), {
        id_token = as_str("id_token")
    });

    // Grab the keys from the provider, then verify the signature.
    let jwt_payload = {
        let keys_obj: Value = try!(
            app.store.cache.fetch_json_url(
                &app.store,
                CacheKey::KeySet { domain: &email_addr.domain },
                &client,
                &jwks_url
            )
        );
        verify_jws(id_token, &keys_obj).unwrap()
    };

    // Verify the claims contained in the token.
    extract_json_fields!(jwt_payload, format!("{} token", domain), {
        iss = as_str("iss"),
        aud = as_str("aud"),
        token_addr = as_str("email"),
        exp = as_i64("exp")
    });
    let issuer_origin = vec!["https://", &provider.issuer].join("");
    assert!(iss == provider.issuer || iss == issuer_origin);
    assert!(aud == provider.client_id);
    assert!(canonicalized(token_addr) == canonicalized(&email_addr.to_string()));
    let now = now_utc().to_timespec().sec;
    assert!(now < exp);

    // If everything is okay, build a new identity token and send it
    // to the relying party.
    let id_token = create_jwt(app, &email_addr.to_string(), origin, nonce);
    let redirect = &stored["redirect"];
    Ok((id_token, redirect.to_string()))

}

#[cfg(test)]
mod tests {
    use super::canonicalized;
    #[test]
    fn test_canonicalized_google() {
        assert_eq!(canonicalized("example.foo+bar@example.com"),
                   "example.foo+bar@example.com");
        assert_eq!(canonicalized("example@gmail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example@googlemail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example.foo@gmail.com"),
                   "examplefoo@gmail.com");
        assert_eq!(canonicalized("example+bar@gmail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example.foo+bar@googlemail.com"),
                   "examplefoo@gmail.com");
    }
}
<commit_msg>Handle remaining `oidc::verify` errors.<commit_after>use emailaddress::EmailAddress;
use iron::Url;
use serde_json::de::from_reader;
use serde_json::value::Value;
use super::error::{BrokerError, BrokerResult};
use super::hyper::client::Client as HttpClient;
use super::hyper::header::ContentType as HyContentType;
use super::hyper::header::Headers;
use super::{AppConfig, create_jwt};
use super::crypto::{session_id, verify_jws};
use super::store_cache::CacheKey;
use time::now_utc;
use url::percent_encoding::{utf8_percent_encode, QUERY_ENCODE_SET};

/// Macro used to extract a bunch of parameters from a JSON Value.
///
/// This macro will return from the contained method with a BrokerResult when
/// it fails to parse one of the params. The error will contain the description
/// string from the second parameter.
///
/// ```
/// extract_json_fields!(value, "example document", {
///     foo = as_str("foo"),
///     bar = as_i64("BAR"),
/// });
/// ```
macro_rules! extract_json_fields {
    ( $input:expr , $descr:expr , { $( $var:ident = $conv:ident ( $key:tt ) ),* } ) => {
        let descr = $descr;
        $(
            let $var = match $input.find($key).and_then(|v| v.$conv()) {
                None => {
                    return Err(BrokerError::Custom(format!("{} missing from {}", $key, descr)))
                },
                Some(value) => {
                    value
                }
            };
        )*
    }
}

/// Helper method to issue an OAuth authorization request.
///
/// When an authentication request comes in and matches one of the "famous"
/// identity providers configured in the `AppConfig`, we redirect the client
/// to an Authentication Request URL, which we discover by reading the
/// provider's configured Discovery URL. We pass in the client ID we received
/// when pre-registering for the provider, as well as a callback URL which the
/// user will be redirected back to after confirming (or denying) the
/// Authentication Request. Included in the request is a nonce which we can
/// later use to definitively match the callback to this request.
pub fn request(app: &AppConfig, email_addr: EmailAddress, client_id: &str, nonce: &str, redirect_uri: &str)
               -> BrokerResult<Url> {

    let session = session_id(&email_addr, client_id);

    // Store the nonce and the RP's `redirect_uri` in Redis for use in the
    // callback handler.
    try!(app.store.store_session(&session, &[
        ("type", "oidc"),
        ("email", &email_addr.to_string()),
        ("client_id", client_id),
        ("nonce", nonce),
        ("redirect", redirect_uri),
    ]));

    let client = HttpClient::new();

    // Retrieve the provider's Discovery document and extract the
    // `authorization_endpoint` from it.
    let domain = &email_addr.domain;
    let provider = &app.providers[domain];
    let config_obj: Value = try!(
        app.store.cache.fetch_json_url(
            &app.store,
            CacheKey::Discovery { domain: &email_addr.domain },
            &client,
            &provider.discovery
        )
    );
    extract_json_fields!(config_obj, format!("{} discovery document", domain), {
        authz_base = as_str("authorization_endpoint")
    });

    // Create the URL to redirect to, properly escaping all parameters.
    Url::parse(&vec![
        authz_base,
        "?",
        "client_id=",
        &utf8_percent_encode(&provider.client_id, QUERY_ENCODE_SET).to_string(),
        "&response_type=code",
        "&scope=",
        &utf8_percent_encode("openid email", QUERY_ENCODE_SET).to_string(),
        "&redirect_uri=",
        &utf8_percent_encode(&format!("{}/callback", &app.base_url),
                             QUERY_ENCODE_SET).to_string(),
        "&state=",
        &utf8_percent_encode(&session, QUERY_ENCODE_SET).to_string(),
        "&login_hint=",
        &utf8_percent_encode(&email_addr.to_string(), QUERY_ENCODE_SET).to_string(),
    ].join("")).map_err(|_| {
        BrokerError::Custom("authorization_endpoint is an invalid URL".to_string())
    })

}

pub fn canonicalize_google(email: String) -> String {
    let at = email.find("@").unwrap();
    let (user, domain) = email.split_at(at);
    let domain = &domain[1..];
    let user = &user.replace(".", ""); // Ignore dots

    // Trim plus addresses
    let user = match user.find("+") {
        Some(pos) => user.split_at(pos).0,
        None => user,
    };

    // Normalize googlemail.com to gmail.com
    let domain = if domain == "googlemail.com" {
        "gmail.com"
    } else {
        domain
    };
    user.to_string() + "@" + domain
}

pub fn canonicalized(email: &str) -> String {
    let normalized = email.to_lowercase();
    if normalized.ends_with("@gmail.com") || normalized.ends_with("@googlemail.com") {
        canonicalize_google(normalized)
    } else {
        normalized
    }
}

/// Helper method to verify OAuth authentication result.
///
/// Match the returned email address and nonce against our Redis data, then
/// extract the identity token returned by the provider and verify it. Return
/// an identity token for the RP if successful, or an error message otherwise.
pub fn verify(app: &AppConfig, session: &str, code: &str)
              -> BrokerResult<(String, String)> {

    // Validate that the callback matches an auth request in Redis.
    let stored = try!(app.store.get_session(&session));
    if &stored["type"] != "oidc" {
        return Err(BrokerError::Custom("invalid session".to_string()));
    }

    let email_addr = EmailAddress::new(&stored["email"]).unwrap();
    let origin = &stored["client_id"];
    let nonce = &stored["nonce"];

    let client = HttpClient::new();

    // Request the provider's Discovery document to get the
    // `token_endpoint` and `jwks_uri` values from it. TODO: save these
    // when requesting the Discovery document in the `oauth_request()`
    // function, and/or cache them by provider host.
    let domain = &email_addr.domain;
    let provider = &app.providers[domain];
    let config_obj: Value = try!(
        app.store.cache.fetch_json_url(
            &app.store,
            CacheKey::Discovery { domain: &email_addr.domain },
            &client,
            &provider.discovery
        )
    );
    extract_json_fields!(config_obj, format!("{} discovery document", domain), {
        token_url = as_str("token_endpoint"),
        jwks_url = as_str("jwks_uri")
    });

    // Create form data for the Token Request, where we exchange the code
    // received in this callback request for an identity token (while
    // proving our identity by passing the client secret value).
    let body: String = vec![
        "code=",
        &utf8_percent_encode(code, QUERY_ENCODE_SET).to_string(),
        "&client_id=",
        &utf8_percent_encode(&provider.client_id, QUERY_ENCODE_SET).to_string(),
        "&client_secret=",
        &utf8_percent_encode(&provider.secret, QUERY_ENCODE_SET).to_string(),
        "&redirect_uri=",
        &utf8_percent_encode(&format!("{}/callback", &app.base_url),
                             QUERY_ENCODE_SET).to_string(),
        "&grant_type=authorization_code",
    ].join("");

    // Send the Token Request and extract the `id_token` from the response.
    let token_obj: Value = {
        let mut headers = Headers::new();
        headers.set(HyContentType::form_url_encoded());
        try!(
            client.post(token_url).headers(headers).body(&body).send()
                .map_err(|e| {
                    BrokerError::Http(e)
                })
                .and_then(|rsp| {
                    from_reader(rsp).map_err(|_| {
                        BrokerError::Custom("response is not valid JSON".to_string())
                    })
                })
        )
    };
    extract_json_fields!(token_obj, format!("{} token response", domain), {
        id_token = as_str("id_token")
    });

    // Grab the keys from the provider, then verify the signature.
    let jwt_payload = {
        let keys_obj: Value = try!(
            app.store.cache.fetch_json_url(
                &app.store,
                CacheKey::KeySet { domain: &email_addr.domain },
                &client,
                &jwks_url
            )
        );
        try!(
            verify_jws(id_token, &keys_obj).map_err(|_| {
                BrokerError::Custom(format!("could not verify the token received from {}", domain))
            })
        )
    };

    // Verify the claims contained in the token.
    extract_json_fields!(jwt_payload, format!("{} token", domain), {
        iss = as_str("iss"),
        aud = as_str("aud"),
        token_addr = as_str("email"),
        exp = as_i64("exp")
    });
    let issuer_origin = vec!["https://", &provider.issuer].join("");
    assert!(iss == provider.issuer || iss == issuer_origin);
    assert!(aud == provider.client_id);
    assert!(canonicalized(token_addr) == canonicalized(&email_addr.to_string()));
    let now = now_utc().to_timespec().sec;
    assert!(now < exp);

    // If everything is okay, build a new identity token and send it
    // to the relying party.
    let id_token = create_jwt(app, &email_addr.to_string(), origin, nonce);
    let redirect = &stored["redirect"];
    Ok((id_token, redirect.to_string()))

}

#[cfg(test)]
mod tests {
    use super::canonicalized;
    #[test]
    fn test_canonicalized_google() {
        assert_eq!(canonicalized("example.foo+bar@example.com"),
                   "example.foo+bar@example.com");
        assert_eq!(canonicalized("example@gmail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example@googlemail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example.foo@gmail.com"),
                   "examplefoo@gmail.com");
        assert_eq!(canonicalized("example+bar@gmail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example.foo+bar@googlemail.com"),
                   "examplefoo@gmail.com");
    }
}
<|endoftext|>


<commit_before>use log::debug;
use async_std::prelude::*;
use async_std::io;
use async_std::stream;
use async_std::sync::Sender;
use crate::common::Result;
use crate::config::Config;
use crate::events::Event;

pub async fn task<R>(_config: Config, pipein: R, pipe_sender: Sender<Event>) -> Result<()>
where
    R: io::Read + Unpin + Send + 'static
{
    debug!("[task] start");

    let reader = io::BufReader::new(pipein);
    let mut stream = reader.lines()
        .map(|res| {
            let line = res.expect("Error reading from PIPE");

            Event::Packet(line)
        })
        .chain(stream::once(Event::EOF));

    while let Some(event) = stream.next().await {
        debug!("Got event {:?}", event);

        match event {
            Event::Packet(_) => pipe_sender.send(event).await,
            Event::EOF => {
                pipe_sender.send(event).await;
                break;
            },
            _ => (),
        }
    }

    drop(pipe_sender);

    debug!("[task] end");

    Ok(())
}
<commit_msg>No need for "break" in pipe task<commit_after>use log::debug;
use async_std::prelude::*;
use async_std::io;
use async_std::stream;
use async_std::sync::Sender;
use crate::common::Result;
use crate::config::Config;
use crate::events::Event;

pub async fn task<R>(_config: Config, pipein: R, pipe_sender: Sender<Event>) -> Result<()>
where
    R: io::Read + Unpin + Send + 'static
{
    debug!("[task] start");

    let reader = io::BufReader::new(pipein);
    let mut stream = reader.lines()
        .map(|res| {
            let line = res.expect("Error reading from PIPE");

            Event::Packet(line)
        })
        .chain(stream::once(Event::EOF));

    while let Some(event) = stream.next().await {
        pipe_sender.send(event).await;
    }

    drop(pipe_sender);

    debug!("[task] end");

    Ok(())
}
<|endoftext|>


<commit_before>use std::rc::Rc;
use std::cell::RefCell;
use std::io;

use byteorder::{BigEndian, WriteBytesExt};

use mio;

use EventLoop;
use Message;
use protocol::Protocol as Protocol;
use transport::Connection as Connection;

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum PipeStateIdx {
	Handshake,
	Connected
}

pub struct Pipe {
	state: PipeStateIdx,
	handshake_state: HandshakePipeState,
	connected_state: ReadyPipeState
}

impl Pipe {

	pub fn new(id: usize, protocol: &Protocol, connection: Box<Connection>) -> Pipe {
		let conn_ref = Rc::new(RefCell::new(connection));

		Pipe {
			state: PipeStateIdx::Handshake,
			handshake_state: HandshakePipeState::new(id, protocol, conn_ref.clone()),
			connected_state: ReadyPipeState::new(id, conn_ref.clone())
		}
	}

	pub fn is_ready(&self) -> bool {
		self.state == PipeStateIdx::Connected
	}

	pub fn send(&mut self, msg: Message) {
		self.get_state().send(msg);
	}

	fn get_state<'a>(&'a mut self) -> &'a mut PipeState {
		match self.state {
			PipeStateIdx::Handshake => &mut self.handshake_state,
			PipeStateIdx::Connected => &mut self.connected_state
		}
	}

	fn set_state(&mut self, state: PipeStateIdx, event_loop: &mut EventLoop) -> io::Result<()> {
		self.state = state;
		self.get_state().enter(event_loop)
	}

	pub fn init(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
		self.set_state(PipeStateIdx::Handshake, event_loop)
	}

	pub fn ready(&mut self, event_loop: &mut EventLoop, events: mio::EventSet)-> io::Result<()> {
		if events.is_readable() {
			try!(self.readable(event_loop));
		}
		if events.is_writable() {
			try!(self.writable(event_loop));
		}
		Ok(())
	}

	pub fn readable(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
		if let Some(new_state) = try!(self.get_state().readable(event_loop)) {
			try!(self.set_state(new_state, event_loop));
		}

		Ok(())
	}

	pub fn writable(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
		if let Some(new_state) = try!(self.get_state().writable(event_loop)) {
			try!(self.set_state(new_state, event_loop));
		}

		Ok(())
	}

}

trait PipeState {
	fn enter(&mut self, event_loop: &mut EventLoop) -> io::Result<()>;
	fn readable(&mut self, event_loop: &mut EventLoop) -> io::Result<Option<PipeStateIdx>>;
	fn writable(&mut self, event_loop: &mut EventLoop) -> io::Result<Option<PipeStateIdx>>;

	fn send(&mut self, msg: Message) -> io::Result<()>;
}

struct HandshakePipeState {
	id: usize,
	protocol_id: u16,
	protocol_peer_id: u16,
	connection: Rc<RefCell<Box<Connection>>>,
	sent: bool,
	received: bool
}

impl HandshakePipeState {

	fn new(id: usize, protocol: &Protocol, connection: Rc<RefCell<Box<Connection>>>) -> HandshakePipeState {
		HandshakePipeState {
			id: id,
			protocol_id: protocol.id(),
			protocol_peer_id: protocol.peer_id(),
			connection: connection,
			sent: false,
			received: false }
	}

	fn check_received_handshake(&self, handshake: &[u8; 8]) -> io::Result<()> {
		let mut expected_handshake = vec!(0, 83, 80, 0);
		try!(expected_handshake.write_u16::<BigEndian>(self.protocol_peer_id));
		try!(expected_handshake.write_u16::<BigEndian>(0));
		let mut both = handshake.iter().zip(expected_handshake.iter());

		if both.all(|(l,r)| l == r) {
			Ok(())
		} else {
			Err(io::Error::new(io::ErrorKind::InvalidData, "received wrong handshake"))
		}
	}

	fn check_sent_handshake(&self, written: Option<usize>) -> io::Result<()> {
		match written {
			Some(8) => Ok(()),
			_ => Err(io::Error::new(io::ErrorKind::InvalidData, "failed to send handshake"))
		}
	}

	fn read_handshake(&mut self) -> io::Result<()> {
		let mut handshake = [0u8; 8];
		let mut connection = self.connection.borrow_mut();
		try!(
			connection.try_read(&mut handshake).
			and_then(|_| self.check_received_handshake(&handshake)));
		debug!("handshake received !");
		self.received = true;
		Ok(())
	}

	fn write_handshake(&mut self) -> io::Result<()> {
		// handshake is Zero, 'S', 'P', Version, Proto, Rsvd
		let mut handshake = vec!(0, 83, 80, 0);
		try!(handshake.write_u16::<BigEndian>(self.protocol_id));
		try!(handshake.write_u16::<BigEndian>(0));
		let mut connection = self.connection.borrow_mut();
		try!(
			connection.try_write(&handshake).
			and_then(|w| self.check_sent_handshake(w)));
		debug!("handshake sent !");
		self.sent = true;
		Ok(())
	}

	fn register(&self, event_loop: &mut EventLoop, interest: mio::EventSet, poll_opt: mio::PollOpt) -> io::Result<()> {
		let token = mio::Token(self.id);
		let connection = self.connection.borrow();
		let fd = connection.as_evented();

		event_loop.register_opt(fd, token, interest, poll_opt)
	}

	fn reregister(&self, event_loop: &mut EventLoop, interest: mio::EventSet, poll_opt: mio::PollOpt) -> io::Result<()> {
		let token = mio::Token(self.id);
		let connection = self.connection.borrow();
		let fd = connection.as_evented();

		event_loop.reregister(fd, token, interest, poll_opt)
	}

}

impl PipeState for HandshakePipeState {

	fn enter(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
		debug!("Enter handshake state");
		self.sent = false;
		self.received = false;

		let interest = mio::EventSet::readable() | mio::EventSet::writable() | mio::EventSet::hup();
		let poll_opt = mio::PollOpt::oneshot();

		try!(self.register(event_loop, interest, poll_opt));

		Ok(())
	}

	fn readable(&mut self, event_loop: &mut EventLoop) -> io::Result<Option<PipeStateIdx>> {
		if self.received {
			return Ok(None)
		}

		try!(self.read_handshake());

		if self.sent {
			Ok(Some(PipeStateIdx::Connected))
		} else {
			let interest = mio::EventSet::hup() | mio::EventSet::writable();
			let poll_opt = mio::PollOpt::oneshot();

			try!(self.reregister(event_loop, interest, poll_opt));
			Ok(None)
		}
	}

	fn writable(&mut self, event_loop: &mut EventLoop) -> io::Result<Option<PipeStateIdx>> {
		if self.sent {
			return Ok(None)
		}

		try!(self.write_handshake());

		if self.received {
			Ok(Some(PipeStateIdx::Connected))
		} else {
			let interest = mio::EventSet::hup() | mio::EventSet::readable();
			let poll_opt = mio::PollOpt::oneshot();
			try!(self.reregister(event_loop, interest, poll_opt));
			Ok(None)
		}
	}

	fn send(&mut self, _: Message) -> io::Result<()> {
		Ok(())
	}

}

struct ReadyPipeState {
	id: usize,
	connection: Rc<RefCell<Box<Connection>>>,
	sent: bool
}

impl ReadyPipeState {
	fn new(id: usize, connection: Rc<RefCell<Box<Connection>>>) -> ReadyPipeState {
		ReadyPipeState {
			id: id,
			connection: connection,
			sent: false }
	}
}

impl PipeState for ReadyPipeState {
	fn enter(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
		debug!("Enter ready state");
		let token = mio::Token(self.id);
		let connection = self.connection.borrow_mut();
		let fd = connection.as_evented();
		let interest = mio::EventSet::hup() | mio::EventSet::writable();
		let poll_opt = mio::PollOpt::edge();
		try!(event_loop.reregister(fd, token, interest, poll_opt));
		Ok(())
	}

	fn readable(&mut self, _: &mut EventLoop) -> io::Result<Option<PipeStateIdx>> {
		Ok(None)
	}

	fn writable(&mut self, _: &mut EventLoop) -> io::Result<Option<PipeStateIdx>> {
		if self.sent {
			Ok(None)
		} else {
			let mut connection = self.connection.borrow_mut();
			let header = [0, 0, 0, 0, 0, 0, 0, 3];
			let body = [65, 86, 67];

			try!(connection.try_write(&header));
			try!(connection.try_write(&body));
			self.sent = true;
			debug!("dummy msg sent !");

			Ok(None)
		}
	}

	fn send(&mut self, msg: Message) -> io::Result<()> {
		let mut connection = self.connection.borrow_mut();
		let mut prefix = Vec::with_capacity(8);
		try!(prefix.write_u64::<BigEndian>(msg.len() as u64));

		try!(connection.try_write(&prefix));

		if msg.get_header().len() > 0 {
			try!(connection.try_write(msg.get_header()));
		}

		if msg.get_body().len() > 0 {
			try!(connection.try_write(msg.get_body()));
		}

		debug!("msg sent !");
		Ok(())
	}
}<commit_msg>Renames ready pipe state struct to connected<commit_after>use std::rc::Rc;
use std::cell::RefCell;
use std::io;

use byteorder::{BigEndian, WriteBytesExt};

use mio;

use EventLoop;
use Message;
use protocol::Protocol as Protocol;
use transport::Connection as Connection;

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum PipeStateIdx {
	Handshake,
	Connected
}

pub struct Pipe {
	state: PipeStateIdx,
	handshake_state: HandshakePipeState,
	connected_state: ConnectedPipeState
}

impl Pipe {

	pub fn new(id: usize, protocol: &Protocol, connection: Box<Connection>) -> Pipe {
		let conn_ref = Rc::new(RefCell::new(connection));

		Pipe {
			state: PipeStateIdx::Handshake,
			handshake_state: HandshakePipeState::new(id, protocol, conn_ref.clone()),
			connected_state: ConnectedPipeState::new(id, conn_ref.clone())
		}
	}

	pub fn is_ready(&self) -> bool {
		self.state == PipeStateIdx::Connected
	}

	pub fn send(&mut self, msg: Message) {
		self.get_state().send(msg);
	}

	fn get_state<'a>(&'a mut self) -> &'a mut PipeState {
		match self.state {
			PipeStateIdx::Handshake => &mut self.handshake_state,
			PipeStateIdx::Connected => &mut self.connected_state
		}
	}

	fn set_state(&mut self, state: PipeStateIdx, event_loop: &mut EventLoop) -> io::Result<()> {
		self.state = state;
		self.get_state().enter(event_loop)
	}

	pub fn init(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
		self.set_state(PipeStateIdx::Handshake, event_loop)
	}

	pub fn ready(&mut self, event_loop: &mut EventLoop, events: mio::EventSet)-> io::Result<()> {
		if events.is_readable() {
			try!(self.readable(event_loop));
		}
		if events.is_writable() {
			try!(self.writable(event_loop));
		}
		Ok(())
	}

	pub fn readable(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
		if let Some(new_state) = try!(self.get_state().readable(event_loop)) {
			try!(self.set_state(new_state, event_loop));
		}

		Ok(())
	}

	pub fn writable(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
		if let Some(new_state) = try!(self.get_state().writable(event_loop)) {
			try!(self.set_state(new_state, event_loop));
		}

		Ok(())
	}

}

trait PipeState {
	fn enter(&mut self, event_loop: &mut EventLoop) -> io::Result<()>;
	fn readable(&mut self, event_loop: &mut EventLoop) -> io::Result<Option<PipeStateIdx>>;
	fn writable(&mut self, event_loop: &mut EventLoop) -> io::Result<Option<PipeStateIdx>>;

	fn send(&mut self, msg: Message) -> io::Result<()>;
}

struct HandshakePipeState {
	id: usize,
	protocol_id: u16,
	protocol_peer_id: u16,
	connection: Rc<RefCell<Box<Connection>>>,
	sent: bool,
	received: bool
}

impl HandshakePipeState {

	fn new(id: usize, protocol: &Protocol, connection: Rc<RefCell<Box<Connection>>>) -> HandshakePipeState {
		HandshakePipeState {
			id: id,
			protocol_id: protocol.id(),
			protocol_peer_id: protocol.peer_id(),
			connection: connection,
			sent: false,
			received: false }
	}

	fn check_received_handshake(&self, handshake: &[u8; 8]) -> io::Result<()> {
		let mut expected_handshake = vec!(0, 83, 80, 0);
		try!(expected_handshake.write_u16::<BigEndian>(self.protocol_peer_id));
		try!(expected_handshake.write_u16::<BigEndian>(0));
		let mut both = handshake.iter().zip(expected_handshake.iter());

		if both.all(|(l,r)| l == r) {
			Ok(())
		} else {
			Err(io::Error::new(io::ErrorKind::InvalidData, "received wrong handshake"))
		}
	}

	fn check_sent_handshake(&self, written: Option<usize>) -> io::Result<()> {
		match written {
			Some(8) => Ok(()),
			_ => Err(io::Error::new(io::ErrorKind::InvalidData, "failed to send handshake"))
		}
	}

	fn read_handshake(&mut self) -> io::Result<()> {
		let mut handshake = [0u8; 8];
		let mut connection = self.connection.borrow_mut();
		try!(
			connection.try_read(&mut handshake).
			and_then(|_| self.check_received_handshake(&handshake)));
		debug!("handshake received !");
		self.received = true;
		Ok(())
	}

	fn write_handshake(&mut self) -> io::Result<()> {
		// handshake is Zero, 'S', 'P', Version, Proto, Rsvd
		let mut handshake = vec!(0, 83, 80, 0);
		try!(handshake.write_u16::<BigEndian>(self.protocol_id));
		try!(handshake.write_u16::<BigEndian>(0));
		let mut connection = self.connection.borrow_mut();
		try!(
			connection.try_write(&handshake).
			and_then(|w| self.check_sent_handshake(w)));
		debug!("handshake sent !");
		self.sent = true;
		Ok(())
	}

	fn register(&self, event_loop: &mut EventLoop, interest: mio::EventSet, poll_opt: mio::PollOpt) -> io::Result<()> {
		let token = mio::Token(self.id);
		let connection = self.connection.borrow();
		let fd = connection.as_evented();

		event_loop.register_opt(fd, token, interest, poll_opt)
	}

	fn reregister(&self, event_loop: &mut EventLoop, interest: mio::EventSet, poll_opt: mio::PollOpt) -> io::Result<()> {
		let token = mio::Token(self.id);
		let connection = self.connection.borrow();
		let fd = connection.as_evented();

		event_loop.reregister(fd, token, interest, poll_opt)
	}

}

impl PipeState for HandshakePipeState {

	fn enter(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
		debug!("Enter handshake state");
		self.sent = false;
		self.received = false;

		let interest = mio::EventSet::readable() | mio::EventSet::writable() | mio::EventSet::hup();
		let poll_opt = mio::PollOpt::oneshot();

		try!(self.register(event_loop, interest, poll_opt));

		Ok(())
	}

	fn readable(&mut self, event_loop: &mut EventLoop) -> io::Result<Option<PipeStateIdx>> {
		if self.received {
			return Ok(None)
		}

		try!(self.read_handshake());

		if self.sent {
			Ok(Some(PipeStateIdx::Connected))
		} else {
			let interest = mio::EventSet::hup() | mio::EventSet::writable();
			let poll_opt = mio::PollOpt::oneshot();

			try!(self.reregister(event_loop, interest, poll_opt));
			Ok(None)
		}
	}

	fn writable(&mut self, event_loop: &mut EventLoop) -> io::Result<Option<PipeStateIdx>> {
		if self.sent {
			return Ok(None)
		}

		try!(self.write_handshake());

		if self.received {
			Ok(Some(PipeStateIdx::Connected))
		} else {
			let interest = mio::EventSet::hup() | mio::EventSet::readable();
			let poll_opt = mio::PollOpt::oneshot();
			try!(self.reregister(event_loop, interest, poll_opt));
			Ok(None)
		}
	}

	fn send(&mut self, _: Message) -> io::Result<()> {
		Ok(())
	}

}

struct ConnectedPipeState {
	id: usize,
	connection: Rc<RefCell<Box<Connection>>>,
	sent: bool
}

impl ConnectedPipeState {
	fn new(id: usize, connection: Rc<RefCell<Box<Connection>>>) -> ConnectedPipeState {
		ConnectedPipeState {
			id: id,
			connection: connection,
			sent: false }
	}
}

impl PipeState for ConnectedPipeState {
	fn enter(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
		debug!("Enter ready state");
		let token = mio::Token(self.id);
		let connection = self.connection.borrow_mut();
		let fd = connection.as_evented();
		let interest = mio::EventSet::hup() | mio::EventSet::writable();
		let poll_opt = mio::PollOpt::edge();
		try!(event_loop.reregister(fd, token, interest, poll_opt));
		Ok(())
	}

	fn readable(&mut self, _: &mut EventLoop) -> io::Result<Option<PipeStateIdx>> {
		Ok(None)
	}

	fn writable(&mut self, _: &mut EventLoop) -> io::Result<Option<PipeStateIdx>> {
		if self.sent {
			Ok(None)
		} else {
			let mut connection = self.connection.borrow_mut();
			let header = [0, 0, 0, 0, 0, 0, 0, 3];
			let body = [65, 86, 67];

			try!(connection.try_write(&header));
			try!(connection.try_write(&body));
			self.sent = true;
			debug!("dummy msg sent !");

			Ok(None)
		}
	}

	fn send(&mut self, msg: Message) -> io::Result<()> {
		let mut connection = self.connection.borrow_mut();
		let mut prefix = Vec::with_capacity(8);
		try!(prefix.write_u64::<BigEndian>(msg.len() as u64));

		try!(connection.try_write(&prefix));

		if msg.get_header().len() > 0 {
			try!(connection.try_write(msg.get_header()));
		}

		if msg.get_body().len() > 0 {
			try!(connection.try_write(msg.get_body()));
		}

		debug!("msg sent !");
		Ok(())
	}
}<|endoftext|>