<commit_before>use std::collections::BinaryHeap;
use std::fmt;

use platform::Platform;
use profile::Profile;
use traffic::Traffic;
use workload::{Pattern, Workload};
use {Config, Result, Source};

pub struct System {
    platform: Platform,
    traffic: Traffic,
    workload: Workload,
    stats: Stats,
    queue: BinaryHeap<Event>,
}

#[derive(Clone, Debug)]
pub struct Job {
    id: usize,
    arrival: f64,
    pattern: Pattern,
}

#[derive(Clone, Debug)]
pub struct Event {
    pub time: f64,
    pub job: Job,
    pub kind: EventKind,
}

order!(Event(time) descending);

#[derive(Clone, Copy, Debug)]
pub enum EventKind {
    Arrival,
    Start,
    Finish,
}

#[derive(Clone, Copy, Debug, Default)]
pub struct Stats {
    pub created: usize,
    pub arrived: usize,
    pub started: usize,
    pub finished: usize,
}

pub type Increment = (Event, Profile, Profile);

impl System {
    pub fn new(config: &Config, source: &Source) -> Result<System> {
        let platform = {
            let config = some!(config.branch("platform"), "a platform configuration is required");
            try!(Platform::new(&config))
        };
        let traffic = {
            let config = some!(config.branch("traffic"), "a traffic configuration is required");
            try!(Traffic::new(&config, source))
        };
        let workload = {
            let config = some!(config.branch("workload"), "a workload configuration is required");
            try!(Workload::new(&config, source))
        };

        Ok(System {
            platform: platform,
            traffic: traffic,
            workload: workload,
            stats: Stats::default(),
            queue: BinaryHeap::new(),
        })
    }

    #[inline]
    pub fn time_step(&self) -> f64 {
        self.platform.time_step()
    }

    #[inline]
    pub fn units(&self) -> usize {
        self.platform.units()
    }

    getter! { ref stats: Stats }
}

impl System {
    fn tick(&mut self) -> Result<()> {
        match (self.traffic.peek(), self.queue.peek()) {
            (Some(_), None) => {}
            (Some(&arrival), Some(&Event { time, .. })) => if arrival < time {},
            _ => return Ok(()),
        }

        let job = match (self.traffic.next(), self.workload.next()) {
            (Some(arrival), Some(pattern)) => {
                let id = self.stats.created;
                self.stats.created += 1;
                Job { id: id, arrival: arrival, pattern: pattern }
            },
            _ => raise!("failed to generate a new job"),
        };

        self.queue.push(Event { time: job.arrival, job: job.clone(), kind: EventKind::Arrival });

        let (start, finish) = try!(self.platform.push(&job));

        self.queue.push(Event { time: start, job: job.clone(), kind: EventKind::Start });
        self.queue.push(Event { time: finish, job: job, kind: EventKind::Finish });

        Ok(())
    }
}

impl Iterator for System {
    type Item = Increment;

    fn next(&mut self) -> Option<Self::Item> {
        if let Err(error) = self.tick() {
            error!(target: "System", "Failed to update the state ({}).", error);
        }
        let event = match self.queue.pop() {
            Some(event) => event,
            _ => return None,
        };
        let (power, temperature) = match self.platform.next(event.time) {
            Some((power, temperature)) => (power, temperature),
            _ => return None,
        };
        self.stats.account(&event);
        Some((event, power, temperature))
    }
}

impl Job {
    getter! { arrival: f64 }
}

deref! { Job::pattern => Pattern }

impl fmt::Display for Event {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        let pattern = &self.job.pattern;
        write!(formatter, "{:7.2} s | job #{:3} ( {:20} | {:2} units | {:6.2} s ) {:7}",
               self.time, self.job.id, pattern.name, pattern.units,
               (pattern.steps as f64) * pattern.time_step, self.kind)
    }
}

impl fmt::Display for EventKind {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            EventKind::Arrival => "arrival".fmt(formatter),
            EventKind::Start => "start".fmt(formatter),
            EventKind::Finish => "finish".fmt(formatter),
        }
    }
}

impl Stats {
    fn account(&mut self, event: &Event) {
        match event.kind {
            EventKind::Arrival => self.arrived += 1,
            EventKind::Start => self.started += 1,
            EventKind::Finish => self.finished += 1,
        }
    }
}
<commit_msg>Fix a typo<commit_after>use std::collections::BinaryHeap;
use std::fmt;

use platform::Platform;
use profile::Profile;
use traffic::Traffic;
use workload::{Pattern, Workload};
use {Config, Result, Source};

pub struct System {
    platform: Platform,
    traffic: Traffic,
    workload: Workload,
    stats: Stats,
    queue: BinaryHeap<Event>,
}

#[derive(Clone, Debug)]
pub struct Job {
    id: usize,
    arrival: f64,
    pattern: Pattern,
}

#[derive(Clone, Debug)]
pub struct Event {
    pub time: f64,
    pub job: Job,
    pub kind: EventKind,
}

order!(Event(time) descending);

#[derive(Clone, Copy, Debug)]
pub enum EventKind {
    Arrival,
    Start,
    Finish,
}

#[derive(Clone, Copy, Debug, Default)]
pub struct Stats {
    pub created: usize,
    pub arrived: usize,
    pub started: usize,
    pub finished: usize,
}

pub type Increment = (Event, Profile, Profile);

impl System {
    pub fn new(config: &Config, source: &Source) -> Result<System> {
        let platform = {
            let config = some!(config.branch("platform"), "a platform configuration is required");
            try!(Platform::new(&config))
        };
        let traffic = {
            let config = some!(config.branch("traffic"), "a traffic configuration is required");
            try!(Traffic::new(&config, source))
        };
        let workload = {
            let config = some!(config.branch("workload"), "a workload configuration is required");
            try!(Workload::new(&config, source))
        };

        Ok(System {
            platform: platform,
            traffic: traffic,
            workload: workload,
            stats: Stats::default(),
            queue: BinaryHeap::new(),
        })
    }

    #[inline]
    pub fn time_step(&self) -> f64 {
        self.platform.time_step()
    }

    #[inline]
    pub fn units(&self) -> usize {
        self.platform.units()
    }

    getter! { ref stats: Stats }
}

impl System {
    fn tick(&mut self) -> Result<()> {
        match (self.traffic.peek(), self.queue.peek()) {
            (Some(_), None) => {}
            (Some(&arrival), Some(&Event { time, .. })) if arrival < time => {},
            _ => return Ok(()),
        }

        let job = match (self.traffic.next(), self.workload.next()) {
            (Some(arrival), Some(pattern)) => {
                let id = self.stats.created;
                self.stats.created += 1;
                Job { id: id, arrival: arrival, pattern: pattern }
            },
            _ => raise!("failed to generate a new job"),
        };

        self.queue.push(Event { time: job.arrival, job: job.clone(), kind: EventKind::Arrival });

        let (start, finish) = try!(self.platform.push(&job));

        self.queue.push(Event { time: start, job: job.clone(), kind: EventKind::Start });
        self.queue.push(Event { time: finish, job: job, kind: EventKind::Finish });

        Ok(())
    }
}

impl Iterator for System {
    type Item = Increment;

    fn next(&mut self) -> Option<Self::Item> {
        if let Err(error) = self.tick() {
            error!(target: "System", "Failed to update the state ({}).", error);
        }
        let event = match self.queue.pop() {
            Some(event) => event,
            _ => return None,
        };
        let (power, temperature) = match self.platform.next(event.time) {
            Some((power, temperature)) => (power, temperature),
            _ => return None,
        };
        self.stats.account(&event);
        Some((event, power, temperature))
    }
}

impl Job {
    getter! { arrival: f64 }
}

deref! { Job::pattern => Pattern }

impl fmt::Display for Event {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        let pattern = &self.job.pattern;
        write!(formatter, "{:7.2} s | job #{:3} ( {:20} | {:2} units | {:6.2} s ) {:7}",
               self.time, self.job.id, pattern.name, pattern.units,
               (pattern.steps as f64) * pattern.time_step, self.kind)
    }
}

impl fmt::Display for EventKind {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            EventKind::Arrival => "arrival".fmt(formatter),
            EventKind::Start => "start".fmt(formatter),
            EventKind::Finish => "finish".fmt(formatter),
        }
    }
}

impl Stats {
    fn account(&mut self, event: &Event) {
        match event.kind {
            EventKind::Arrival => self.arrived += 1,
            EventKind::Start => self.started += 1,
            EventKind::Finish => self.finished += 1,
        }
    }
}
<|endoftext|>


<commit_before>use rustc_hash::{FxHashMap, FxHashSet};

use hir::{Mutability, Ty};
use ra_db::SourceDatabase;
use ra_prof::profile;
use ra_syntax::{
    ast::{self, NameOwner},
    AstNode, Direction, SmolStr, SyntaxElement, SyntaxKind,
    SyntaxKind::*,
    TextRange, T,
};

use crate::{
    db::RootDatabase,
    name_ref_kind::{classify_name_ref, NameRefKind::*},
    FileId,
};

#[derive(Debug)]
pub struct HighlightedRange {
    pub range: TextRange,
    pub tag: &'static str,
    pub binding_hash: Option<u64>,
}

fn is_control_keyword(kind: SyntaxKind) -> bool {
    match kind {
        T![for]
        | T![loop]
        | T![while]
        | T![continue]
        | T![break]
        | T![if]
        | T![else]
        | T![match]
        | T![return] => true,
        _ => false,
    }
}

fn is_variable_mutable(
    db: &RootDatabase,
    analyzer: &hir::SourceAnalyzer,
    pat: ast::BindPat,
) -> bool {
    if pat.is_mutable() {
        return true;
    }

    let ty = analyzer.type_of_pat(db, &pat.into()).unwrap_or(Ty::Unknown);
    if let Some((_, mutability)) = ty.as_reference() {
        match mutability {
            Mutability::Shared => false,
            Mutability::Mut => true,
        }
    } else {
        false
    }
}

pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRange> {
    let _p = profile("highlight");
    let parse = db.parse(file_id);
    let root = parse.tree().syntax().clone();

    fn calc_binding_hash(file_id: FileId, text: &SmolStr, shadow_count: u32) -> u64 {
        fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 {
            use std::{collections::hash_map::DefaultHasher, hash::Hasher};

            let mut hasher = DefaultHasher::new();
            x.hash(&mut hasher);
            hasher.finish()
        }

        hash((file_id, text, shadow_count))
    }

    // Visited nodes to handle highlighting priorities
    // FIXME: retain only ranges here
    let mut highlighted: FxHashSet<SyntaxElement> = FxHashSet::default();
    let mut bindings_shadow_count: FxHashMap<SmolStr, u32> = FxHashMap::default();

    let mut res = Vec::new();
    for node in root.descendants_with_tokens() {
        if highlighted.contains(&node) {
            continue;
        }
        let mut binding_hash = None;
        let tag = match node.kind() {
            FN_DEF => {
                bindings_shadow_count.clear();
                continue;
            }
            COMMENT => "comment",
            STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string",
            ATTR => "attribute",
            NAME_REF => {
                if let Some(name_ref) = node.as_node().cloned().and_then(ast::NameRef::cast) {
                    // FIXME: try to reuse the SourceAnalyzers
                    let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None);
                    match classify_name_ref(db, &analyzer, &name_ref) {
                        Some(Method(_)) => "function",
                        Some(Macro(_)) => "macro",
                        Some(FieldAccess(_)) => "field",
                        Some(AssocItem(hir::ImplItem::Method(_))) => "function",
                        Some(AssocItem(hir::ImplItem::Const(_))) => "constant",
                        Some(AssocItem(hir::ImplItem::TypeAlias(_))) => "type",
                        Some(Def(hir::ModuleDef::Module(_))) => "module",
                        Some(Def(hir::ModuleDef::Function(_))) => "function",
                        Some(Def(hir::ModuleDef::Struct(_))) => "type",
                        Some(Def(hir::ModuleDef::Union(_))) => "type",
                        Some(Def(hir::ModuleDef::Enum(_))) => "type",
                        Some(Def(hir::ModuleDef::EnumVariant(_))) => "constant",
                        Some(Def(hir::ModuleDef::Const(_))) => "constant",
                        Some(Def(hir::ModuleDef::Static(_))) => "constant",
                        Some(Def(hir::ModuleDef::Trait(_))) => "type",
                        Some(Def(hir::ModuleDef::TypeAlias(_))) => "type",
                        Some(Def(hir::ModuleDef::BuiltinType(_))) => "type",
                        Some(SelfType(_)) => "type",
                        Some(Pat(ptr)) => {
                            let pat = ptr.to_node(&root);
                            if let Some(name) = pat.name() {
                                let text = name.text();
                                let shadow_count =
                                    bindings_shadow_count.entry(text.clone()).or_default();
                                binding_hash =
                                    Some(calc_binding_hash(file_id, &text, *shadow_count))
                            }

                            if is_variable_mutable(db, &analyzer, ptr.to_node(&root)) {
                                "variable.mut"
                            } else {
                                "variable"
                            }
                        }
                        Some(SelfParam(_)) => "type",
                        Some(GenericParam(_)) => "type",
                        None => "text",
                    }
                } else {
                    "text"
                }
            }
            NAME => {
                if let Some(name) = node.as_node().cloned().and_then(ast::Name::cast) {
                    let analyzer = hir::SourceAnalyzer::new(db, file_id, name.syntax(), None);
                    if let Some(pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) {
                        if let Some(name) = pat.name() {
                            let text = name.text();
                            let shadow_count =
                                bindings_shadow_count.entry(text.clone()).or_default();
                            *shadow_count += 1;
                            binding_hash = Some(calc_binding_hash(file_id, &text, *shadow_count))
                        }

                        if is_variable_mutable(db, &analyzer, pat) {
                            "variable.mut"
                        } else {
                            "variable"
                        }
                    } else if name
                        .syntax()
                        .parent()
                        .map(|x| {
                            x.kind() == TYPE_PARAM
                                || x.kind() == STRUCT_DEF
                                || x.kind() == ENUM_DEF
                                || x.kind() == TRAIT_DEF
                                || x.kind() == TYPE_ALIAS_DEF
                        })
                        .unwrap_or(false)
                    {
                        "type"
                    } else {
                        "function"
                    }
                } else {
                    "text"
                }
            }
            INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal",
            LIFETIME => "parameter",
            T![unsafe] => "keyword.unsafe",
            k if is_control_keyword(k) => "keyword.control",
            k if k.is_keyword() => "keyword",
            _ => {
                if let Some(macro_call) = node.as_node().cloned().and_then(ast::MacroCall::cast) {
                    if let Some(path) = macro_call.path() {
                        if let Some(segment) = path.segment() {
                            if let Some(name_ref) = segment.name_ref() {
                                highlighted.insert(name_ref.syntax().clone().into());
                                let range_start = name_ref.syntax().text_range().start();
                                let mut range_end = name_ref.syntax().text_range().end();
                                for sibling in path.syntax().siblings_with_tokens(Direction::Next) {
                                    match sibling.kind() {
                                        T![!] | IDENT => range_end = sibling.text_range().end(),
                                        _ => (),
                                    }
                                }
                                res.push(HighlightedRange {
                                    range: TextRange::from_to(range_start, range_end),
                                    tag: "macro",
                                    binding_hash: None,
                                })
                            }
                        }
                    }
                }
                continue;
            }
        };
        res.push(HighlightedRange { range: node.text_range(), tag, binding_hash })
    }
    res
}

pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String {
    let parse = db.parse(file_id);

    fn rainbowify(seed: u64) -> String {
        use rand::prelude::*;
        let mut rng = SmallRng::seed_from_u64(seed);
        format!(
            "hsl({h},{s}%,{l}%)",
            h = rng.gen_range::<u16, _, _>(0, 361),
            s = rng.gen_range::<u16, _, _>(42, 99),
            l = rng.gen_range::<u16, _, _>(40, 91),
        )
    }

    let mut ranges = highlight(db, file_id);
    ranges.sort_by_key(|it| it.range.start());
    // quick non-optimal heuristic to intersect token ranges and highlighted ranges
    let mut frontier = 0;
    let mut could_intersect: Vec<&HighlightedRange> = Vec::new();

    let mut buf = String::new();
    buf.push_str(&STYLE);
    buf.push_str("
");
    let tokens = parse.tree().syntax().descendants_with_tokens().filter_map(|it| it.into_token());
    for token in tokens {
        could_intersect.retain(|it| token.text_range().start() <= it.range.end());
        while let Some(r) = ranges.get(frontier) {
            if r.range.start() <= token.text_range().end() {
                could_intersect.push(r);
                frontier += 1;
            } else {
                break;
            }
        }
        let text = html_escape(&token.text());
        let ranges = could_intersect
            .iter()
            .filter(|it| token.text_range().is_subrange(&it.range))
            .collect::<Vec<_>>();
        if ranges.is_empty() {
            buf.push_str(&text);
        } else {
            let classes = ranges.iter().map(|x| x.tag).collect::<Vec<_>>().join(" ");
            let binding_hash = ranges.first().and_then(|x| x.binding_hash);
            let color = match (rainbow, binding_hash) {
                (true, Some(hash)) => format!(
                    " data-binding-hash=\"{}\" style=\"color: {};\"",
                    hash,
                    rainbowify(hash)
                ),
                _ => "".into(),
            };
            buf.push_str(&format!("{}", classes, color, text));
        }
    }
    buf.push_str("
");
    buf
}

//FIXME: like, real html escaping
fn html_escape(text: &str) -> String {
    text.replace("<", "<").replace(">", ">")
}

const STYLE: &str = "

";

#[cfg(test)]
mod tests {
    use crate::mock_analysis::single_file;
    use test_utils::{assert_eq_text, project_dir, read_text};

    #[test]
    fn test_highlighting() {
        let (analysis, file_id) = single_file(
            r#"
#[derive(Clone, Debug)]
struct Foo {
    pub x: i32,
    pub y: i32,
}

fn foo<T>() -> T {
    unimplemented!();
    foo::<i32>();
}

// comment
fn main() {
    println!("Hello, {}!", 92);

    let mut vec = Vec::new();
    if true {
        vec.push(Foo { x: 0, y: 1 });
    }
    unsafe { vec.set_len(0); }

    let mut x = 42;
    let y = &mut x;
    let z = &y;

    y;
}
"#
            .trim(),
        );
        let dst_file = project_dir().join("crates/ra_ide_api/src/snapshots/highlighting.html");
        let actual_html = &analysis.highlight_as_html(file_id, false).unwrap();
        let expected_html = &read_text(&dst_file);
        std::fs::write(dst_file, &actual_html).unwrap();
        assert_eq_text!(expected_html, actual_html);
    }

    #[test]
    fn test_rainbow_highlighting() {
        let (analysis, file_id) = single_file(
            r#"
fn main() {
    let hello = "hello";
    let x = hello.to_string();
    let y = hello.to_string();

    let x = "other color please!";
    let y = x.to_string();
}

fn bar() {
    let mut hello = "hello";
}
"#
            .trim(),
        );
        let dst_file =
            project_dir().join("crates/ra_ide_api/src/snapshots/rainbow_highlighting.html");
        let actual_html = &analysis.highlight_as_html(file_id, true).unwrap();
        let expected_html = &read_text(&dst_file);
        std::fs::write(dst_file, &actual_html).unwrap();
        assert_eq_text!(expected_html, actual_html);
    }
}
<commit_msg>Fix syntax-highlighting for fields (`NAMED_FIELD_DEF`)<commit_after>use rustc_hash::{FxHashMap, FxHashSet};

use hir::{Mutability, Ty};
use ra_db::SourceDatabase;
use ra_prof::profile;
use ra_syntax::{
    ast::{self, NameOwner},
    AstNode, Direction, SmolStr, SyntaxElement, SyntaxKind,
    SyntaxKind::*,
    TextRange, T,
};

use crate::{
    db::RootDatabase,
    name_ref_kind::{classify_name_ref, NameRefKind::*},
    FileId,
};

#[derive(Debug)]
pub struct HighlightedRange {
    pub range: TextRange,
    pub tag: &'static str,
    pub binding_hash: Option<u64>,
}

fn is_control_keyword(kind: SyntaxKind) -> bool {
    match kind {
        T![for]
        | T![loop]
        | T![while]
        | T![continue]
        | T![break]
        | T![if]
        | T![else]
        | T![match]
        | T![return] => true,
        _ => false,
    }
}

fn is_variable_mutable(
    db: &RootDatabase,
    analyzer: &hir::SourceAnalyzer,
    pat: ast::BindPat,
) -> bool {
    if pat.is_mutable() {
        return true;
    }

    let ty = analyzer.type_of_pat(db, &pat.into()).unwrap_or(Ty::Unknown);
    if let Some((_, mutability)) = ty.as_reference() {
        match mutability {
            Mutability::Shared => false,
            Mutability::Mut => true,
        }
    } else {
        false
    }
}

pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRange> {
    let _p = profile("highlight");
    let parse = db.parse(file_id);
    let root = parse.tree().syntax().clone();

    fn calc_binding_hash(file_id: FileId, text: &SmolStr, shadow_count: u32) -> u64 {
        fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 {
            use std::{collections::hash_map::DefaultHasher, hash::Hasher};

            let mut hasher = DefaultHasher::new();
            x.hash(&mut hasher);
            hasher.finish()
        }

        hash((file_id, text, shadow_count))
    }

    // Visited nodes to handle highlighting priorities
    // FIXME: retain only ranges here
    let mut highlighted: FxHashSet<SyntaxElement> = FxHashSet::default();
    let mut bindings_shadow_count: FxHashMap<SmolStr, u32> = FxHashMap::default();

    let mut res = Vec::new();
    for node in root.descendants_with_tokens() {
        if highlighted.contains(&node) {
            continue;
        }
        let mut binding_hash = None;
        let tag = match node.kind() {
            FN_DEF => {
                bindings_shadow_count.clear();
                continue;
            }
            COMMENT => "comment",
            STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string",
            ATTR => "attribute",
            NAME_REF => {
                if let Some(name_ref) = node.as_node().cloned().and_then(ast::NameRef::cast) {
                    // FIXME: try to reuse the SourceAnalyzers
                    let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None);
                    match classify_name_ref(db, &analyzer, &name_ref) {
                        Some(Method(_)) => "function",
                        Some(Macro(_)) => "macro",
                        Some(FieldAccess(_)) => "field",
                        Some(AssocItem(hir::ImplItem::Method(_))) => "function",
                        Some(AssocItem(hir::ImplItem::Const(_))) => "constant",
                        Some(AssocItem(hir::ImplItem::TypeAlias(_))) => "type",
                        Some(Def(hir::ModuleDef::Module(_))) => "module",
                        Some(Def(hir::ModuleDef::Function(_))) => "function",
                        Some(Def(hir::ModuleDef::Struct(_))) => "type",
                        Some(Def(hir::ModuleDef::Union(_))) => "type",
                        Some(Def(hir::ModuleDef::Enum(_))) => "type",
                        Some(Def(hir::ModuleDef::EnumVariant(_))) => "constant",
                        Some(Def(hir::ModuleDef::Const(_))) => "constant",
                        Some(Def(hir::ModuleDef::Static(_))) => "constant",
                        Some(Def(hir::ModuleDef::Trait(_))) => "type",
                        Some(Def(hir::ModuleDef::TypeAlias(_))) => "type",
                        Some(Def(hir::ModuleDef::BuiltinType(_))) => "type",
                        Some(SelfType(_)) => "type",
                        Some(Pat(ptr)) => {
                            let pat = ptr.to_node(&root);
                            if let Some(name) = pat.name() {
                                let text = name.text();
                                let shadow_count =
                                    bindings_shadow_count.entry(text.clone()).or_default();
                                binding_hash =
                                    Some(calc_binding_hash(file_id, &text, *shadow_count))
                            }

                            if is_variable_mutable(db, &analyzer, ptr.to_node(&root)) {
                                "variable.mut"
                            } else {
                                "variable"
                            }
                        }
                        Some(SelfParam(_)) => "type",
                        Some(GenericParam(_)) => "type",
                        None => "text",
                    }
                } else {
                    "text"
                }
            }
            NAME => {
                if let Some(name) = node.as_node().cloned().and_then(ast::Name::cast) {
                    let analyzer = hir::SourceAnalyzer::new(db, file_id, name.syntax(), None);
                    if let Some(pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) {
                        if let Some(name) = pat.name() {
                            let text = name.text();
                            let shadow_count =
                                bindings_shadow_count.entry(text.clone()).or_default();
                            *shadow_count += 1;
                            binding_hash = Some(calc_binding_hash(file_id, &text, *shadow_count))
                        }

                        if is_variable_mutable(db, &analyzer, pat) {
                            "variable.mut"
                        } else {
                            "variable"
                        }
                    } else {
                        name.syntax()
                            .parent()
                            .map(|x| match x.kind() {
                                TYPE_PARAM | STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => {
                                    "type"
                                }
                                NAMED_FIELD_DEF => "field",
                                _ => "function",
                            })
                            .unwrap_or("function")
                    }
                } else {
                    "text"
                }
            }
            INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal",
            LIFETIME => "parameter",
            T![unsafe] => "keyword.unsafe",
            k if is_control_keyword(k) => "keyword.control",
            k if k.is_keyword() => "keyword",
            _ => {
                if let Some(macro_call) = node.as_node().cloned().and_then(ast::MacroCall::cast) {
                    if let Some(path) = macro_call.path() {
                        if let Some(segment) = path.segment() {
                            if let Some(name_ref) = segment.name_ref() {
                                highlighted.insert(name_ref.syntax().clone().into());
                                let range_start = name_ref.syntax().text_range().start();
                                let mut range_end = name_ref.syntax().text_range().end();
                                for sibling in path.syntax().siblings_with_tokens(Direction::Next) {
                                    match sibling.kind() {
                                        T![!] | IDENT => range_end = sibling.text_range().end(),
                                        _ => (),
                                    }
                                }
                                res.push(HighlightedRange {
                                    range: TextRange::from_to(range_start, range_end),
                                    tag: "macro",
                                    binding_hash: None,
                                })
                            }
                        }
                    }
                }
                continue;
            }
        };
        res.push(HighlightedRange { range: node.text_range(), tag, binding_hash })
    }
    res
}

pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String {
    let parse = db.parse(file_id);

    fn rainbowify(seed: u64) -> String {
        use rand::prelude::*;
        let mut rng = SmallRng::seed_from_u64(seed);
        format!(
            "hsl({h},{s}%,{l}%)",
            h = rng.gen_range::<u16, _, _>(0, 361),
            s = rng.gen_range::<u16, _, _>(42, 99),
            l = rng.gen_range::<u16, _, _>(40, 91),
        )
    }

    let mut ranges = highlight(db, file_id);
    ranges.sort_by_key(|it| it.range.start());
    // quick non-optimal heuristic to intersect token ranges and highlighted ranges
    let mut frontier = 0;
    let mut could_intersect: Vec<&HighlightedRange> = Vec::new();

    let mut buf = String::new();
    buf.push_str(&STYLE);
    buf.push_str("
");
    let tokens = parse.tree().syntax().descendants_with_tokens().filter_map(|it| it.into_token());
    for token in tokens {
        could_intersect.retain(|it| token.text_range().start() <= it.range.end());
        while let Some(r) = ranges.get(frontier) {
            if r.range.start() <= token.text_range().end() {
                could_intersect.push(r);
                frontier += 1;
            } else {
                break;
            }
        }
        let text = html_escape(&token.text());
        let ranges = could_intersect
            .iter()
            .filter(|it| token.text_range().is_subrange(&it.range))
            .collect::<Vec<_>>();
        if ranges.is_empty() {
            buf.push_str(&text);
        } else {
            let classes = ranges.iter().map(|x| x.tag).collect::<Vec<_>>().join(" ");
            let binding_hash = ranges.first().and_then(|x| x.binding_hash);
            let color = match (rainbow, binding_hash) {
                (true, Some(hash)) => format!(
                    " data-binding-hash=\"{}\" style=\"color: {};\"",
                    hash,
                    rainbowify(hash)
                ),
                _ => "".into(),
            };
            buf.push_str(&format!("{}", classes, color, text));
        }
    }
    buf.push_str("
");
    buf
}

//FIXME: like, real html escaping
fn html_escape(text: &str) -> String {
    text.replace("<", "<").replace(">", ">")
}

const STYLE: &str = "

";

#[cfg(test)]
mod tests {
    use crate::mock_analysis::single_file;
    use test_utils::{assert_eq_text, project_dir, read_text};

    #[test]
    fn test_highlighting() {
        let (analysis, file_id) = single_file(
            r#"
#[derive(Clone, Debug)]
struct Foo {
    pub x: i32,
    pub y: i32,
}

fn foo<T>() -> T {
    unimplemented!();
    foo::<i32>();
}

// comment
fn main() {
    println!("Hello, {}!", 92);

    let mut vec = Vec::new();
    if true {
        vec.push(Foo { x: 0, y: 1 });
    }
    unsafe { vec.set_len(0); }

    let mut x = 42;
    let y = &mut x;
    let z = &y;

    y;
}
"#
            .trim(),
        );
        let dst_file = project_dir().join("crates/ra_ide_api/src/snapshots/highlighting.html");
        let actual_html = &analysis.highlight_as_html(file_id, false).unwrap();
        let expected_html = &read_text(&dst_file);
        std::fs::write(dst_file, &actual_html).unwrap();
        assert_eq_text!(expected_html, actual_html);
    }

    #[test]
    fn test_rainbow_highlighting() {
        let (analysis, file_id) = single_file(
            r#"
fn main() {
    let hello = "hello";
    let x = hello.to_string();
    let y = hello.to_string();

    let x = "other color please!";
    let y = x.to_string();
}

fn bar() {
    let mut hello = "hello";
}
"#
            .trim(),
        );
        let dst_file =
            project_dir().join("crates/ra_ide_api/src/snapshots/rainbow_highlighting.html");
        let actual_html = &analysis.highlight_as_html(file_id, true).unwrap();
        let expected_html = &read_text(&dst_file);
        std::fs::write(dst_file, &actual_html).unwrap();
        assert_eq_text!(expected_html, actual_html);
    }
}
<|endoftext|>


<commit_before>/**
Registers a selector, returning an `Sel`.

# Example
```
# #[macro_use] extern crate objc;
# fn main() {
let sel = sel!(description);
let sel = sel!(setObject:forKey:);
# }
```
*/
#[macro_export]
macro_rules! sel {
    // Declare a function to hide unsafety, otherwise we can trigger the
    // unused_unsafe lint; see rust-lang/rust#8472
    ($name:ident) => ({
        #[inline(always)]
        fn register_sel(name_with_nul: &str) -> $crate::runtime::Sel {
            let ptr = name_with_nul.as_ptr() as *const i8;
            unsafe { $crate::runtime::sel_registerName(ptr) }
        }
        register_sel(concat!(stringify!($name), '\0'))

    });
    ($($name:ident :)+) => ({
        #[inline(always)]
        fn register_sel(name_with_nul: &str) -> $crate::runtime::Sel {
            let ptr = name_with_nul.as_ptr() as *const i8;
            unsafe { $crate::runtime::sel_registerName(ptr) }
        }
        register_sel(concat!($(stringify!($name), ':'),+, '\0'))
    });
}

/**
Sends a message to an object. The first argument should implement the
`ToMessage` trait, and the syntax is similar to the message syntax in
Objective-C. Variadic arguments are not currently supported.

# Example
``` no_run
# #[macro_use] extern crate objc;
# use objc::runtime::Object;
# fn main() {
# unsafe {
let obj: *mut Object;
# let obj: *mut Object = 0 as *mut Object;
let description: *const Object = msg_send![obj, description];
let _: () = msg_send![obj, setArg1:1 arg2:2];
# }
# }
```
*/
#[macro_export]
macro_rules! msg_send {
    (super($obj:expr, $superclass:expr), $name:ident) => ({
        #[inline(always)]
        unsafe fn to_mut<T>(ptr: &T) -> *mut T { ptr as *const T as *mut T }
        let sel = sel!($name);
        $crate::MessageArguments::send_super((), to_mut(&*$obj), $superclass, sel)
    });
    (super($obj:expr, $superclass:expr), $($name:ident : $arg:expr)+) => ({
        #[inline(always)]
        unsafe fn to_mut<T>(ptr: &T) -> *mut T { ptr as *const T as *mut T }
        let sel = sel!($($name:)+);
        $crate::MessageArguments::send_super(($($arg,)*), to_mut(&*$obj), $superclass, sel)
    });
    ($obj:expr, $name:ident) => ({
        #[inline(always)]
        unsafe fn to_mut<T>(ptr: &T) -> *mut T { ptr as *const T as *mut T }
        let sel = sel!($name);
        $crate::MessageArguments::send((), to_mut(&*$obj), sel)
    });
    ($obj:expr, $($name:ident : $arg:expr)+) => ({
        #[inline(always)]
        unsafe fn to_mut<T>(ptr: &T) -> *mut T { ptr as *const T as *mut T }
        let sel = sel!($($name:)+);
        $crate::MessageArguments::send(($($arg,)*), to_mut(&*$obj), sel)
    });
}

/// Implements the `Encode` trait for a `Message` type.
/// Specifically, this will implement `Encode` for reference, pointers, and
/// `Option` references of the given type.
///
/// The first argument should be a static string that is the type encoding
/// to use in the implementation. The second argument is the ident of the name
/// of the type to implement `Encode` for, and any further arguments are
/// used as type parameters for the type.
///
/// # Example
/// ``` ignore
/// impl Message for Object { }
/// encode_message_impl!("@", Object)
///
/// impl<T> Message for Array<T> { }
/// encode_message_impl!("@", Array, T)
/// ```
macro_rules! encode_message_impl {
    ($code:expr, $name:ident) => (
        encode_message_impl!($code, $name,);
    );
    ($code:expr, $name:ident, $($t:ident),*) => (
        impl<'a $(, $t)*> $crate::Encode for &'a $name<$($t),*> {
            fn code() -> &'static str { $code }
        }

        impl<'a $(, $t)*> $crate::Encode for &'a mut $name<$($t),*> {
            fn code() -> &'static str { $code }
        }

        impl<'a $(, $t)*> $crate::Encode for Option<&'a $name<$($t),*>> {
            fn code() -> &'static str { $code }
        }

        impl<'a $(, $t)*> $crate::Encode for Option<&'a mut $name<$($t),*>> {
            fn code() -> &'static str { $code }
        }

        impl<$($t),*> $crate::Encode for *const $name<$($t),*> {
            fn code() -> &'static str { $code }
        }

        impl<$($t),*> $crate::Encode for *mut $name<$($t),*> {
            fn code() -> &'static str { $code }
        }
    );
}
<commit_msg>Don't assume c_char is i8 in sel! macro.<commit_after>/**
Registers a selector, returning an `Sel`.

# Example
```
# #[macro_use] extern crate objc;
# fn main() {
let sel = sel!(description);
let sel = sel!(setObject:forKey:);
# }
```
*/
#[macro_export]
macro_rules! sel {
    // Declare a function to hide unsafety, otherwise we can trigger the
    // unused_unsafe lint; see rust-lang/rust#8472
    ($name:ident) => ({
        #[inline(always)]
        fn register_sel(name_with_nul: &str) -> $crate::runtime::Sel {
            let ptr = name_with_nul.as_ptr() as *const _;
            unsafe { $crate::runtime::sel_registerName(ptr) }
        }
        register_sel(concat!(stringify!($name), '\0'))
    });
    ($($name:ident :)+) => ({
        #[inline(always)]
        fn register_sel(name_with_nul: &str) -> $crate::runtime::Sel {
            let ptr = name_with_nul.as_ptr() as *const _;
            unsafe { $crate::runtime::sel_registerName(ptr) }
        }
        register_sel(concat!($(stringify!($name), ':'),+, '\0'))
    });
}

/**
Sends a message to an object. The first argument should implement the
`ToMessage` trait, and the syntax is similar to the message syntax in
Objective-C. Variadic arguments are not currently supported.

# Example
``` no_run
# #[macro_use] extern crate objc;
# use objc::runtime::Object;
# fn main() {
# unsafe {
let obj: *mut Object;
# let obj: *mut Object = 0 as *mut Object;
let description: *const Object = msg_send![obj, description];
let _: () = msg_send![obj, setArg1:1 arg2:2];
# }
# }
```
*/
#[macro_export]
macro_rules! msg_send {
    (super($obj:expr, $superclass:expr), $name:ident) => ({
        #[inline(always)]
        unsafe fn to_mut<T>(ptr: &T) -> *mut T { ptr as *const T as *mut T }
        let sel = sel!($name);
        $crate::MessageArguments::send_super((), to_mut(&*$obj), $superclass, sel)
    });
    (super($obj:expr, $superclass:expr), $($name:ident : $arg:expr)+) => ({
        #[inline(always)]
        unsafe fn to_mut<T>(ptr: &T) -> *mut T { ptr as *const T as *mut T }
        let sel = sel!($($name:)+);
        $crate::MessageArguments::send_super(($($arg,)*), to_mut(&*$obj), $superclass, sel)
    });
    ($obj:expr, $name:ident) => ({
        #[inline(always)]
        unsafe fn to_mut<T>(ptr: &T) -> *mut T { ptr as *const T as *mut T }
        let sel = sel!($name);
        $crate::MessageArguments::send((), to_mut(&*$obj), sel)
    });
    ($obj:expr, $($name:ident : $arg:expr)+) => ({
        #[inline(always)]
        unsafe fn to_mut<T>(ptr: &T) -> *mut T { ptr as *const T as *mut T }
        let sel = sel!($($name:)+);
        $crate::MessageArguments::send(($($arg,)*), to_mut(&*$obj), sel)
    });
}

/// Implements the `Encode` trait for a `Message` type.
/// Specifically, this will implement `Encode` for reference, pointers, and
/// `Option` references of the given type.
///
/// The first argument should be a static string that is the type encoding
/// to use in the implementation. The second argument is the ident of the name
/// of the type to implement `Encode` for, and any further arguments are
/// used as type parameters for the type.
///
/// # Example
/// ``` ignore
/// impl Message for Object { }
/// encode_message_impl!("@", Object)
///
/// impl<T> Message for Array<T> { }
/// encode_message_impl!("@", Array, T)
/// ```
macro_rules! encode_message_impl {
    ($code:expr, $name:ident) => (
        encode_message_impl!($code, $name,);
    );
    ($code:expr, $name:ident, $($t:ident),*) => (
        impl<'a $(, $t)*> $crate::Encode for &'a $name<$($t),*> {
            fn code() -> &'static str { $code }
        }

        impl<'a $(, $t)*> $crate::Encode for &'a mut $name<$($t),*> {
            fn code() -> &'static str { $code }
        }

        impl<'a $(, $t)*> $crate::Encode for Option<&'a $name<$($t),*>> {
            fn code() -> &'static str { $code }
        }

        impl<'a $(, $t)*> $crate::Encode for Option<&'a mut $name<$($t),*>> {
            fn code() -> &'static str { $code }
        }

        impl<$($t),*> $crate::Encode for *const $name<$($t),*> {
            fn code() -> &'static str { $code }
        }

        impl<$($t),*> $crate::Encode for *mut $name<$($t),*> {
            fn code() -> &'static str { $code }
        }
    );
}
<|endoftext|>


<commit_before>use memory::paging::entry::*;
use memory::paging::ENTRY_COUNT;
use core::ops::{Index, IndexMut};

pub const P4: *mut Table = 0xffffffff_fffff000 as *mut _;

pub struct Table {
    entries: [Entry; ENTRY_COUNT],
}

impl Table {
    pub fn zero(&mut self) {
        for entry in self.entries.iter_mut() {
            entry.set_unused();
        }
    }

    fn next_table_address(&self, index: usize) -> Option<usize> {
        let entry_flags = self[index].flags();
        if entry_flags.contains(PRESENT) && !entry_flags.contains(HUGE_PAGE) {
            let table_address = self as *const _ as usize;
            Some((table_address << 9) | (index << 12))
        } else {
            None
        }
    }

    pub unsafe fn next_table(&self, index: usize) -> Option<&Table> {
        self.next_table_address(index)
            .map(|t| unsafe { &*(t as *const _) })
    }

    pub unsafe fn next_table_mut(&mut self, index: usize) -> Option<&mut Table> {
        self.next_table_address(index)
            .map(|t| unsafe { &mut *(t as *mut _) })
    }
}

impl Index<usize> for Table {
    type Output = Entry;

    fn index(&self, index: usize) -> &Entry {
        &self.entries[index]
    }
}

impl IndexMut<usize> for Table {
    fn index_mut(&mut self, index: usize) -> &mut Entry {
        &mut self.entries[index]
    }
}
<commit_msg>Define the next_table methods only for P4, P3, and P2 tables<commit_after>use memory::paging::entry::*;
use memory::paging::ENTRY_COUNT;
use core::ops::{Index, IndexMut};
use core::marker::PhantomData;

pub const P4: *mut Table<Level4> = 0xffffffff_fffff000 as *mut _;

pub struct Table<L: TableLevel> {
    entries: [Entry; ENTRY_COUNT],
    level: PhantomData<L>,
}

impl<L> Table<L> where L: TableLevel
{
    pub fn zero(&mut self) {
        for entry in self.entries.iter_mut() {
            entry.set_unused();
        }
    }
}

impl<L> Table<L> where L: HierachicalLevel
{
    fn next_table_address(&self, index: usize) -> Option<usize> {
        let entry_flags = self[index].flags();
        if entry_flags.contains(PRESENT) && !entry_flags.contains(HUGE_PAGE) {
            let table_address = self as *const _ as usize;
            Some((table_address << 9) | (index << 12))
        } else {
            None
        }
    }

    pub fn next_table(&self, index: usize) -> Option<&Table<L::NextLevel>> {
        self.next_table_address(index)
            .map(|t| unsafe { &*(t as *const _) })
    }

    pub fn next_table_mut(&mut self, index: usize) -> Option<&mut Table<L::NextLevel>> {
        self.next_table_address(index)
            .map(|t| unsafe { &mut *(t as *mut _) })
    }
}

impl<L> Index<usize> for Table<L> where L: TableLevel
{
    type Output = Entry;

    fn index(&self, index: usize) -> &Entry {
        &self.entries[index]
    }
}

impl<L> IndexMut<usize> for Table<L> where L: TableLevel
{
    fn index_mut(&mut self, index: usize) -> &mut Entry {
        &mut self.entries[index]
    }
}

pub trait TableLevel {}

pub enum Level4 {}
enum Level3 {}
enum Level2 {}
enum Level1 {}

impl TableLevel for Level4 {}
impl TableLevel for Level3 {}
impl TableLevel for Level2 {}
impl TableLevel for Level1 {}

trait HierachicalLevel: TableLevel {
    type NextLevel: TableLevel;
}

impl HierachicalLevel for Level4 {
    type NextLevel = Level3;
}

impl HierachicalLevel for Level3 {
    type NextLevel = Level2;
}

impl HierachicalLevel for Level2 {
    type NextLevel = Level1;
}
<|endoftext|>


<commit_before>//! The main entry point for a simulation.

use std;

use serde_json;
use na;
use kiss3d;
use glfw;

use flate2::read::GzDecoder;
use kiss3d::window::Window;
use glfw::{WindowEvent,Key};

use std::path::Path;
use std::fs::File;
use std::f32::consts::PI;

use timer::Timer;
use misc;
use palette::{Color,Palette};
use objects::{Sphere,Frame,ObjectTracker};

/// The configuration options for a Parviewer instance.
#[derive(Debug, RustcDecodable, RustcEncodable, Serialize, Deserialize)]
pub struct Config {
    /// Pitch of the camera, in degrees
    pub pitch : f32,
    /// Yaw of the camera, in degrees
    pub yaw : f32,
    /// Field-of-view range of the camera, in degrees
    pub fov : f32,
    /// Distance of the camera from the origin
    pub distance : f32,
    /// Window width
    pub width : u32,
    /// Window height
    pub height : u32,
    /// How long to pause before looping. None indicates no looping.
    pub pauseloop : Option<f32>,
    /// framerate limit
    pub framerate : f32,
}

/// Open a `json` or `json.gz` file, and deserialize it into a `Vec<Frame>`
pub fn open_file(path : &Path) -> Result<Vec<Frame>, serde_json::error::Error> {
    let mut buf : std::io::BufReader<File> = std::io::BufReader::new(try!(File::open(path)));
    // let f = try!(File::open(path));

    //~ let coded = json::from_reader(&mut buf).unwrap();
    //~ let mut decoder =json::Decoder::new(coded);
    //~ let frames: Vec<Frame> = Decodable::decode(&mut decoder).unwrap();

    let ext : Option<&str> = path.extension().and_then(|s| {s.to_str()});

    if ext == Some("gz") {
        println!("gz ext!");
    }

    let coded_opt = match ext {
        Some("gz") => {
            let mut gzbuf = try!(GzDecoder::new(buf));
            serde_json::de::from_reader(&mut gzbuf)
        },
        _ => {
            serde_json::de::from_reader(&mut buf)
            }
    };

    coded_opt
}

/// The main entry point,maintaining a window, a Config, objects, etc.
pub struct Parviewer {
    config : Config,
    frames : Vec<Frame>,
    /// Palette
    pub palette : Palette,
    /// Track where we are
    pub timer : Timer,
    /// kiss3d Window
    pub window : Window,
    /// Camera
    pub camera : kiss3d::camera::ArcBall,
    nodes : ObjectTracker<Sphere>,
    font : std::rc::Rc<kiss3d::text::Font>
}

impl Parviewer {
    /// Create a new Parviewer instance from a give Config
    pub fn new(frames : Vec<Frame>, palette : Palette, config : Config)
                -> Result<Parviewer, Box<std::error::Error>> {

        // TODO: this is also a configuration option
        let title : String = format!("Parviewer");
        let width : u32 = config.width;
        let height : u32 = config.height;
        let mut window = Window::new_with_size(&*title, width, height);
        // TODO: cube configuration
        let _ = misc::draw_cube(&mut window);

        let eye              = na::Pnt3::new(0.0f32, 0.0, config.distance);
        let at               = na::orig();
        let mut arc_ball     = kiss3d::camera::ArcBall::new_with_frustrum(config.fov * PI / 180., 0.1, 1024.0, eye, at);

        arc_ball.set_yaw(config.yaw * PI / 180.);
        arc_ball.set_pitch(config.pitch * PI / 180.);

        //window.set_background_color(1.0, 1.0, 1.0);
        window.set_light(kiss3d::light::Light::StickToCamera);
        window.set_framerate_limit(Some(config.framerate as u64));

        let nodes = ObjectTracker::new(&mut window);

        // TODO: config?
        let dts_first = vec![1., 2., 3., 4., 6., 8., 12., 16., 24., 32., 48., 64., 96., 128.];
        let mut dts = dts_first.iter().rev().map(|n|{1./n}).collect::<Vec<f32>>();
        dts.extend(dts_first);
        dts.dedup();

        let mut timer = Timer::new(dts, Some(frames.len()));

        timer.loop_pause = config.pauseloop;
        timer.fps = config.framerate;

        let fontsize = 48;
        let font = misc::inconsolata(fontsize);

        Ok(Parviewer {
            config : config,
            frames : frames,
            palette : palette,
            timer : timer,
            window : window,
            nodes : nodes,
            camera : arc_ball,
            font : font
        })
    }

    /// View the current config.
    pub fn get_config(&self) -> &Config {
        &self.config
    }

    /// Borrow current frame
    pub fn get_frame(&self) -> &Frame {
        let ix = self.timer.get_index();
        &self.frames[ix]
    }

    /// Draw some text in the window, with coordinates in the window frame (i.e., 0 to 1).
    pub fn draw_text(&mut self, t: &str, x: f32, y: f32, color: Color) {
        let max_width = self.window.width() * 2.;
        // TODO: Figure out why the bottom is window.height() * 2.
        let max_height = self.window.height() * 2. - (self.font.height() as f32);
        let text_loc = na::Pnt2::new(x * max_width, y * max_height);
        let text_color = color.to_pnt3();

        self.window.draw_text(t, &text_loc, &self.font, &text_color);
    }

    /// Draw some text in the window, with coordinates in the window frame (i.e., 0 to 1).
    pub fn draw_frame_text(&mut self, x: f32, y: f32, color: Color) {
        let ix = self.timer.get_index();
        let frame = &self.frames[ix];
        if let Some(ref text) = frame.text {
            let max_width = self.window.width() * 2.;
            // TODO: Figure out why the bottom is window.height() * 2.
            let max_height = self.window.height() * 2. - (self.font.height() as f32);
            let text_loc = na::Pnt2::new(x * max_width, y * max_height);
            let text_color = color.to_pnt3();

            self.window.draw_text(&*text, &text_loc, &self.font, &text_color);
        }
    }

    /// Standard key handling, called by run.
    pub fn handle_events(&mut self) {
        for mut event in self.window.events().iter() {
            match event.value {
                WindowEvent::Key(key, _, glfw::Action::Release, _) => {
                    // Default to inhibiting, although this can be overridden
                    let mut inhibit = true;
                    match key {
                        Key::Q => {return;},
                        Key::Comma => {self.timer.slower();},
                        Key::Period => {self.timer.faster();},
                        Key::F => {self.timer.switch_direction();},
                        Key::Up => {
                            self.camera.set_pitch(PI/3.);
                            self.camera.set_yaw(PI/4.);
                        },
                        Key::Down => {
                            self.camera.set_pitch(PI/2.);
                            self.camera.set_yaw(0.);
                        },
                        Key::W => {
                            println!("yaw: {:6.2}, pitch: {:6.2}, distance: {:6.2}",
                                self.camera.yaw() * 180. / PI,
                                self.camera.pitch() * 180. / PI,
                                self.camera.dist()
                            );
                        },
                        Key::Num1 => {self.palette.toggle_partial(0);},
                        Key::Num2 => {self.palette.toggle_partial(1);},
                        Key::Num3 => {self.palette.toggle_partial(2);},
                        Key::Num4 => {self.palette.toggle_partial(3);},
                        Key::Num5 => {self.palette.toggle_partial(4);},
                        Key::Num6 => {self.palette.toggle_partial(5);},
                        Key::Num7 => {self.palette.toggle_partial(6);},
                        Key::Num8 => {self.palette.toggle_partial(7);},
                        Key::Num9 => {self.palette.set_all_partial(true);},
                        Key::Num0 => {self.palette.set_all_partial(false);},
                        code => {
                            println!("You released the key with code: {:?}", code);
                            inhibit = false;
                        }

                    }
                    event.inhibited = inhibit;
                },
                _ => {}
            }
        }
    }

    /// Start the whole running sequence.
    pub fn run<F>(&mut self, mut update: F)
            where   F: FnMut(&mut Parviewer, bool) {
        let mut lastframe : isize = -1;
        while self.window.render_with_camera(&mut self.camera) {
            self.timer.incr();
            let ix = self.timer.get_index();

            let new_index = lastframe != (ix as isize);
            if new_index {
                let ref frame = self.frames[ix];
                self.nodes.update(frame.spheres.iter(), &mut self.palette);
                lastframe = ix as isize;
            }

            update(self, new_index);

            self.handle_events();
        };
    }
}
<commit_msg>Fixed Q key handling<commit_after>//! The main entry point for a simulation.

use std;

use serde_json;
use na;
use kiss3d;
use glfw;

use flate2::read::GzDecoder;
use kiss3d::window::Window;
use glfw::{WindowEvent,Key};

use std::path::Path;
use std::fs::File;
use std::f32::consts::PI;

use timer::Timer;
use misc;
use palette::{Color,Palette};
use objects::{Sphere,Frame,ObjectTracker};

/// The configuration options for a Parviewer instance.
#[derive(Debug, RustcDecodable, RustcEncodable, Serialize, Deserialize)]
pub struct Config {
    /// Pitch of the camera, in degrees
    pub pitch : f32,
    /// Yaw of the camera, in degrees
    pub yaw : f32,
    /// Field-of-view range of the camera, in degrees
    pub fov : f32,
    /// Distance of the camera from the origin
    pub distance : f32,
    /// Window width
    pub width : u32,
    /// Window height
    pub height : u32,
    /// How long to pause before looping. None indicates no looping.
    pub pauseloop : Option<f32>,
    /// framerate limit
    pub framerate : f32,
}

/// Open a `json` or `json.gz` file, and deserialize it into a `Vec<Frame>`
pub fn open_file(path : &Path) -> Result<Vec<Frame>, serde_json::error::Error> {
    let mut buf : std::io::BufReader<File> = std::io::BufReader::new(try!(File::open(path)));
    // let f = try!(File::open(path));

    //~ let coded = json::from_reader(&mut buf).unwrap();
    //~ let mut decoder =json::Decoder::new(coded);
    //~ let frames: Vec<Frame> = Decodable::decode(&mut decoder).unwrap();

    let ext : Option<&str> = path.extension().and_then(|s| {s.to_str()});

    if ext == Some("gz") {
        println!("gz ext!");
    }

    let coded_opt = match ext {
        Some("gz") => {
            let mut gzbuf = try!(GzDecoder::new(buf));
            serde_json::de::from_reader(&mut gzbuf)
        },
        _ => {
            serde_json::de::from_reader(&mut buf)
            }
    };

    coded_opt
}

/// The main entry point,maintaining a window, a Config, objects, etc.
pub struct Parviewer {
    config : Config,
    frames : Vec<Frame>,
    /// Palette
    pub palette : Palette,
    /// Track where we are
    pub timer : Timer,
    /// kiss3d Window
    pub window : Window,
    /// Camera
    pub camera : kiss3d::camera::ArcBall,
    nodes : ObjectTracker<Sphere>,
    font : std::rc::Rc<kiss3d::text::Font>
}

impl Parviewer {
    /// Create a new Parviewer instance from a give Config
    pub fn new(frames : Vec<Frame>, palette : Palette, config : Config)
                -> Result<Parviewer, Box<std::error::Error>> {

        // TODO: this is also a configuration option
        let title : String = format!("Parviewer");
        let width : u32 = config.width;
        let height : u32 = config.height;
        let mut window = Window::new_with_size(&*title, width, height);
        // TODO: cube configuration
        let _ = misc::draw_cube(&mut window);

        let eye              = na::Pnt3::new(0.0f32, 0.0, config.distance);
        let at               = na::orig();
        let mut arc_ball     = kiss3d::camera::ArcBall::new_with_frustrum(config.fov * PI / 180., 0.1, 1024.0, eye, at);

        arc_ball.set_yaw(config.yaw * PI / 180.);
        arc_ball.set_pitch(config.pitch * PI / 180.);

        //window.set_background_color(1.0, 1.0, 1.0);
        window.set_light(kiss3d::light::Light::StickToCamera);
        window.set_framerate_limit(Some(config.framerate as u64));

        let nodes = ObjectTracker::new(&mut window);

        // TODO: config?
        let dts_first = vec![1., 2., 3., 4., 6., 8., 12., 16., 24., 32., 48., 64., 96., 128.];
        let mut dts = dts_first.iter().rev().map(|n|{1./n}).collect::<Vec<f32>>();
        dts.extend(dts_first);
        dts.dedup();

        let mut timer = Timer::new(dts, Some(frames.len()));

        timer.loop_pause = config.pauseloop;
        timer.fps = config.framerate;

        let fontsize = 48;
        let font = misc::inconsolata(fontsize);

        Ok(Parviewer {
            config : config,
            frames : frames,
            palette : palette,
            timer : timer,
            window : window,
            nodes : nodes,
            camera : arc_ball,
            font : font
        })
    }

    /// View the current config.
    pub fn get_config(&self) -> &Config {
        &self.config
    }

    /// Borrow current frame
    pub fn get_frame(&self) -> &Frame {
        let ix = self.timer.get_index();
        &self.frames[ix]
    }

    /// Draw some text in the window, with coordinates in the window frame (i.e., 0 to 1).
    pub fn draw_text(&mut self, t: &str, x: f32, y: f32, color: Color) {
        let max_width = self.window.width() * 2.;
        // TODO: Figure out why the bottom is window.height() * 2.
        let max_height = self.window.height() * 2. - (self.font.height() as f32);
        let text_loc = na::Pnt2::new(x * max_width, y * max_height);
        let text_color = color.to_pnt3();

        self.window.draw_text(t, &text_loc, &self.font, &text_color);
    }

    /// Draw some text in the window, with coordinates in the window frame (i.e., 0 to 1).
    pub fn draw_frame_text(&mut self, x: f32, y: f32, color: Color) {
        let ix = self.timer.get_index();
        let frame = &self.frames[ix];
        if let Some(ref text) = frame.text {
            let max_width = self.window.width() * 2.;
            // TODO: Figure out why the bottom is window.height() * 2.
            let max_height = self.window.height() * 2. - (self.font.height() as f32);
            let text_loc = na::Pnt2::new(x * max_width, y * max_height);
            let text_color = color.to_pnt3();

            self.window.draw_text(&*text, &text_loc, &self.font, &text_color);
        }
    }

    /// Standard key handling, called by run.
    pub fn handle_events(&mut self) {
        for mut event in self.window.events().iter() {
            match event.value {
                WindowEvent::Key(key, _, glfw::Action::Release, _) => {
                    // Default to inhibiting, although this can be overridden
                    let mut inhibit = true;
                    match key {
                        Key::Q => {
                            self.window.close();
                            return;
                        },
                        Key::Comma => {self.timer.slower();},
                        Key::Period => {self.timer.faster();},
                        Key::F => {self.timer.switch_direction();},
                        Key::Up => {
                            self.camera.set_pitch(PI/3.);
                            self.camera.set_yaw(PI/4.);
                        },
                        Key::Down => {
                            self.camera.set_pitch(PI/2.);
                            self.camera.set_yaw(0.);
                        },
                        Key::W => {
                            println!("yaw: {:6.2}, pitch: {:6.2}, distance: {:6.2}",
                                self.camera.yaw() * 180. / PI,
                                self.camera.pitch() * 180. / PI,
                                self.camera.dist()
                            );
                        },
                        Key::Num1 => {self.palette.toggle_partial(0);},
                        Key::Num2 => {self.palette.toggle_partial(1);},
                        Key::Num3 => {self.palette.toggle_partial(2);},
                        Key::Num4 => {self.palette.toggle_partial(3);},
                        Key::Num5 => {self.palette.toggle_partial(4);},
                        Key::Num6 => {self.palette.toggle_partial(5);},
                        Key::Num7 => {self.palette.toggle_partial(6);},
                        Key::Num8 => {self.palette.toggle_partial(7);},
                        Key::Num9 => {self.palette.set_all_partial(true);},
                        Key::Num0 => {self.palette.set_all_partial(false);},
                        code => {
                            println!("You released the key with code: {:?}", code);
                            inhibit = false;
                        }

                    }
                    event.inhibited = inhibit;
                },
                _ => {}
            }
        }
    }

    /// Start the whole running sequence.
    pub fn run<F>(&mut self, mut update: F)
            where   F: FnMut(&mut Parviewer, bool) {
        let mut lastframe : isize = -1;
        while self.window.render_with_camera(&mut self.camera) {
            self.timer.incr();
            let ix = self.timer.get_index();

            let new_index = lastframe != (ix as isize);
            if new_index {
                let ref frame = self.frames[ix];
                self.nodes.update(frame.spheres.iter(), &mut self.palette);
                lastframe = ix as isize;
            }

            update(self, new_index);

            self.handle_events();
        };
    }
}
<|endoftext|>


<commit_before>use super::FindMove;
use model::{Board, Point};
use rand::{self, ThreadRng};

pub struct Player {
    rng: ThreadRng,
}

impl Player {
    pub fn new() -> Self {
        Player {
            rng: rand::thread_rng(),
        }
    }
}

impl FindMove for Player {
    fn find_move(&mut self, board: Board) -> Point {
        let size = board.size();
        let pts = board.move_candidates().points(size);
        rand::sample(&mut self.rng, pts, 1)[0]
    }
}
<commit_msg>Remove deprecated funcutin usage<commit_after>use super::FindMove;
use model::{Board, Point};
use rand::{self, ThreadRng};

pub struct Player {
    rng: ThreadRng,
}

impl Player {
    pub fn new() -> Self {
        Player {
            rng: rand::thread_rng(),
        }
    }
}

impl FindMove for Player {
    fn find_move(&mut self, board: Board) -> Point {
        let size = board.size();
        let pts = board.move_candidates().points(size);
        rand::seq::sample_iter(&mut self.rng, pts, 1).unwrap()[0]
    }
}
<|endoftext|>


<commit_before>//! Library service providers implementation.

extern crate hyper;

use hyper::client::{Client, Response};
use hyper::header::ContentType;
use url::form_urlencoded::byte_serialize;

macro_rules! parse_xml_tag {
    ($fname: ident, $tag: expr) => {
        fn $fname(res: &str) -> Option<String> {
            res.split(&format!("<{}>", $tag))
                .nth(1)
                .unwrap_or("")
                .split(&format!("</{}>", $tag))
                .next()
                .map(String::from)
        }
    }
}

macro_rules! parse_json_tag {
    ($fname: ident, $tag: expr, $prefix: expr) => {
        fn $fname(res: &str) -> Option<String> {
            res.to_owned()
                .split(&format!("\"{}\"", $tag))
                .nth(1)
                .unwrap_or("")
                .split(",")
                .next()
                .unwrap_or("")
                .split("\"")
                .nth(1)
                .map(|v| format!("{}{}", $prefix, v.replace("\\", "")))
        }
    }
}

macro_rules! parse {
    ($name:ident) => {
        fn $name(res: &str) -> Option<String> {
            Some(res.to_owned())
        }
    };
}

macro_rules! request {
    ($name:ident, $method:ident, $req_url:expr) => {
        fn $name(url: &str, client: &Client) -> Option<Response> {
            let url = byte_serialize(url.as_bytes()).collect::<String>();
            client.$method(&format!($req_url, url))
                .send()
                .ok()
        }
    };

    (B, $name:ident, $method: ident, $req_url:expr, $body:expr) => {
        fn $name(url: &str, client: &Client) -> Option<Response> {
            client.$method($req_url)
                .body(&format!($body, url))
                .send()
                .ok()
        }
    };

    ($name:ident, $method:ident, $req_url:expr, $body:expr, $header:expr) => {
        fn $name(url: &str, client: &Client) -> Option<Response> {
            client.$method($req_url)
                .body(&format!($body, url))
                .header($header)
                .send()
                .ok()
        }
    };
}

/// Used to specify which provider to use to generate a short URL.
#[derive(Clone, Copy, Debug)]
pub enum Provider {
    /// http://abv8.me provider
    ///
    /// Notes:
    ///
    /// * You may not shorten more than 20 unique URLs within a 3-minute period.
    /// * You may not shorten more than 60 unique URLs within a 15-minute
    ///   period.
    Abv8,
    /// https://bam.bz provider
    BamBz,
    /// http://bmeo.org provider
    Bmeo,
    /// https://bn.gy provider
    BnGy,
    /// http://fifo.cc provider
    FifoCc,
    /// https://hec.su provider
    ///
    /// Notes:
    ///
    /// * Limited to 3000 API requests per day
    HecSu,
    /// https://is.gd provider
    IsGd,
    /// http://nowlinks.net provider
    NowLinks,
    /// http://phx.co.in provider
    ///
    /// Notes:
    ///
    /// * After some time the service will display ads
    /// * Instead of redirecting, a preview page will be displayed
    /// * Currently unstable
    PhxCoIn,
    /// http://psbe.co provider
    PsbeCo,
    /// http://s.coop provider
    SCoop,
    /// http://readbility.com provider
    Rdd,
    /// http://rlu.ru provider
    ///
    /// Notes:
    ///
    /// * If you send a lot of requests from one IP, it can be
    ///   blocked. If you plan to add more then 100 URLs in one hour, please let
    ///   the technical support know. Otherwise your IP can be blocked
    ///   unexpectedly. Prior added URLs can be deleted.
    Rlu,
    /// http://sirbz.com provider
    ///
    /// Notes:
    ///
    /// * By default, you are limited to 250 requests per 15 minutes.
    SirBz,
    /// http://tinyurl.com provider
    ///
    /// Notes:
    ///
    /// * This service does not provide any API.
    /// * The implementation result depends on the service result web page.
    TinyUrl,
    /// http://tiny.ph provider
    TinyPh,
    /// http://tny.im provider
    TnyIm,
    /// http://url-shortener.io provider
    UrlShortenerIo,
    /// https://v.gd provider
    VGd,
}

impl Provider {
    /// Converts the Provider variant into its domain name equivilant
    pub fn to_name(&self) -> &str {
        match *self {
            Provider::Abv8 => "abv8.me",
            Provider::BamBz => "bam.bz",
            Provider::Bmeo => "bmeo.org",
            Provider::BnGy => "bn.gy",
            Provider::FifoCc => "fifo.cc",
            Provider::HecSu => "hec.su",
            Provider::IsGd => "is.gd",
            Provider::NowLinks => "nowlinks.net",
            Provider::PhxCoIn => "phx.co.in",
            Provider::PsbeCo => "psbe.co",
            Provider::SCoop => "s.coop",
            Provider::SirBz => "sirbz.com",
            Provider::Rdd => "readability.com",
            Provider::Rlu => "rlu.ru",
            Provider::TinyUrl => "tinyurl.com",
            Provider::TinyPh => "tiny.ph",
            Provider::TnyIm => "tny.im",
            Provider::UrlShortenerIo => "url-shortener.io",
            Provider::VGd => "v.gd",
        }
    }
}

/// Returns a vector of all `Provider` variants. This list is in order of
/// provider quality.
///
/// The providers which are discouraged from use - due to problems such as rate
/// limitations - are at the end of the resultant vector.
///
/// Note that some providers may not provide a generated short URL because the
/// submitted URL may already be short enough and would not benefit from
/// shortening via their service.
pub fn providers() -> Vec<Provider> {
    vec![
        Provider::IsGd,
        Provider::BnGy,
        Provider::VGd,
        Provider::Rdd,
        Provider::BamBz,
        Provider::TinyPh,
        Provider::FifoCc,
        Provider::SCoop,
        Provider::Bmeo,
        Provider::UrlShortenerIo,

        // The following list are items that have long response sometimes:
        Provider::TnyIm,

        // The following list are items that are discouraged from use:

        // Reasons:
        //
        // * rate limit (250 requests per 15 minutes)
        // * does not accept short urls (ex: http://google.com)
        Provider::SirBz,
        // Reason: rate limit (100 requests per hour)
        Provider::Rlu,
        // Reason: rate limit (3000 requests per day)
        Provider::HecSu,
        // Reason: rate limit (20r/3min; 60r/15min for a UNIQUE urls only)
        Provider::Abv8,
        // Reason: does not provide an api
        Provider::TinyUrl,
        // Reason: unstable work
        Provider::PsbeCo,

        // The following list are items that show previews instead of direct
        // links.
        Provider::NowLinks,
    ]
}

parse!(abv8_parse);
request!(abv8_req, get, "http://abv8.me/?url={}");

parse_json_tag!(bambz_parse, "url", "");
request!(bambz_req,
         post,
         "https://bam.bz/api/short",
         "target={}",
         ContentType::form_url_encoded());

parse_json_tag!(bmeo_parse, "short", "");
request!(bmeo_req, get, "http://bmeo.org/api.php?url={}");

parse_xml_tag!(bngy_parse, "ShortenedUrl");
request!(bngy_req, get, "https://bn.gy/API.asmx/CreateUrl?real_url={}");

parse_json_tag!(fifocc_parse, "shortner", "http://fifo.cc/");
request!(fifocc_req, get, "https://fifo.cc/api/v2?url={}");

parse_xml_tag!(hecsu_parse, "short");
request!(hecsu_req, get, "https://hec.su/api?url={}&method=xml");

parse!(isgd_parse);
request!(isgd_req, get, "https://is.gd/create.php?format=simple&url={}");

parse!(nowlinks_parse);
request!(nowlinks_req, get, "http://nowlinks.net/api?url={}");

parse!(phxcoin_parse);
request!(phxcoin_req, get, "http://phx.co.in/shrink.asp?url={}");

parse_xml_tag!(psbeco_parse, "ShortUrl");
request!(psbeco_req, get, "http://psbe.co/API.asmx/CreateUrl?real_url={}");

parse!(scoop_parse);
request!(scoop_req,
         get,
         "http://s.coop/devapi.php?action=shorturl&url={}&format=RETURN");

parse_json_tag!(rdd_parse, "rdd_url", "");
request!(B,
         rdd_req,
         post,
         "https://readability.com/api/shortener/v1/urls",
         "url={}");

parse!(rlu_parse);
request!(rlu_req, get, "http://rlu.ru/index.sema?a=api&link={}");

parse_json_tag!(sirbz_parse, "short_link", "");
request!(sirbz_req,
         post,
         "http://sirbz.com/api/shorten_url",
         "url={}",
         ContentType::form_url_encoded());

fn tinyurl_parse(res: &str) -> Option<String> {
    res.split("data-clipboard-text=\"")
        .nth(1)
        .unwrap_or("")
        .split("\">")
        .next()
        .map(String::from)
}
request!(tinyurl_req, get, "http://tinyurl.com/create.php?url={}");

parse_json_tag!(tinyph_parse, "hash", "http://tiny.ph/");
request!(tinyph_req,
         post,
         "http://tiny.ph/api/url/create",
         "url={}",
         ContentType::form_url_encoded());

parse_xml_tag!(tnyim_parse, "shorturl");
request!(tnyim_req, get, "http://tny.im/yourls-api.php?action=shorturl&url={}");

parse!(urlshortenerio_parse);
request!(urlshortenerio_req,
         post,
         "http://url-shortener.io/shorten",
         "url_param={}",
         ContentType::form_url_encoded());

parse!(vgd_parse);
request!(vgd_req, get, "http://is.gd/create.php?format=simple&url={}");

/// Parses the response from a successful request to a provider into the
/// URL-shortened string.
pub fn parse(res: &str, provider: Provider) -> Option<String> {
    match provider {
        Provider::Abv8 => abv8_parse(res),
        Provider::BamBz => bambz_parse(res),
        Provider::Bmeo => bmeo_parse(res),
        Provider::BnGy => bngy_parse(res),
        Provider::FifoCc => fifocc_parse(res),
        Provider::HecSu => hecsu_parse(res),
        Provider::IsGd => isgd_parse(res),
        Provider::NowLinks => nowlinks_parse(res),
        Provider::PhxCoIn => phxcoin_parse(res),
        Provider::PsbeCo => psbeco_parse(res),
        Provider::SCoop => scoop_parse(res),
        Provider::SirBz => sirbz_parse(res),
        Provider::Rdd => rdd_parse(res),
        Provider::Rlu => rlu_parse(res),
        Provider::TinyUrl => tinyurl_parse(res),
        Provider::TinyPh => tinyph_parse(res),
        Provider::TnyIm => tnyim_parse(res),
        Provider::UrlShortenerIo => urlshortenerio_parse(res),
        Provider::VGd => vgd_parse(res),
    }
}

/// Performs a request to the short link provider.
/// Returns the parsed response on success or a `None` on error.
pub fn request(url: &str,
               client: &Client,
               provider: Provider)
               -> Option<Response> {
    match provider {
        Provider::Abv8 => abv8_req(url, client),
        Provider::BamBz => bambz_req(url, client),
        Provider::Bmeo => bmeo_req(url, client),
        Provider::BnGy => bngy_req(url, client),
        Provider::FifoCc => fifocc_req(url, client),
        Provider::HecSu => hecsu_req(url, client),
        Provider::IsGd => isgd_req(url, client),
        Provider::NowLinks => nowlinks_req(url, client),
        Provider::PhxCoIn => phxcoin_req(url, client),
        Provider::PsbeCo => psbeco_req(url, client),
        Provider::SCoop => scoop_req(url, client),
        Provider::SirBz => sirbz_req(url, client),
        Provider::Rdd => rdd_req(url, client),
        Provider::Rlu => rlu_req(url, client),
        Provider::TinyUrl => tinyurl_req(url, client),
        Provider::TinyPh => tinyph_req(url, client),
        Provider::TnyIm => tnyim_req(url, client),
        Provider::UrlShortenerIo => urlshortenerio_req(url, client),
        Provider::VGd => vgd_req(url, client),
    }
}
<commit_msg>Module-qualify url::form_urlencoded call<commit_after>//! Library service providers implementation.

extern crate hyper;

use hyper::client::{Client, Response};
use hyper::header::ContentType;
use url::form_urlencoded;

macro_rules! parse_xml_tag {
    ($fname: ident, $tag: expr) => {
        fn $fname(res: &str) -> Option<String> {
            res.split(&format!("<{}>", $tag))
                .nth(1)
                .unwrap_or("")
                .split(&format!("</{}>", $tag))
                .next()
                .map(String::from)
        }
    }
}

macro_rules! parse_json_tag {
    ($fname: ident, $tag: expr, $prefix: expr) => {
        fn $fname(res: &str) -> Option<String> {
            res.to_owned()
                .split(&format!("\"{}\"", $tag))
                .nth(1)
                .unwrap_or("")
                .split(",")
                .next()
                .unwrap_or("")
                .split("\"")
                .nth(1)
                .map(|v| format!("{}{}", $prefix, v.replace("\\", "")))
        }
    }
}

macro_rules! parse {
    ($name:ident) => {
        fn $name(res: &str) -> Option<String> {
            Some(res.to_owned())
        }
    };
}

macro_rules! request {
    ($name:ident, $method:ident, $req_url:expr) => {
        fn $name(url: &str, client: &Client) -> Option<Response> {
            let url = form_urlencoded::byte_serialize(url.as_bytes())
                .collect::<String>();
            client.$method(&format!($req_url, url))
                .send()
                .ok()
        }
    };

    (B, $name:ident, $method: ident, $req_url:expr, $body:expr) => {
        fn $name(url: &str, client: &Client) -> Option<Response> {
            client.$method($req_url)
                .body(&format!($body, url))
                .send()
                .ok()
        }
    };

    ($name:ident, $method:ident, $req_url:expr, $body:expr, $header:expr) => {
        fn $name(url: &str, client: &Client) -> Option<Response> {
            client.$method($req_url)
                .body(&format!($body, url))
                .header($header)
                .send()
                .ok()
        }
    };
}

/// Used to specify which provider to use to generate a short URL.
#[derive(Clone, Copy, Debug)]
pub enum Provider {
    /// http://abv8.me provider
    ///
    /// Notes:
    ///
    /// * You may not shorten more than 20 unique URLs within a 3-minute period.
    /// * You may not shorten more than 60 unique URLs within a 15-minute
    ///   period.
    Abv8,
    /// https://bam.bz provider
    BamBz,
    /// http://bmeo.org provider
    Bmeo,
    /// https://bn.gy provider
    BnGy,
    /// http://fifo.cc provider
    FifoCc,
    /// https://hec.su provider
    ///
    /// Notes:
    ///
    /// * Limited to 3000 API requests per day
    HecSu,
    /// https://is.gd provider
    IsGd,
    /// http://nowlinks.net provider
    NowLinks,
    /// http://phx.co.in provider
    ///
    /// Notes:
    ///
    /// * After some time the service will display ads
    /// * Instead of redirecting, a preview page will be displayed
    /// * Currently unstable
    PhxCoIn,
    /// http://psbe.co provider
    PsbeCo,
    /// http://s.coop provider
    SCoop,
    /// http://readbility.com provider
    Rdd,
    /// http://rlu.ru provider
    ///
    /// Notes:
    ///
    /// * If you send a lot of requests from one IP, it can be
    ///   blocked. If you plan to add more then 100 URLs in one hour, please let
    ///   the technical support know. Otherwise your IP can be blocked
    ///   unexpectedly. Prior added URLs can be deleted.
    Rlu,
    /// http://sirbz.com provider
    ///
    /// Notes:
    ///
    /// * By default, you are limited to 250 requests per 15 minutes.
    SirBz,
    /// http://tinyurl.com provider
    ///
    /// Notes:
    ///
    /// * This service does not provide any API.
    /// * The implementation result depends on the service result web page.
    TinyUrl,
    /// http://tiny.ph provider
    TinyPh,
    /// http://tny.im provider
    TnyIm,
    /// http://url-shortener.io provider
    UrlShortenerIo,
    /// https://v.gd provider
    VGd,
}

impl Provider {
    /// Converts the Provider variant into its domain name equivilant
    pub fn to_name(&self) -> &str {
        match *self {
            Provider::Abv8 => "abv8.me",
            Provider::BamBz => "bam.bz",
            Provider::Bmeo => "bmeo.org",
            Provider::BnGy => "bn.gy",
            Provider::FifoCc => "fifo.cc",
            Provider::HecSu => "hec.su",
            Provider::IsGd => "is.gd",
            Provider::NowLinks => "nowlinks.net",
            Provider::PhxCoIn => "phx.co.in",
            Provider::PsbeCo => "psbe.co",
            Provider::SCoop => "s.coop",
            Provider::SirBz => "sirbz.com",
            Provider::Rdd => "readability.com",
            Provider::Rlu => "rlu.ru",
            Provider::TinyUrl => "tinyurl.com",
            Provider::TinyPh => "tiny.ph",
            Provider::TnyIm => "tny.im",
            Provider::UrlShortenerIo => "url-shortener.io",
            Provider::VGd => "v.gd",
        }
    }
}

/// Returns a vector of all `Provider` variants. This list is in order of
/// provider quality.
///
/// The providers which are discouraged from use - due to problems such as rate
/// limitations - are at the end of the resultant vector.
///
/// Note that some providers may not provide a generated short URL because the
/// submitted URL may already be short enough and would not benefit from
/// shortening via their service.
pub fn providers() -> Vec<Provider> {
    vec![
        Provider::IsGd,
        Provider::BnGy,
        Provider::VGd,
        Provider::Rdd,
        Provider::BamBz,
        Provider::TinyPh,
        Provider::FifoCc,
        Provider::SCoop,
        Provider::Bmeo,
        Provider::UrlShortenerIo,

        // The following list are items that have long response sometimes:
        Provider::TnyIm,

        // The following list are items that are discouraged from use:

        // Reasons:
        //
        // * rate limit (250 requests per 15 minutes)
        // * does not accept short urls (ex: http://google.com)
        Provider::SirBz,
        // Reason: rate limit (100 requests per hour)
        Provider::Rlu,
        // Reason: rate limit (3000 requests per day)
        Provider::HecSu,
        // Reason: rate limit (20r/3min; 60r/15min for a UNIQUE urls only)
        Provider::Abv8,
        // Reason: does not provide an api
        Provider::TinyUrl,
        // Reason: unstable work
        Provider::PsbeCo,

        // The following list are items that show previews instead of direct
        // links.
        Provider::NowLinks,
    ]
}

parse!(abv8_parse);
request!(abv8_req, get, "http://abv8.me/?url={}");

parse_json_tag!(bambz_parse, "url", "");
request!(bambz_req,
         post,
         "https://bam.bz/api/short",
         "target={}",
         ContentType::form_url_encoded());

parse_json_tag!(bmeo_parse, "short", "");
request!(bmeo_req, get, "http://bmeo.org/api.php?url={}");

parse_xml_tag!(bngy_parse, "ShortenedUrl");
request!(bngy_req, get, "https://bn.gy/API.asmx/CreateUrl?real_url={}");

parse_json_tag!(fifocc_parse, "shortner", "http://fifo.cc/");
request!(fifocc_req, get, "https://fifo.cc/api/v2?url={}");

parse_xml_tag!(hecsu_parse, "short");
request!(hecsu_req, get, "https://hec.su/api?url={}&method=xml");

parse!(isgd_parse);
request!(isgd_req, get, "https://is.gd/create.php?format=simple&url={}");

parse!(nowlinks_parse);
request!(nowlinks_req, get, "http://nowlinks.net/api?url={}");

parse!(phxcoin_parse);
request!(phxcoin_req, get, "http://phx.co.in/shrink.asp?url={}");

parse_xml_tag!(psbeco_parse, "ShortUrl");
request!(psbeco_req, get, "http://psbe.co/API.asmx/CreateUrl?real_url={}");

parse!(scoop_parse);
request!(scoop_req,
         get,
         "http://s.coop/devapi.php?action=shorturl&url={}&format=RETURN");

parse_json_tag!(rdd_parse, "rdd_url", "");
request!(B,
         rdd_req,
         post,
         "https://readability.com/api/shortener/v1/urls",
         "url={}");

parse!(rlu_parse);
request!(rlu_req, get, "http://rlu.ru/index.sema?a=api&link={}");

parse_json_tag!(sirbz_parse, "short_link", "");
request!(sirbz_req,
         post,
         "http://sirbz.com/api/shorten_url",
         "url={}",
         ContentType::form_url_encoded());

fn tinyurl_parse(res: &str) -> Option<String> {
    res.split("data-clipboard-text=\"")
        .nth(1)
        .unwrap_or("")
        .split("\">")
        .next()
        .map(String::from)
}
request!(tinyurl_req, get, "http://tinyurl.com/create.php?url={}");

parse_json_tag!(tinyph_parse, "hash", "http://tiny.ph/");
request!(tinyph_req,
         post,
         "http://tiny.ph/api/url/create",
         "url={}",
         ContentType::form_url_encoded());

parse_xml_tag!(tnyim_parse, "shorturl");
request!(tnyim_req, get, "http://tny.im/yourls-api.php?action=shorturl&url={}");

parse!(urlshortenerio_parse);
request!(urlshortenerio_req,
         post,
         "http://url-shortener.io/shorten",
         "url_param={}",
         ContentType::form_url_encoded());

parse!(vgd_parse);
request!(vgd_req, get, "http://is.gd/create.php?format=simple&url={}");

/// Parses the response from a successful request to a provider into the
/// URL-shortened string.
pub fn parse(res: &str, provider: Provider) -> Option<String> {
    match provider {
        Provider::Abv8 => abv8_parse(res),
        Provider::BamBz => bambz_parse(res),
        Provider::Bmeo => bmeo_parse(res),
        Provider::BnGy => bngy_parse(res),
        Provider::FifoCc => fifocc_parse(res),
        Provider::HecSu => hecsu_parse(res),
        Provider::IsGd => isgd_parse(res),
        Provider::NowLinks => nowlinks_parse(res),
        Provider::PhxCoIn => phxcoin_parse(res),
        Provider::PsbeCo => psbeco_parse(res),
        Provider::SCoop => scoop_parse(res),
        Provider::SirBz => sirbz_parse(res),
        Provider::Rdd => rdd_parse(res),
        Provider::Rlu => rlu_parse(res),
        Provider::TinyUrl => tinyurl_parse(res),
        Provider::TinyPh => tinyph_parse(res),
        Provider::TnyIm => tnyim_parse(res),
        Provider::UrlShortenerIo => urlshortenerio_parse(res),
        Provider::VGd => vgd_parse(res),
    }
}

/// Performs a request to the short link provider.
/// Returns the parsed response on success or a `None` on error.
pub fn request(url: &str,
               client: &Client,
               provider: Provider)
               -> Option<Response> {
    match provider {
        Provider::Abv8 => abv8_req(url, client),
        Provider::BamBz => bambz_req(url, client),
        Provider::Bmeo => bmeo_req(url, client),
        Provider::BnGy => bngy_req(url, client),
        Provider::FifoCc => fifocc_req(url, client),
        Provider::HecSu => hecsu_req(url, client),
        Provider::IsGd => isgd_req(url, client),
        Provider::NowLinks => nowlinks_req(url, client),
        Provider::PhxCoIn => phxcoin_req(url, client),
        Provider::PsbeCo => psbeco_req(url, client),
        Provider::SCoop => scoop_req(url, client),
        Provider::SirBz => sirbz_req(url, client),
        Provider::Rdd => rdd_req(url, client),
        Provider::Rlu => rlu_req(url, client),
        Provider::TinyUrl => tinyurl_req(url, client),
        Provider::TinyPh => tinyph_req(url, client),
        Provider::TnyIm => tnyim_req(url, client),
        Provider::UrlShortenerIo => urlshortenerio_req(url, client),
        Provider::VGd => vgd_req(url, client),
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2018 Ben Ashford
 *
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 * http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 * <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 * option. This file may not be copied, modified, or distributed
 * except according to those terms.
 */

use std::error as std_error;
use std::sync::{Arc, Mutex, RwLock};

use futures::{future::Either, sync::oneshot, Future};

use tokio_executor::{DefaultExecutor, Executor};

#[derive(Debug)]
pub(crate) enum ReconnectError {
    ConnectionDropped,
    ConnectionFailed,
}

pub(crate) struct Reconnect<A, T, RE, CE> {
    state: Arc<RwLock<ReconnectState<T, A>>>,

    work_fn: Arc<Fn(&T, A) -> Box<Future<Item = (), Error = RE> + Send> + Send + Sync>,
    conn_fn: Arc<Fn() -> Box<Future<Item = T, Error = CE> + Send> + Send + Sync>,
}

pub(crate) fn reconnect<A, T, RE, CE, W, C>(w: W, c: C) -> Reconnect<A, T, RE, CE>
where
    A: Send + 'static,
    W: Fn(&T, A) -> Box<Future<Item = (), Error = RE> + Send> + Send + Sync + 'static,
    C: Fn() -> Box<Future<Item = T, Error = CE> + Send> + Send + Sync + 'static,
    T: Clone + Send + Sync + 'static,
    RE: std_error::Error + 'static,
    CE: std_error::Error + 'static,
{
    let r = Reconnect {
        state: Arc::new(RwLock::new(ReconnectState::Initialising)),

        work_fn: Arc::new(w),
        conn_fn: Arc::new(c),
    };
    r.reconnect();
    r
}

struct PendingAction<A> {
    result_c: oneshot::Sender<Box<Future<Item = (), Error = ReconnectError> + Send>>,
    action: A,
}

enum ReconnectState<T, A> {
    Initialising,
    Good(T),
    Pending(Mutex<Vec<PendingAction<A>>>),
}

use self::ReconnectState::*;

impl<A, T, RE, CE> Reconnect<A, T, RE, CE>
where
    A: Send + 'static,
    T: Clone + Send + Sync + 'static,
    RE: std_error::Error + 'static,
    CE: std_error::Error + 'static,
{
    fn call_work(&self, t: &T, a: A) -> impl Future<Item = (), Error = ReconnectError> {
        let reconnect = self.clone();
        (self.work_fn)(t, a).map_err(move |e| {
            error!("Cannot perform action: {}", e);
            reconnect.reconnect();
            ReconnectError::ConnectionDropped
        })
    }

    pub(crate) fn do_work(&self, a: A) -> impl Future<Item = (), Error = ReconnectError> {
        let state = self.state.read().expect("Cannot obtain read lock");
        match *state {
            Initialising => panic!("Invalid state, this should be an impossible condition as `do_work` can only be called after `reconnect`"),
            Good(ref t) => Either::A(self.call_work(t, a)),
            Pending(ref pending) => {
                let mut pending = pending.lock().expect("Cannot lock pending");
                let (tx, rx) = oneshot::channel();

                pending.push(PendingAction {
                    result_c: tx,
                    action: a,
                });

                let r = rx.map_err(|_| ReconnectError::ConnectionFailed).and_then(|o| {
                    o
                });

                Either::B(r)
            }
        }
    }

    pub(crate) fn reconnect(&self) {
        let mut state = self.state.write().expect("Cannot obtain write lock");
        if state.is_pending() {
            warn!("Trying to reconnect when already reconnecting");
            return;
        }
        *state = ReconnectState::Pending(Mutex::new(Vec::new()));

        let reconnect = self.clone();
        let connect_f = (self.conn_fn)();

        let connect_f = connect_f.then(move |t| {
            let mut state = reconnect.state.write().expect("Cannot obtain write lock");
            match *state {
                Initialising => panic!("Invalid state, this should be Pending"),
                Good(_) => {
                    return Ok(match t {
                        Ok(_) => warn!("Attempting to reset a state to good that is already good, dropping new connection instead"),
                        Err(e) => error!("Cannot create a new connection, but connection is good anyway due to another connection: {}", e),
                    })
                }
                Pending(ref pending) => {
                    match t {
                        Ok(ref t) => {
                            let mut pending = pending.lock().expect("Cannot obtain lock");
                            for PendingAction { result_c, action } in pending.drain(..) {
                                let _ = result_c.send(Box::new(reconnect.call_work(&t, action)));
                            }
                        },
                        Err(ref e) => error!("Cannot create a new connection, pending requests will be rejected, subsequent requests will initialise a new connection: {}", e),
                    }
                }
            }
            match t {
                Ok(t) => {
                    *state = ReconnectState::Good(t);
                    Ok(())
                }
                Err(_) => Err(()),
            }
        });

        let mut executor = DefaultExecutor::current();

        executor
            .spawn(Box::new(connect_f.map_err(|_| panic!("Error connecting"))))
            .expect("Cannot spawn future");
    }
}

impl<T, A> ReconnectState<T, A> {
    fn is_pending(&self) -> bool {
        match self {
            Initialising => false,
            Good(_) => false,
            Pending(_) => true,
        }
    }
}

impl<A, T, RE, CE> Clone for Reconnect<A, T, RE, CE> {
    fn clone(&self) -> Self {
        Reconnect {
            state: self.state.clone(),
            work_fn: self.work_fn.clone(),
            conn_fn: self.conn_fn.clone(),
        }
    }
}
<commit_msg>Don't panic<commit_after>/*
 * Copyright 2018 Ben Ashford
 *
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 * http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 * <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 * option. This file may not be copied, modified, or distributed
 * except according to those terms.
 */

use std::error as std_error;
use std::sync::{Arc, Mutex, RwLock};

use futures::{future::Either, sync::oneshot, Future};

use tokio_executor::{DefaultExecutor, Executor};

#[derive(Debug)]
pub(crate) enum ReconnectError {
    ConnectionDropped,
    ConnectionFailed,
}

pub(crate) struct Reconnect<A, T, RE, CE> {
    state: Arc<RwLock<ReconnectState<T, A>>>,

    work_fn: Arc<Fn(&T, A) -> Box<Future<Item = (), Error = RE> + Send> + Send + Sync>,
    conn_fn: Arc<Fn() -> Box<Future<Item = T, Error = CE> + Send> + Send + Sync>,
}

pub(crate) fn reconnect<A, T, RE, CE, W, C>(w: W, c: C) -> Reconnect<A, T, RE, CE>
where
    A: Send + 'static,
    W: Fn(&T, A) -> Box<Future<Item = (), Error = RE> + Send> + Send + Sync + 'static,
    C: Fn() -> Box<Future<Item = T, Error = CE> + Send> + Send + Sync + 'static,
    T: Clone + Send + Sync + 'static,
    RE: std_error::Error + 'static,
    CE: std_error::Error + 'static,
{
    let r = Reconnect {
        state: Arc::new(RwLock::new(ReconnectState::Initialising)),

        work_fn: Arc::new(w),
        conn_fn: Arc::new(c),
    };
    r.reconnect();
    r
}

struct PendingAction<A> {
    result_c: oneshot::Sender<Box<Future<Item = (), Error = ReconnectError> + Send>>,
    action: A,
}

enum ReconnectState<T, A> {
    Initialising,
    Good(T),
    Pending(Mutex<Vec<PendingAction<A>>>),
}

use self::ReconnectState::*;

impl<A, T, RE, CE> Reconnect<A, T, RE, CE>
where
    A: Send + 'static,
    T: Clone + Send + Sync + 'static,
    RE: std_error::Error + 'static,
    CE: std_error::Error + 'static,
{
    fn call_work(&self, t: &T, a: A) -> impl Future<Item = (), Error = ReconnectError> {
        let reconnect = self.clone();
        (self.work_fn)(t, a).map_err(move |e| {
            error!("Cannot perform action: {}", e);
            reconnect.reconnect();
            ReconnectError::ConnectionDropped
        })
    }

    pub(crate) fn do_work(&self, a: A) -> impl Future<Item = (), Error = ReconnectError> {
        let state = self.state.read().expect("Cannot obtain read lock");
        match *state {
            Initialising => panic!("Invalid state, this should be an impossible condition as `do_work` can only be called after `reconnect`"),
            Good(ref t) => Either::A(self.call_work(t, a)),
            Pending(ref pending) => {
                let mut pending = pending.lock().expect("Cannot lock pending");
                let (tx, rx) = oneshot::channel();

                pending.push(PendingAction {
                    result_c: tx,
                    action: a,
                });

                let r = rx.map_err(|_| ReconnectError::ConnectionFailed).and_then(|o| {
                    o
                });

                Either::B(r)
            }
        }
    }

    pub(crate) fn reconnect(&self) {
        let mut state = self.state.write().expect("Cannot obtain write lock");
        if state.is_pending() {
            warn!("Trying to reconnect when already reconnecting");
            return;
        }
        *state = ReconnectState::Pending(Mutex::new(Vec::new()));

        let reconnect = self.clone();
        let connect_f = (self.conn_fn)();

        let connect_f = connect_f.then(move |t| {
            let mut state = reconnect.state.write().expect("Cannot obtain write lock");
            match *state {
                Initialising => panic!("Invalid state, this should be Pending"),
                Good(_) => {
                    return Ok(match t {
                        Ok(_) => warn!("Attempting to reset a state to good that is already good, dropping new connection instead"),
                        Err(e) => error!("Cannot create a new connection, but connection is good anyway due to another connection: {}", e),
                    })
                }
                Pending(ref pending) => {
                    match t {
                        Ok(ref t) => {
                            let mut pending = pending.lock().expect("Cannot obtain lock");
                            for PendingAction { result_c, action } in pending.drain(..) {
                                let _ = result_c.send(Box::new(reconnect.call_work(&t, action)));
                            }
                        },
                        Err(ref e) => error!("Cannot create a new connection, pending requests will be rejected, subsequent requests will initialise a new connection: {}", e),
                    }
                }
            }
            match t {
                Ok(t) => {
                    *state = ReconnectState::Good(t);
                    Ok(())
                }
                Err(_) => Err(()),
            }
        });

        let mut executor = DefaultExecutor::current();

        executor
            .spawn(Box::new(connect_f))
            .expect("Cannot spawn future");
    }
}

impl<T, A> ReconnectState<T, A> {
    fn is_pending(&self) -> bool {
        match self {
            Initialising => false,
            Good(_) => false,
            Pending(_) => true,
        }
    }
}

impl<A, T, RE, CE> Clone for Reconnect<A, T, RE, CE> {
    fn clone(&self) -> Self {
        Reconnect {
            state: self.state.clone(),
            work_fn: self.work_fn.clone(),
            conn_fn: self.conn_fn.clone(),
        }
    }
}
<|endoftext|>