<commit_before>// Copyright (C) 2019 Guillaume Desmottes <guillaume.desmottes@collabora.com>
//
// 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.

extern crate gstreamer as gst;
extern crate gstreamer_app as gst_app;

use gst::prelude::*;
use std::path::PathBuf;

fn init() {
    use std::sync::Once;
    static INIT: Once = Once::new();

    INIT.call_once(|| {
        gst::init().unwrap();
        gstcdg::plugin_register_static().expect("cdgdec tests");
    });
}

#[test]
fn test_cdgdec() {
    init();

    let pipeline = gst::Pipeline::new(Some("cdgdec-test"));

    let input_path = {
        let mut r = PathBuf::new();
        r.push(env!("CARGO_MANIFEST_DIR"));
        r.push("tests");
        r.push("BrotherJohn");
        r.set_extension("cdg");
        r
    };

    let filesrc = gst::ElementFactory::make("filesrc", None).unwrap();
    filesrc
        .set_property("location", &input_path.to_str().unwrap())
        .expect("failed to set 'location' property");
    filesrc
        .set_property("num-buffers", &1)
        .expect("failed to set 'num-buffers' property");
    let blocksize: u32 = 24; // One CDG instruction
    filesrc
        .set_property("blocksize", &blocksize)
        .expect("failed to set 'blocksize' property");

    let parse = gst::ElementFactory::make("cdgparse", None).unwrap();
    let dec = gst::ElementFactory::make("cdgdec", None).unwrap();
    let sink = gst::ElementFactory::make("appsink", None).unwrap();

    pipeline
        .add_many(&[&filesrc, &parse, &dec, &sink])
        .expect("failed to add elements to the pipeline");
    gst::Element::link_many(&[&filesrc, &parse, &dec, &sink]).expect("failed to link the elements");

    let sink = sink.downcast::<gst_app::AppSink>().unwrap();
    sink.set_callbacks(
        gst_app::AppSinkCallbacks::new()
            // Add a handler to the "new-sample" signal.
            .new_sample(move |appsink| {
                // Pull the sample in question out of the appsink's buffer.
                let sample = appsink.pull_sample().map_err(|_| gst::FlowError::Eos)?;
                let buffer = sample.get_buffer().ok_or(gst::FlowError::Error)?;
                let map = buffer.map_readable().map_err(|_| gst::FlowError::Error)?;

                // First frame fully blue
                map.as_slice()
                    .chunks_exact(4)
                    .for_each(|p| assert_eq!(p, [0, 0, 136, 255]));

                Ok(gst::FlowSuccess::Ok)
            })
            .build(),
    );

    pipeline
        .set_state(gst::State::Playing)
        .expect("Unable to set the pipeline to the `Playing` state");

    let bus = pipeline.get_bus().unwrap();
    for msg in bus.iter_timed(gst::CLOCK_TIME_NONE) {
        use gst::MessageView;
        match msg.view() {
            MessageView::Error(err) => {
                eprintln!(
                    "Error received from element {:?}: {}",
                    err.get_src().map(|s| s.get_path_string()),
                    err.get_error()
                );
                eprintln!("Debugging information: {:?}", err.get_debug());
                unreachable!();
            }
            MessageView::Eos(..) => break,
            _ => (),
        }
    }

    pipeline
        .set_state(gst::State::Null)
        .expect("Unable to set the pipeline to the `Null` state");
}
<commit_msg>cdgdec: fix test with gst master<commit_after>// Copyright (C) 2019 Guillaume Desmottes <guillaume.desmottes@collabora.com>
//
// 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.

extern crate gstreamer as gst;
extern crate gstreamer_app as gst_app;

use gst::prelude::*;
use std::path::PathBuf;

fn init() {
    use std::sync::Once;
    static INIT: Once = Once::new();

    INIT.call_once(|| {
        gst::init().unwrap();
        gstcdg::plugin_register_static().expect("cdgdec tests");
    });
}

#[test]
fn test_cdgdec() {
    init();

    let pipeline = gst::Pipeline::new(Some("cdgdec-test"));

    let input_path = {
        let mut r = PathBuf::new();
        r.push(env!("CARGO_MANIFEST_DIR"));
        r.push("tests");
        r.push("BrotherJohn");
        r.set_extension("cdg");
        r
    };

    // Ensure we are in push mode so 'blocksize' prop is used
    let filesrc = gst::ElementFactory::make("pushfilesrc", None).unwrap();
    filesrc
        .set_property("location", &input_path.to_str().unwrap())
        .expect("failed to set 'location' property");
    {
        let child_proxy = filesrc.dynamic_cast_ref::<gst::ChildProxy>().unwrap();
        child_proxy
            .set_child_property("real-filesrc::num-buffers", &1)
            .expect("failed to set 'num-buffers' property");
        let blocksize: u32 = 24; // One CDG instruction
        child_proxy
            .set_child_property("real-filesrc::blocksize", &blocksize)
            .expect("failed to set 'blocksize' property");
    }

    let parse = gst::ElementFactory::make("cdgparse", None).unwrap();
    let dec = gst::ElementFactory::make("cdgdec", None).unwrap();
    let sink = gst::ElementFactory::make("appsink", None).unwrap();

    pipeline
        .add_many(&[&filesrc, &parse, &dec, &sink])
        .expect("failed to add elements to the pipeline");
    gst::Element::link_many(&[&filesrc, &parse, &dec, &sink]).expect("failed to link the elements");

    let sink = sink.downcast::<gst_app::AppSink>().unwrap();
    sink.set_callbacks(
        gst_app::AppSinkCallbacks::new()
            // Add a handler to the "new-sample" signal.
            .new_sample(move |appsink| {
                // Pull the sample in question out of the appsink's buffer.
                let sample = appsink.pull_sample().map_err(|_| gst::FlowError::Eos)?;
                let buffer = sample.get_buffer().ok_or(gst::FlowError::Error)?;
                let map = buffer.map_readable().map_err(|_| gst::FlowError::Error)?;

                // First frame fully blue
                map.as_slice()
                    .chunks_exact(4)
                    .for_each(|p| assert_eq!(p, [0, 0, 136, 255]));

                Ok(gst::FlowSuccess::Ok)
            })
            .build(),
    );

    pipeline
        .set_state(gst::State::Playing)
        .expect("Unable to set the pipeline to the `Playing` state");

    let bus = pipeline.get_bus().unwrap();
    for msg in bus.iter_timed(gst::CLOCK_TIME_NONE) {
        use gst::MessageView;
        match msg.view() {
            MessageView::Error(err) => {
                eprintln!(
                    "Error received from element {:?}: {}",
                    err.get_src().map(|s| s.get_path_string()),
                    err.get_error()
                );
                eprintln!("Debugging information: {:?}", err.get_debug());
                unreachable!();
            }
            MessageView::Eos(..) => break,
            _ => (),
        }
    }

    pipeline
        .set_state(gst::State::Null)
        .expect("Unable to set the pipeline to the `Null` state");
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

mod tags;
mod html;

use hir::{Name, Semantics};
use ra_ide_db::{
    defs::{classify_name, NameDefinition},
    RootDatabase,
};
use ra_prof::profile;
use ra_syntax::{
    ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken,
    TextRange, WalkEvent, T,
};
use rustc_hash::FxHashMap;

use crate::{references::classify_name_ref, FileId};

pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag};

pub(crate) use html::highlight_as_html;

#[derive(Debug)]
pub struct HighlightedRange {
    pub range: TextRange,
    pub highlight: Highlight,
    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,
    }
}

pub(crate) fn highlight(
    db: &RootDatabase,
    file_id: FileId,
    range_to_highlight: Option<TextRange>,
) -> Vec<HighlightedRange> {
    let _p = profile("highlight");
    let sema = Semantics::new(db);

    // Determine the root based on the given range.
    let (root, range_to_highlight) = {
        let source_file = sema.parse(file_id);
        match range_to_highlight {
            Some(range) => {
                let node = match source_file.syntax().covering_element(range) {
                    NodeOrToken::Node(it) => it,
                    NodeOrToken::Token(it) => it.parent(),
                };
                (node, range)
            }
            None => (source_file.syntax().clone(), source_file.syntax().text_range()),
        }
    };

    let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();
    let mut res = Vec::new();

    let mut in_macro_call = None;

    for event in root.preorder_with_tokens() {
        let event_range = match &event {
            WalkEvent::Enter(it) => it.text_range(),
            WalkEvent::Leave(it) => it.text_range(),
        };

        if event_range.intersection(&range_to_highlight).is_none() {
            continue;
        }

        match event {
            WalkEvent::Enter(node) => match node.kind() {
                MACRO_CALL => {
                    in_macro_call = Some(node.clone());
                    if let Some(range) = highlight_macro(node) {
                        res.push(HighlightedRange {
                            range,
                            highlight: HighlightTag::Macro.into(),
                            binding_hash: None,
                        });
                    }
                }
                _ if in_macro_call.is_some() => {
                    if let Some(token) = node.as_token() {
                        if let Some((highlight, binding_hash)) =
                            highlight_token_tree(&sema, &mut bindings_shadow_count, token.clone())
                        {
                            res.push(HighlightedRange {
                                range: node.text_range(),
                                highlight,
                                binding_hash,
                            });
                        }
                    }
                }
                _ => {
                    if let Some((highlight, binding_hash)) =
                        highlight_node(&sema, &mut bindings_shadow_count, node.clone())
                    {
                        res.push(HighlightedRange {
                            range: node.text_range(),
                            highlight,
                            binding_hash,
                        });
                    }
                }
            },
            WalkEvent::Leave(node) => {
                if let Some(m) = in_macro_call.as_ref() {
                    if *m == node {
                        in_macro_call = None;
                    }
                }
            }
        }
    }

    res
}

fn highlight_macro(node: SyntaxElement) -> Option<TextRange> {
    let macro_call = ast::MacroCall::cast(node.as_node()?.clone())?;
    let path = macro_call.path()?;
    let name_ref = path.segment()?.name_ref()?;

    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(),
            _ => (),
        }
    }

    Some(TextRange::from_to(range_start, range_end))
}

fn highlight_token_tree(
    sema: &Semantics<RootDatabase>,
    bindings_shadow_count: &mut FxHashMap<Name, u32>,
    token: SyntaxToken,
) -> Option<(Highlight, Option<u64>)> {
    if token.parent().kind() != TOKEN_TREE {
        return None;
    }
    let token = sema.descend_into_macros(token.clone());
    let expanded = {
        let parent = token.parent();
        // We only care Name and Name_ref
        match (token.kind(), parent.kind()) {
            (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(),
            _ => token.into(),
        }
    };

    highlight_node(sema, bindings_shadow_count, expanded)
}

fn highlight_node(
    sema: &Semantics<RootDatabase>,
    bindings_shadow_count: &mut FxHashMap<Name, u32>,
    node: SyntaxElement,
) -> Option<(Highlight, Option<u64>)> {
    let db = sema.db;
    let mut binding_hash = None;
    let highlight: Highlight = match node.kind() {
        FN_DEF => {
            bindings_shadow_count.clear();
            return None;
        }
        COMMENT => HighlightTag::Comment.into(),
        STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => HighlightTag::LiteralString.into(),
        ATTR => HighlightTag::Attribute.into(),
        // Special-case field init shorthand
        NAME_REF if node.parent().and_then(ast::RecordField::cast).is_some() => {
            HighlightTag::Field.into()
        }
        NAME_REF if node.ancestors().any(|it| it.kind() == ATTR) => return None,
        NAME_REF => {
            let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap();
            let name_kind = classify_name_ref(sema, &name_ref);
            match name_kind {
                Some(name_kind) => {
                    if let NameDefinition::Local(local) = &name_kind {
                        if let Some(name) = local.name(db) {
                            let shadow_count =
                                bindings_shadow_count.entry(name.clone()).or_default();
                            binding_hash = Some(calc_binding_hash(&name, *shadow_count))
                        }
                    };

                    highlight_name(db, name_kind)
                }
                _ => return None,
            }
        }
        NAME => {
            let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap();
            let name_kind = classify_name(sema, &name);

            if let Some(NameDefinition::Local(local)) = &name_kind {
                if let Some(name) = local.name(db) {
                    let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
                    *shadow_count += 1;
                    binding_hash = Some(calc_binding_hash(&name, *shadow_count))
                }
            };

            match name_kind {
                Some(name_kind) => highlight_name(db, name_kind),
                None => name.syntax().parent().map_or(HighlightTag::Function.into(), |x| {
                    match x.kind() {
                        STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => {
                            HighlightTag::Type.into()
                        }
                        TYPE_PARAM => HighlightTag::TypeParam.into(),
                        RECORD_FIELD_DEF => HighlightTag::Field.into(),
                        _ => HighlightTag::Function.into(),
                    }
                }),
            }
        }
        INT_NUMBER | FLOAT_NUMBER => HighlightTag::LiteralNumeric.into(),
        BYTE => HighlightTag::LiteralByte.into(),
        CHAR => HighlightTag::LiteralChar.into(),
        LIFETIME => HighlightTag::TypeLifetime.into(),
        T![unsafe] => HighlightTag::Keyword | HighlightModifier::Unsafe,
        k if is_control_keyword(k) => HighlightTag::Keyword | HighlightModifier::Control,
        k if k.is_keyword() => HighlightTag::Keyword.into(),

        _ => return None,
    };

    return Some((highlight, binding_hash));

    fn calc_binding_hash(name: &Name, 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((name, shadow_count))
    }
}

fn highlight_name(db: &RootDatabase, def: NameDefinition) -> Highlight {
    match def {
        NameDefinition::Macro(_) => HighlightTag::Macro,
        NameDefinition::StructField(_) => HighlightTag::Field,
        NameDefinition::ModuleDef(hir::ModuleDef::Module(_)) => HighlightTag::Module,
        NameDefinition::ModuleDef(hir::ModuleDef::Function(_)) => HighlightTag::Function,
        NameDefinition::ModuleDef(hir::ModuleDef::Adt(_)) => HighlightTag::Type,
        NameDefinition::ModuleDef(hir::ModuleDef::EnumVariant(_)) => HighlightTag::Constant,
        NameDefinition::ModuleDef(hir::ModuleDef::Const(_)) => HighlightTag::Constant,
        NameDefinition::ModuleDef(hir::ModuleDef::Static(_)) => HighlightTag::Constant,
        NameDefinition::ModuleDef(hir::ModuleDef::Trait(_)) => HighlightTag::Type,
        NameDefinition::ModuleDef(hir::ModuleDef::TypeAlias(_)) => HighlightTag::Type,
        NameDefinition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => {
            return HighlightTag::Type | HighlightModifier::Builtin
        }
        NameDefinition::SelfType(_) => HighlightTag::TypeSelf,
        NameDefinition::TypeParam(_) => HighlightTag::TypeParam,
        NameDefinition::Local(local) => {
            let mut h = Highlight::new(HighlightTag::Variable);
            if local.is_mut(db) || local.ty(db).is_mutable_reference() {
                h |= HighlightModifier::Mutable;
            }
            return h;
        }
    }
    .into()
}

#[cfg(test)]
mod tests {
    use std::fs;

    use test_utils::{assert_eq_text, project_dir, read_text};

    use crate::{
        mock_analysis::{single_file, MockAnalysis},
        FileRange, TextRange,
    };

    #[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>();
}

macro_rules! def_fn {
    ($($tt:tt)*) => {$($tt)*}
}

def_fn!{
    fn bar() -> u32 {
        100
    }
}

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

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

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

    y;
}

enum E<X> {
    V(X)
}

impl<X> E<X> {
    fn new<T>() -> E<T> {}
}
"#
            .trim(),
        );
        let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlighting.html");
        let actual_html = &analysis.highlight_as_html(file_id, false).unwrap();
        let expected_html = &read_text(&dst_file);
        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/src/snapshots/rainbow_highlighting.html");
        let actual_html = &analysis.highlight_as_html(file_id, true).unwrap();
        let expected_html = &read_text(&dst_file);
        fs::write(dst_file, &actual_html).unwrap();
        assert_eq_text!(expected_html, actual_html);
    }

    #[test]
    fn accidentally_quadratic() {
        let file = project_dir().join("crates/ra_syntax/test_data/accidentally_quadratic");
        let src = fs::read_to_string(file).unwrap();

        let mut mock = MockAnalysis::new();
        let file_id = mock.add_file("/main.rs", &src);
        let host = mock.analysis_host();

        // let t = std::time::Instant::now();
        let _ = host.analysis().highlight(file_id).unwrap();
        // eprintln!("elapsed: {:?}", t.elapsed());
    }

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

        // The "x"
        let highlights = &analysis
            .highlight_range(FileRange {
                file_id,
                range: TextRange::offset_len(82.into(), 1.into()),
            })
            .unwrap();

        assert_eq!(&highlights[0].highlight.to_string(), "field");
    }
}
<commit_msg>Split loop into orthogonal phases<commit_after>//! FIXME: write short doc here

mod tags;
mod html;

use hir::{Name, Semantics};
use ra_ide_db::{
    defs::{classify_name, NameDefinition},
    RootDatabase,
};
use ra_prof::profile;
use ra_syntax::{
    ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken,
    TextRange, WalkEvent, T,
};
use rustc_hash::FxHashMap;

use crate::{references::classify_name_ref, FileId};

pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag};

pub(crate) use html::highlight_as_html;

#[derive(Debug)]
pub struct HighlightedRange {
    pub range: TextRange,
    pub highlight: Highlight,
    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,
    }
}

pub(crate) fn highlight(
    db: &RootDatabase,
    file_id: FileId,
    range_to_highlight: Option<TextRange>,
) -> Vec<HighlightedRange> {
    let _p = profile("highlight");
    let sema = Semantics::new(db);

    // Determine the root based on the given range.
    let (root, range_to_highlight) = {
        let source_file = sema.parse(file_id);
        match range_to_highlight {
            Some(range) => {
                let node = match source_file.syntax().covering_element(range) {
                    NodeOrToken::Node(it) => it,
                    NodeOrToken::Token(it) => it.parent(),
                };
                (node, range)
            }
            None => (source_file.syntax().clone(), source_file.syntax().text_range()),
        }
    };

    let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();
    let mut res = Vec::new();

    let mut current_macro_call: Option<ast::MacroCall> = None;

    for event in root.preorder_with_tokens() {
        let event_range = match &event {
            WalkEvent::Enter(it) => it.text_range(),
            WalkEvent::Leave(it) => it.text_range(),
        };

        if event_range.intersection(&range_to_highlight).is_none() {
            continue;
        }

        match event.clone().map(|it| it.into_node().and_then(ast::MacroCall::cast)) {
            WalkEvent::Enter(Some(mc)) => {
                current_macro_call = Some(mc.clone());
                if let Some(range) = highlight_macro(&mc) {
                    res.push(HighlightedRange {
                        range,
                        highlight: HighlightTag::Macro.into(),
                        binding_hash: None,
                    });
                }
                continue;
            }
            WalkEvent::Leave(Some(mc)) => {
                assert!(current_macro_call == Some(mc));
                current_macro_call = None;
                continue;
            }
            _ => (),
        }

        let node = match event {
            WalkEvent::Enter(it) => it,
            WalkEvent::Leave(_) => continue,
        };

        if current_macro_call.is_some() {
            if let Some(token) = node.into_token() {
                if let Some((highlight, binding_hash)) =
                    highlight_token_tree(&sema, &mut bindings_shadow_count, token.clone())
                {
                    res.push(HighlightedRange {
                        range: token.text_range(),
                        highlight,
                        binding_hash,
                    });
                }
            }
            continue;
        }

        if let Some((highlight, binding_hash)) =
            highlight_node(&sema, &mut bindings_shadow_count, node.clone())
        {
            res.push(HighlightedRange { range: node.text_range(), highlight, binding_hash });
        }
    }

    res
}

fn highlight_macro(macro_call: &ast::MacroCall) -> Option<TextRange> {
    let path = macro_call.path()?;
    let name_ref = path.segment()?.name_ref()?;

    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(),
            _ => (),
        }
    }

    Some(TextRange::from_to(range_start, range_end))
}

fn highlight_token_tree(
    sema: &Semantics<RootDatabase>,
    bindings_shadow_count: &mut FxHashMap<Name, u32>,
    token: SyntaxToken,
) -> Option<(Highlight, Option<u64>)> {
    if token.parent().kind() != TOKEN_TREE {
        return None;
    }
    let token = sema.descend_into_macros(token.clone());
    let expanded = {
        let parent = token.parent();
        // We only care Name and Name_ref
        match (token.kind(), parent.kind()) {
            (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(),
            _ => token.into(),
        }
    };

    highlight_node(sema, bindings_shadow_count, expanded)
}

fn highlight_node(
    sema: &Semantics<RootDatabase>,
    bindings_shadow_count: &mut FxHashMap<Name, u32>,
    node: SyntaxElement,
) -> Option<(Highlight, Option<u64>)> {
    let db = sema.db;
    let mut binding_hash = None;
    let highlight: Highlight = match node.kind() {
        FN_DEF => {
            bindings_shadow_count.clear();
            return None;
        }
        COMMENT => HighlightTag::Comment.into(),
        STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => HighlightTag::LiteralString.into(),
        ATTR => HighlightTag::Attribute.into(),
        // Special-case field init shorthand
        NAME_REF if node.parent().and_then(ast::RecordField::cast).is_some() => {
            HighlightTag::Field.into()
        }
        NAME_REF if node.ancestors().any(|it| it.kind() == ATTR) => return None,
        NAME_REF => {
            let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap();
            let name_kind = classify_name_ref(sema, &name_ref);
            match name_kind {
                Some(name_kind) => {
                    if let NameDefinition::Local(local) = &name_kind {
                        if let Some(name) = local.name(db) {
                            let shadow_count =
                                bindings_shadow_count.entry(name.clone()).or_default();
                            binding_hash = Some(calc_binding_hash(&name, *shadow_count))
                        }
                    };

                    highlight_name(db, name_kind)
                }
                _ => return None,
            }
        }
        NAME => {
            let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap();
            let name_kind = classify_name(sema, &name);

            if let Some(NameDefinition::Local(local)) = &name_kind {
                if let Some(name) = local.name(db) {
                    let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
                    *shadow_count += 1;
                    binding_hash = Some(calc_binding_hash(&name, *shadow_count))
                }
            };

            match name_kind {
                Some(name_kind) => highlight_name(db, name_kind),
                None => name.syntax().parent().map_or(HighlightTag::Function.into(), |x| {
                    match x.kind() {
                        STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => {
                            HighlightTag::Type.into()
                        }
                        TYPE_PARAM => HighlightTag::TypeParam.into(),
                        RECORD_FIELD_DEF => HighlightTag::Field.into(),
                        _ => HighlightTag::Function.into(),
                    }
                }),
            }
        }
        INT_NUMBER | FLOAT_NUMBER => HighlightTag::LiteralNumeric.into(),
        BYTE => HighlightTag::LiteralByte.into(),
        CHAR => HighlightTag::LiteralChar.into(),
        LIFETIME => HighlightTag::TypeLifetime.into(),
        T![unsafe] => HighlightTag::Keyword | HighlightModifier::Unsafe,
        k if is_control_keyword(k) => HighlightTag::Keyword | HighlightModifier::Control,
        k if k.is_keyword() => HighlightTag::Keyword.into(),

        _ => return None,
    };

    return Some((highlight, binding_hash));

    fn calc_binding_hash(name: &Name, 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((name, shadow_count))
    }
}

fn highlight_name(db: &RootDatabase, def: NameDefinition) -> Highlight {
    match def {
        NameDefinition::Macro(_) => HighlightTag::Macro,
        NameDefinition::StructField(_) => HighlightTag::Field,
        NameDefinition::ModuleDef(hir::ModuleDef::Module(_)) => HighlightTag::Module,
        NameDefinition::ModuleDef(hir::ModuleDef::Function(_)) => HighlightTag::Function,
        NameDefinition::ModuleDef(hir::ModuleDef::Adt(_)) => HighlightTag::Type,
        NameDefinition::ModuleDef(hir::ModuleDef::EnumVariant(_)) => HighlightTag::Constant,
        NameDefinition::ModuleDef(hir::ModuleDef::Const(_)) => HighlightTag::Constant,
        NameDefinition::ModuleDef(hir::ModuleDef::Static(_)) => HighlightTag::Constant,
        NameDefinition::ModuleDef(hir::ModuleDef::Trait(_)) => HighlightTag::Type,
        NameDefinition::ModuleDef(hir::ModuleDef::TypeAlias(_)) => HighlightTag::Type,
        NameDefinition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => {
            return HighlightTag::Type | HighlightModifier::Builtin
        }
        NameDefinition::SelfType(_) => HighlightTag::TypeSelf,
        NameDefinition::TypeParam(_) => HighlightTag::TypeParam,
        NameDefinition::Local(local) => {
            let mut h = Highlight::new(HighlightTag::Variable);
            if local.is_mut(db) || local.ty(db).is_mutable_reference() {
                h |= HighlightModifier::Mutable;
            }
            return h;
        }
    }
    .into()
}

#[cfg(test)]
mod tests {
    use std::fs;

    use test_utils::{assert_eq_text, project_dir, read_text};

    use crate::{
        mock_analysis::{single_file, MockAnalysis},
        FileRange, TextRange,
    };

    #[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>();
}

macro_rules! def_fn {
    ($($tt:tt)*) => {$($tt)*}
}

def_fn!{
    fn bar() -> u32 {
        100
    }
}

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

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

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

    y;
}

enum E<X> {
    V(X)
}

impl<X> E<X> {
    fn new<T>() -> E<T> {}
}
"#
            .trim(),
        );
        let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlighting.html");
        let actual_html = &analysis.highlight_as_html(file_id, false).unwrap();
        let expected_html = &read_text(&dst_file);
        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/src/snapshots/rainbow_highlighting.html");
        let actual_html = &analysis.highlight_as_html(file_id, true).unwrap();
        let expected_html = &read_text(&dst_file);
        fs::write(dst_file, &actual_html).unwrap();
        assert_eq_text!(expected_html, actual_html);
    }

    #[test]
    fn accidentally_quadratic() {
        let file = project_dir().join("crates/ra_syntax/test_data/accidentally_quadratic");
        let src = fs::read_to_string(file).unwrap();

        let mut mock = MockAnalysis::new();
        let file_id = mock.add_file("/main.rs", &src);
        let host = mock.analysis_host();

        // let t = std::time::Instant::now();
        let _ = host.analysis().highlight(file_id).unwrap();
        // eprintln!("elapsed: {:?}", t.elapsed());
    }

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

        // The "x"
        let highlights = &analysis
            .highlight_range(FileRange {
                file_id,
                range: TextRange::offset_len(82.into(), 1.into()),
            })
            .unwrap();

        assert_eq!(&highlights[0].highlight.to_string(), "field");
    }
}
<|endoftext|>


<commit_before>use story_builder::article_provider::ArticleProvider;

pub struct StoryBuilder<'a> {
    article_provider: &'a ArticleProvider,
    max_depth: u8,
}

impl <'a> StoryBuilder<'a>  {
    pub fn new(article_provider: &'a ArticleProvider) -> StoryBuilder<'a> {
        StoryBuilder {
            article_provider,
            max_depth: 5, // default value for now
        }
    }

    fn build_story(&self, start_topic: &str , end_topic: &str) -> Result<String, String> {
                   // If one of the topics is an empty string, do not try to make a story out of it.
        if start_topic == "" {
            return Err("Missing start topic.".to_owned());
        }
        if end_topic == "" {
            return Err("Missing end topic.".to_owned());
        }

                   // If both topics are the same,
                   // there is no point in trying to figure out the story.
        if start_topic == end_topic {
            return Err("No story to build; same start and end topics.".to_owned());
        }

                   // Load the first article
        let start_article = match self.article_provider.get(start_topic) {
            Some(start_article) => start_article,
            None => {return Err(self.build_suggestions_msg(start_topic));},
        };

                   // Load the end article
        let end_article = match self.article_provider.get(end_topic) {
            Some(end_topic) => end_topic,
            None => {return Err(self.build_suggestions_msg(end_topic));},
        };

                   /* To build a story, we need to build a tree starting at the start_article
                      node and going down in a "breadth-first" way; that way, once we find
                      the end note, we know it is the shortest path to it. Also, going depth-first
                      will be impossibly long to complete sincethe depth of the wikipedia
                      article tree is almost infinite. */

                   // Initialize the last level vector with the start article:
        let last_level = vec![start_article];
                   /* We look for a paragraph that holds a reference to our end topic
                      somewhere in the last level: */
        if let Some(paragraph) = start_article.get_paragraphs().iter().find(|par| {
                   // if any of the topics in the paragraph is <end>, return it.
            par.topics.iter().any(|topic| {topic == end_topic})
            }) {
                   // We found the paragraph; return it directly.
            return Ok(format!("{}\r\n", &paragraph.text));
        }

        Err(format!("Reached depth of <{}> without finding <{}>. Stopping search.", self.max_depth, end_topic).to_owned())
    }

    fn build_suggestions_msg(&self, topic: &str) -> String {
        let mut msg = String::from(format!("Cannot find wikipedia article for <{}>, try one of the following suggestions:\r\n", topic));
        for sugg in self.article_provider.search(topic) {
        msg.push_str(&format!("- {}\r\n", &sugg));
        }

        msg
    }
}

struct TreeNode<T> {
    data: T,
}

impl <T> TreeNode<T> {
    fn new(data: T) -> TreeNode<T> {
        TreeNode {
            data
        }
    }
}

#[cfg(test)]
mod tests;
<commit_msg>Moved paragraph searching logic int separate function<commit_after>use story_builder::article_provider::*;
use std::borrow::Borrow;

pub struct StoryBuilder<'a> {
    article_provider: &'a ArticleProvider,
    max_depth: u8,
}

impl <'a> StoryBuilder<'a>  {
    pub fn new(article_provider: &'a ArticleProvider) -> StoryBuilder<'a> {
        StoryBuilder {
            article_provider,
            max_depth: 5, // default value for now
        }
    }

    fn build_story(&self, start_topic: &str , end_topic: &str) -> Result<String, String> {
                   // If one of the topics is an empty string, do not try to make a story out of it.
        if start_topic == "" {
            return Err("Missing start topic.".to_owned());
        }
        if end_topic == "" {
            return Err("Missing end topic.".to_owned());
        }

                   // If both topics are the same,
                   // there is no point in trying to figure out the story.
        if start_topic == end_topic {
            return Err("No story to build; same start and end topics.".to_owned());
        }

                   // Load the first article
        let start_article = match self.article_provider.get(start_topic) {
            Some(start_article) => start_article,
            None => {return Err(self.build_suggestions_msg(start_topic));},
        };

                   // Load the end article
        let end_article = match self.article_provider.get(end_topic) {
            Some(end_topic) => end_topic,
            None => {return Err(self.build_suggestions_msg(end_topic));},
        };

                   /* To build a story, we need to build a tree starting at the start_article
                      node and going down in a "breadth-first" way; that way, once we find
                      the end note, we know it is the shortest path to it. Also, going depth-first
                      will be impossibly long to complete sincethe depth of the wikipedia
                      article tree is almost infinite. */

                   // Initialize the last level vector with the start article:
        let last_level = vec![start_article];
                   /* We look for a paragraph that holds a reference to our end topic
                      somewhere in the last level we fetched: */
        for article in last_level.iter() {
            if let Some(text) = StoryBuilder::find_text_for_topic_in_article(article.borrow(), end_topic) {
                // Found the topic. Format and return.
                return Ok(format!("{}\r\n", text));
            }
        }

        Err(format!("Reached depth of <{}> without finding <{}>. Stopping search.", self.max_depth, end_topic).to_owned())
    }

    fn build_suggestions_msg(&self, topic: &str) -> String {
        let mut msg = String::from(format!("Cannot find wikipedia article for <{}>, try one of the following suggestions:\r\n", topic));
        for sugg in self.article_provider.search(topic) {
        msg.push_str(&format!("- {}\r\n", &sugg));
        }

        msg
    }

    fn find_text_for_topic_in_article<'b>(article: &'b Article, topic: &str) -> Option<&'b str> {
        if let Some(paragraph) = article.get_paragraphs().iter().find(|par| {
                   // if any of the topics in the paragraph is <end>, return it.
            par.topics.iter().any(|t| {t == topic})
            }) {
                   // We found the paragraph; return it directly.
            return Some(&paragraph.text);
        } else {
            None
        }
    }
}

struct TreeNode<T> {
    data: T,
}

impl <T> TreeNode<T> {
    fn new(data: T) -> TreeNode<T> {
        TreeNode {
            data
        }
    }
}

#[cfg(test)]
mod tests;
<|endoftext|>


<commit_before>#![warn(missing_docs)]

#[macro_use]
extern crate bitflags;
extern crate libc;
extern crate nix;

extern crate wayland_commons;
#[cfg(feature = "native_lib")]
#[macro_use]
extern crate wayland_sys;

mod client;
mod display;
mod event_loop;
mod globals;
mod resource;
pub mod sources;

pub use client::Client;
pub use display::Display;
pub use globals::Global;
pub use event_loop::{EventLoop, LoopSignal, LoopToken};
pub use resource::{NewResource, Resource};

/// Re-export of wayland-commons
///
/// Common traits and functions to work with wayland objects
pub mod commons {
    pub use wayland_commons::*;
}

#[cfg(feature = "native_lib")]
/// C-associated types
///
/// Required for plugging wayland-scanner generated protocols
/// or interfacing with C code using wayland objects.
pub mod sys {
    pub use super::generated::c_interfaces as protocol_interfaces;
    pub use wayland_sys::{common, server};
}

/// Generated interfaces for the core wayland protocol
pub mod protocol {
    #[cfg(feature = "native_lib")]
    pub use generated::c_api::*;
}

mod generated {
    #![allow(dead_code, non_camel_case_types, unused_unsafe, unused_variables)]
    #![allow(non_upper_case_globals, non_snake_case, unused_imports)]
    #![allow(missing_docs)]

    #[cfg(feature = "native_lib")]
    pub mod c_interfaces {
        include!(concat!(env!("OUT_DIR"), "/wayland_c_interfaces.rs"));
    }
    #[cfg(feature = "native_lib")]
    pub mod c_api {
        pub(crate) use {NewResource, Resource};
        pub(crate) use wayland_commons::{AnonymousObject, Interface, MessageGroup};
        pub(crate) use wayland_sys as sys;
        include!(concat!(env!("OUT_DIR"), "/wayland_c_api.rs"));
    }
}
<commit_msg>server: crate docs<commit_after>//! Server-side Wayland connector
//!
//! ## Overview
//!
//! This crate provides the interfaces and machinery to safely create server
//! for the wayland protocol. It is a rust wrapper around the `libwayland-server.so`
//! C library.
//!
//! The wayland protocol revolves around the creation of various objects and the exchange
//! of messages associated to these objects. Whenever a client connects, a `Display` object
//! is automatically created in their object space, which they use as a root to create new
//! objects and bootstrap their state.
//!
//! ## Protocol and messages handling model
//!
//! The protocol being bi-directional, you can send and receive messages. Sending messages is
//! done via methods of `Resource<_>` objects, receiving and handling them is done by providing
//! implementations.
//!
//! ### Resources
//!
//! The protocol and message model is very similar to the one of `wayland-client`, with the
//! main difference being that the handles to objects are represented by the `Resource<I>`
//! type.
//!
//! These resources are used to senf messages to the clients (they are called "events" in the
//! wayland context). This is done by the `Resource::<I>::send(..)` method.
//!
//! There is not a 1 to 1 mapping between `Resource<I>` instances and protocol objects. Rather,
//! you can think of `Resource<I>` as an `Rc`-like handle to a wayland object. Multiple instances
//! of it can exist referring to the same protocol object.
//!
//! Similarly, the lifetimes of the protocol objects and the `Resource<I>` are not tighly tied.
//! As protocol objects are created and destroyed by protocol messages, it can happen that an object
//! gets destroyed while one or more `Resource<I>` still refers to it. In such case, these resources
//! will be disabled and their `alive()` method will start to return `false`. Events that are
//! subsequently sent to them are ignored.
//!
//! ### Implementations
//!
//! To receive and process messages from the clients to you (in wayland context they are
//! called "requests"), you need to provide an `Implementation` for each wayland object
//! created in the protocol session. Whenever a new protocol object is created, you will
//! receive a `NewResource<I>` object. Providing an implementation via its `implement()` method
//! will turn it into a regular `Resource<I>` object.
//!
//! **All objects must be implemented**, even if it is an implementation doing nothing.
//! Failure to do so (by dropping the `NewResource<I>` for example) can cause future fatal
//! protocol errors if the client tries to send a request to this object.
//!
//! An implementation is just a struct implementing the `Implementation<Resource<I>, I::Requests>`
//! trait, where `I` is the interface of the considered object:
//!
//! ```
//! // Example implementation for the wl_surface interface
//! use wayland_server::Resource;
//! use wayland_server::protocol::wl_surface;
//! use wayland_server::commons::Implementation;
//!
//! struct MyImpl {
//!    // ...
//! }
//!
//! impl Implementation<Resource<wl_surface::WlSurface>, wl_surface::Requests> for MyImpl {
//!     fn receive(&mut self, msg: wl_surface::Requests, resource: Resource<wl_surface::WlSurface>) {
//!         // process the message...
//!     }
//! }
//! # fn main() {}
//! ```
//!
//! The trait is also automatically implemented for `FnMut(I::Requests, Resource<I>)` closures,
//! so you can use them for simplicity if a full struct would be too cumbersome.
//!
//! The `Resource<I>` passed to your implementation is garanteed to be alive (as it just received
//! a request), unless the exact message received is a destructor (which is indicated in the API
//! documentations).
//!
//! ## Event loops and general structure
//!
//! The core of your server is the `Display` object. It represent the ability of your program to
//! process wayland messages. Once this object is created, you can configure it to listen on one
//! or more sockets for incoming client connections (see the `Display` docs for details).
//!
//! The crate also provides an event loop structure. An `EventLoop` is automatically created at the
//! same time as the `Display`, and it will handle the connections of your wayland clients. See the
//! `EventLoop` API documentation for explanations of its use.
//!
//! It is also possible to both create other event loops and insert other kind of sources of events
//! to the event loops. These functions are typically useful to integrate, as a wayland compositor,
//! with other parts of the system (typically listening on file destrictor describing input devices).
//! Adding sources to an event loop is done via the `LoopToken` type, that is retrieved by the
//! `token()` method of `EventLoop`. See their documentations for more details.

#![warn(missing_docs)]

#[macro_use]
extern crate bitflags;
extern crate libc;
extern crate nix;

extern crate wayland_commons;
#[cfg(feature = "native_lib")]
#[macro_use]
extern crate wayland_sys;

mod client;
mod display;
mod event_loop;
mod globals;
mod resource;
pub mod sources;

pub use client::Client;
pub use display::Display;
pub use globals::Global;
pub use event_loop::{EventLoop, LoopSignal, LoopToken};
pub use resource::{NewResource, Resource};

/// Re-export of wayland-commons
///
/// Common traits and functions to work with wayland objects
pub mod commons {
    pub use wayland_commons::*;
}

#[cfg(feature = "native_lib")]
/// C-associated types
///
/// Required for plugging wayland-scanner generated protocols
/// or interfacing with C code using wayland objects.
pub mod sys {
    pub use super::generated::c_interfaces as protocol_interfaces;
    pub use wayland_sys::{common, server};
}

/// Generated interfaces for the core wayland protocol
pub mod protocol {
    #[cfg(feature = "native_lib")]
    pub use generated::c_api::*;
}

mod generated {
    #![allow(dead_code, non_camel_case_types, unused_unsafe, unused_variables)]
    #![allow(non_upper_case_globals, non_snake_case, unused_imports)]
    #![allow(missing_docs)]

    #[cfg(feature = "native_lib")]
    pub mod c_interfaces {
        include!(concat!(env!("OUT_DIR"), "/wayland_c_interfaces.rs"));
    }
    #[cfg(feature = "native_lib")]
    pub mod c_api {
        pub(crate) use {NewResource, Resource};
        pub(crate) use wayland_commons::{AnonymousObject, Interface, MessageGroup};
        pub(crate) use wayland_sys as sys;
        include!(concat!(env!("OUT_DIR"), "/wayland_c_api.rs"));
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use about_loader;
use mime_classifier::MimeClassifier;
use mime_guess::guess_mime_type;
use msg::constellation_msg::{PipelineId, ReferrerPolicy};
use net_traits::{LoadConsumer, LoadData, LoadOrigin, Metadata, NetworkError};
use net_traits::ProgressMsg::{Done, Payload};
use resource_thread::{CancellationListener, ProgressSender};
use resource_thread::{send_error, start_sending_sniffed_opt};
use std::borrow::ToOwned;
use std::error::Error;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::sync::Arc;
use url::Url;
use util::thread::spawn_named;

static READ_SIZE: usize = 8192;

enum ReadStatus {
    Partial(Vec<u8>),
    EOF,
}

enum LoadResult {
    Cancelled,
    Finished,
}

struct FileLoadOrigin;
impl LoadOrigin for FileLoadOrigin {
    fn referrer_url(&self) -> Option<Url> {
        None
    }
    fn referrer_policy(&self) -> Option<ReferrerPolicy> {
        None
    }
    fn pipeline_id(&self) -> Option<PipelineId> {
        None
    }
}

fn read_block(reader: &mut File) -> Result<ReadStatus, String> {
    let mut buf = vec![0; READ_SIZE];
    match reader.read(&mut buf) {
        Ok(0) => Ok(ReadStatus::EOF),
        Ok(n) => {
            buf.truncate(n);
            Ok(ReadStatus::Partial(buf))
        }
        Err(e) => Err(e.description().to_owned()),
    }
}

fn read_all(reader: &mut File, progress_chan: &ProgressSender, cancel_listener: &CancellationListener)
            -> Result<LoadResult, String> {
    while !cancel_listener.is_cancelled() {
        match try!(read_block(reader)) {
            ReadStatus::Partial(buf) => progress_chan.send(Payload(buf)).unwrap(),
            ReadStatus::EOF => return Ok(LoadResult::Finished),
        }
    }
    let _ = progress_chan.send(Done(Err(NetworkError::LoadCancelled)));
    Ok(LoadResult::Cancelled)
}

fn get_progress_chan(load_data: LoadData, file_path: PathBuf,
                     senders: LoadConsumer, classifier: Arc<MimeClassifier>, buf: &[u8])
                     -> Result<ProgressSender, ()> {
    let mut metadata = Metadata::default(load_data.url);
    let mime_type = guess_mime_type(file_path.as_path());
    metadata.set_content_type(Some(&mime_type));
    return start_sending_sniffed_opt(senders, metadata, classifier, buf, load_data.context);
}

pub fn factory(load_data: LoadData,
               senders: LoadConsumer,
               classifier: Arc<MimeClassifier>,
               cancel_listener: CancellationListener) {
    assert!(load_data.url.scheme() == "file");
    spawn_named("file_loader".to_owned(), move || {
        let file_path = match load_data.url.to_file_path() {
            Ok(file_path) => file_path,
            Err(_) => {
                send_error(load_data.url, NetworkError::Internal("Could not parse path".to_owned()), senders);
                return;
            },
        };
        let mut file = File::open(&file_path);
        let reader = match file {
            Ok(ref mut reader) => reader,
            Err(_) => {
                // this should be one of the three errors listed in
                // http://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.open
                // but, we'll go for a "file not found!"
                let url = Url::parse("about:not-found").unwrap();
                let load_data_404 = LoadData::new(load_data.context, url, &FileLoadOrigin);
                about_loader::factory(load_data_404, senders, classifier, cancel_listener);
                return;
            }
        };

        if cancel_listener.is_cancelled() {
            if let Ok(progress_chan) = get_progress_chan(load_data, file_path,
                                                         senders, classifier, &[]) {
                let _ = progress_chan.send(Done(Err(NetworkError::LoadCancelled)));
            }
            return;
        }

        match read_block(reader) {
            Ok(ReadStatus::Partial(buf)) => {
                let progress_chan = get_progress_chan(load_data, file_path,
                                                      senders, classifier, &buf).ok().unwrap();
                progress_chan.send(Payload(buf)).unwrap();
                let read_result = read_all(reader, &progress_chan, &cancel_listener);
                if let Ok(load_result) = read_result {
                    match load_result {
                        LoadResult::Cancelled => return,
                        LoadResult::Finished => progress_chan.send(Done(Ok(()))).unwrap(),
                    }
                }
            }
            Ok(ReadStatus::EOF) => {
                if let Ok(chan) = get_progress_chan(load_data, file_path,
                                                    senders, classifier, &[]) {
                    let _ = chan.send(Done(Ok(())));
                }
            }
            Err(e) => {
                send_error(load_data.url, NetworkError::Internal(e), senders);
            }
        }
    });
}
<commit_msg>Don't require `PathBuf` ownership if we don't need it.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use about_loader;
use mime_classifier::MimeClassifier;
use mime_guess::guess_mime_type;
use msg::constellation_msg::{PipelineId, ReferrerPolicy};
use net_traits::{LoadConsumer, LoadData, LoadOrigin, Metadata, NetworkError};
use net_traits::ProgressMsg::{Done, Payload};
use resource_thread::{CancellationListener, ProgressSender};
use resource_thread::{send_error, start_sending_sniffed_opt};
use std::borrow::ToOwned;
use std::error::Error;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::sync::Arc;
use url::Url;
use util::thread::spawn_named;

static READ_SIZE: usize = 8192;

enum ReadStatus {
    Partial(Vec<u8>),
    EOF,
}

enum LoadResult {
    Cancelled,
    Finished,
}

struct FileLoadOrigin;
impl LoadOrigin for FileLoadOrigin {
    fn referrer_url(&self) -> Option<Url> {
        None
    }
    fn referrer_policy(&self) -> Option<ReferrerPolicy> {
        None
    }
    fn pipeline_id(&self) -> Option<PipelineId> {
        None
    }
}

fn read_block(reader: &mut File) -> Result<ReadStatus, String> {
    let mut buf = vec![0; READ_SIZE];
    match reader.read(&mut buf) {
        Ok(0) => Ok(ReadStatus::EOF),
        Ok(n) => {
            buf.truncate(n);
            Ok(ReadStatus::Partial(buf))
        }
        Err(e) => Err(e.description().to_owned()),
    }
}

fn read_all(reader: &mut File, progress_chan: &ProgressSender, cancel_listener: &CancellationListener)
            -> Result<LoadResult, String> {
    while !cancel_listener.is_cancelled() {
        match try!(read_block(reader)) {
            ReadStatus::Partial(buf) => progress_chan.send(Payload(buf)).unwrap(),
            ReadStatus::EOF => return Ok(LoadResult::Finished),
        }
    }
    let _ = progress_chan.send(Done(Err(NetworkError::LoadCancelled)));
    Ok(LoadResult::Cancelled)
}

fn get_progress_chan(load_data: LoadData, file_path: &Path,
                     senders: LoadConsumer, classifier: Arc<MimeClassifier>, buf: &[u8])
                     -> Result<ProgressSender, ()> {
    let mut metadata = Metadata::default(load_data.url);
    let mime_type = guess_mime_type(file_path);
    metadata.set_content_type(Some(&mime_type));
    return start_sending_sniffed_opt(senders, metadata, classifier, buf, load_data.context);
}

pub fn factory(load_data: LoadData,
               senders: LoadConsumer,
               classifier: Arc<MimeClassifier>,
               cancel_listener: CancellationListener) {
    assert!(load_data.url.scheme() == "file");
    spawn_named("file_loader".to_owned(), move || {
        let file_path = match load_data.url.to_file_path() {
            Ok(file_path) => file_path,
            Err(_) => {
                send_error(load_data.url, NetworkError::Internal("Could not parse path".to_owned()), senders);
                return;
            },
        };
        let mut file = File::open(&file_path);
        let reader = match file {
            Ok(ref mut reader) => reader,
            Err(_) => {
                // this should be one of the three errors listed in
                // http://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.open
                // but, we'll go for a "file not found!"
                let url = Url::parse("about:not-found").unwrap();
                let load_data_404 = LoadData::new(load_data.context, url, &FileLoadOrigin);
                about_loader::factory(load_data_404, senders, classifier, cancel_listener);
                return;
            }
        };

        if cancel_listener.is_cancelled() {
            if let Ok(progress_chan) = get_progress_chan(load_data, &file_path,
                                                         senders, classifier, &[]) {
                let _ = progress_chan.send(Done(Err(NetworkError::LoadCancelled)));
            }
            return;
        }

        match read_block(reader) {
            Ok(ReadStatus::Partial(buf)) => {
                let progress_chan = get_progress_chan(load_data, &file_path,
                                                      senders, classifier, &buf).ok().unwrap();
                progress_chan.send(Payload(buf)).unwrap();
                let read_result = read_all(reader, &progress_chan, &cancel_listener);
                if let Ok(load_result) = read_result {
                    match load_result {
                        LoadResult::Cancelled => return,
                        LoadResult::Finished => progress_chan.send(Done(Ok(()))).unwrap(),
                    }
                }
            }
            Ok(ReadStatus::EOF) => {
                if let Ok(chan) = get_progress_chan(load_data, &file_path,
                                                    senders, classifier, &[]) {
                    let _ = chan.send(Done(Ok(())));
                }
            }
            Err(e) => {
                send_error(load_data.url, NetworkError::Internal(e), senders);
            }
        }
    });
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::AttrBinding;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::InheritTypes::NodeCast;
use dom::bindings::global::Window;
use dom::bindings::js::{JS, JSRef, Temporary};
use dom::bindings::trace::Traceable;
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::element::{Element, AttributeHandlers};
use dom::node::Node;
use dom::window::Window;
use dom::virtualmethods::vtable_for;

use devtools_traits::AttrInfo;
use servo_util::atom::Atom;
use servo_util::namespace;
use servo_util::namespace::Namespace;
use servo_util::str::{DOMString, split_html_space_chars};
use std::cell::{Ref, RefCell};
use std::mem;
use std::slice::Items;

pub enum AttrSettingType {
    FirstSetAttr,
    ReplacedAttr,
}

#[deriving(PartialEq, Clone)]
#[jstraceable]
pub enum AttrValue {
    StringAttrValue(DOMString),
    TokenListAttrValue(DOMString, Vec<Atom>),
    UIntAttrValue(DOMString, u32),
    AtomAttrValue(Atom),
}

impl AttrValue {
    pub fn from_tokenlist(tokens: DOMString) -> AttrValue {
        let atoms = split_html_space_chars(tokens.as_slice())
            .map(|token| Atom::from_slice(token)).collect();
        TokenListAttrValue(tokens, atoms)
    }

    pub fn from_u32(string: DOMString, default: u32) -> AttrValue {
        let result: u32 = from_str(string.as_slice()).unwrap_or(default);
        UIntAttrValue(string, result)
    }

    pub fn from_atomic(string: DOMString) -> AttrValue {
        let value = Atom::from_slice(string.as_slice());
        AtomAttrValue(value)
    }

    pub fn tokens<'a>(&'a self) -> Option<Items<'a, Atom>> {
        match *self {
            TokenListAttrValue(_, ref tokens) => Some(tokens.iter()),
            _ => None
        }
    }
}

impl Str for AttrValue {
    fn as_slice<'a>(&'a self) -> &'a str {
        match *self {
            StringAttrValue(ref value) |
            TokenListAttrValue(ref value, _) |
            UIntAttrValue(ref value, _) => value.as_slice(),
            AtomAttrValue(ref value) => value.as_slice(),
        }
    }
}

#[jstraceable]
#[must_root]
pub struct Attr {
    reflector_: Reflector,
    local_name: Atom,
    value: Traceable<RefCell<AttrValue>>,
    pub name: Atom,
    pub namespace: Namespace,
    pub prefix: Option<DOMString>,

    /// the element that owns this attribute.
    owner: JS<Element>,
}

impl Reflectable for Attr {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        &self.reflector_
    }
}

impl Attr {
    fn new_inherited(local_name: Atom, value: AttrValue,
                     name: Atom, namespace: Namespace,
                     prefix: Option<DOMString>, owner: JSRef<Element>) -> Attr {
        Attr {
            reflector_: Reflector::new(),
            local_name: local_name,
            value: Traceable::new(RefCell::new(value)),
            name: name,
            namespace: namespace,
            prefix: prefix,
            owner: JS::from_rooted(owner),
        }
    }

    pub fn new(window: JSRef<Window>, local_name: Atom, value: AttrValue,
               name: Atom, namespace: Namespace,
               prefix: Option<DOMString>, owner: JSRef<Element>) -> Temporary<Attr> {
        reflect_dom_object(box Attr::new_inherited(local_name, value, name, namespace, prefix, owner),
                           &Window(window), AttrBinding::Wrap)
    }
}

impl<'a> AttrMethods for JSRef<'a, Attr> {
    fn LocalName(self) -> DOMString {
        self.local_name().as_slice().to_string()
    }

    fn Value(self) -> DOMString {
        self.value().as_slice().to_string()
    }

    fn SetValue(self, value: DOMString) {
        let owner = self.owner.root();
        let value = owner.deref().parse_attribute(&self.namespace, self.local_name(), value);
        self.set_value(ReplacedAttr, value);
    }

    fn Name(self) -> DOMString {
        self.name.as_slice().to_string()
    }

    fn GetNamespaceURI(self) -> Option<DOMString> {
        match self.namespace.to_str() {
            "" => None,
            url => Some(url.to_string()),
        }
    }

    fn GetPrefix(self) -> Option<DOMString> {
        self.prefix.clone()
    }
}

pub trait AttrHelpers {
    fn set_value(self, set_type: AttrSettingType, value: AttrValue);
    fn value<'a>(&'a self) -> Ref<'a, AttrValue>;
    fn local_name<'a>(&'a self) -> &'a Atom;
    fn summarize(self) -> AttrInfo;
}

impl<'a> AttrHelpers for JSRef<'a, Attr> {
    fn set_value(self, set_type: AttrSettingType, value: AttrValue) {
        let owner = self.owner.root();
        let node: JSRef<Node> = NodeCast::from_ref(*owner);
        let namespace_is_null = self.namespace == namespace::Null;

        match set_type {
            ReplacedAttr => {
                if namespace_is_null {
                    vtable_for(&node).before_remove_attr(
                        self.local_name(),
                        self.value().as_slice().to_string())
                }
            }
            FirstSetAttr => {}
        }

        *self.value.deref().borrow_mut() = value;

        if namespace_is_null {
            vtable_for(&node).after_set_attr(
                self.local_name(),
                self.value().as_slice().to_string())
        }
    }

    fn value<'a>(&'a self) -> Ref<'a, AttrValue> {
        self.value.deref().borrow()
    }

    fn local_name<'a>(&'a self) -> &'a Atom {
        &self.local_name
    }

    fn summarize(self) -> AttrInfo {
        AttrInfo {
            namespace: self.namespace.to_str().to_string(),
            name: self.Name(),
            value: self.Value(),
        }
    }
}

pub trait AttrHelpersForLayout {
    unsafe fn value_ref_forever(&self) -> &'static str;
    unsafe fn value_atom_forever(&self) -> Option<Atom>;
    unsafe fn value_tokens_forever(&self) -> Option<Items<Atom>>;
    unsafe fn local_name_atom_forever(&self) -> Atom;
}

impl AttrHelpersForLayout for Attr {
    unsafe fn value_ref_forever(&self) -> &'static str {
        // cast to point to T in RefCell<T> directly
        let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(self.value.deref());
        value.as_slice()
    }

    unsafe fn value_atom_forever(&self) -> Option<Atom> {
        // cast to point to T in RefCell<T> directly
        let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(self.value.deref());
        match *value {
            AtomAttrValue(ref val) => Some(val.clone()),
            _ => None,
        }
    }

    unsafe fn value_tokens_forever(&self) -> Option<Items<Atom>> {
        // cast to point to T in RefCell<T> directly
        let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(self.value.deref());
        match *value {
            TokenListAttrValue(_, ref tokens) => Some(tokens.iter()),
            _ => None,
        }
    }

    unsafe fn local_name_atom_forever(&self) -> Atom {
        self.local_name.clone()
    }
}
<commit_msg>Make the AttrHelpers trait use extended_deref<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::AttrBinding;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::InheritTypes::NodeCast;
use dom::bindings::global::Window;
use dom::bindings::js::{JS, JSRef, Temporary};
use dom::bindings::trace::Traceable;
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::element::{Element, AttributeHandlers};
use dom::node::Node;
use dom::window::Window;
use dom::virtualmethods::vtable_for;

use devtools_traits::AttrInfo;
use servo_util::atom::Atom;
use servo_util::namespace;
use servo_util::namespace::Namespace;
use servo_util::str::{DOMString, split_html_space_chars};
use std::cell::{Ref, RefCell};
use std::mem;
use std::slice::Items;

pub enum AttrSettingType {
    FirstSetAttr,
    ReplacedAttr,
}

#[deriving(PartialEq, Clone)]
#[jstraceable]
pub enum AttrValue {
    StringAttrValue(DOMString),
    TokenListAttrValue(DOMString, Vec<Atom>),
    UIntAttrValue(DOMString, u32),
    AtomAttrValue(Atom),
}

impl AttrValue {
    pub fn from_tokenlist(tokens: DOMString) -> AttrValue {
        let atoms = split_html_space_chars(tokens.as_slice())
            .map(|token| Atom::from_slice(token)).collect();
        TokenListAttrValue(tokens, atoms)
    }

    pub fn from_u32(string: DOMString, default: u32) -> AttrValue {
        let result: u32 = from_str(string.as_slice()).unwrap_or(default);
        UIntAttrValue(string, result)
    }

    pub fn from_atomic(string: DOMString) -> AttrValue {
        let value = Atom::from_slice(string.as_slice());
        AtomAttrValue(value)
    }

    pub fn tokens<'a>(&'a self) -> Option<Items<'a, Atom>> {
        match *self {
            TokenListAttrValue(_, ref tokens) => Some(tokens.iter()),
            _ => None
        }
    }
}

impl Str for AttrValue {
    fn as_slice<'a>(&'a self) -> &'a str {
        match *self {
            StringAttrValue(ref value) |
            TokenListAttrValue(ref value, _) |
            UIntAttrValue(ref value, _) => value.as_slice(),
            AtomAttrValue(ref value) => value.as_slice(),
        }
    }
}

#[jstraceable]
#[must_root]
pub struct Attr {
    reflector_: Reflector,
    local_name: Atom,
    value: Traceable<RefCell<AttrValue>>,
    pub name: Atom,
    pub namespace: Namespace,
    pub prefix: Option<DOMString>,

    /// the element that owns this attribute.
    owner: JS<Element>,
}

impl Reflectable for Attr {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        &self.reflector_
    }
}

impl Attr {
    fn new_inherited(local_name: Atom, value: AttrValue,
                     name: Atom, namespace: Namespace,
                     prefix: Option<DOMString>, owner: JSRef<Element>) -> Attr {
        Attr {
            reflector_: Reflector::new(),
            local_name: local_name,
            value: Traceable::new(RefCell::new(value)),
            name: name,
            namespace: namespace,
            prefix: prefix,
            owner: JS::from_rooted(owner),
        }
    }

    pub fn new(window: JSRef<Window>, local_name: Atom, value: AttrValue,
               name: Atom, namespace: Namespace,
               prefix: Option<DOMString>, owner: JSRef<Element>) -> Temporary<Attr> {
        reflect_dom_object(box Attr::new_inherited(local_name, value, name, namespace, prefix, owner),
                           &Window(window), AttrBinding::Wrap)
    }
}

impl<'a> AttrMethods for JSRef<'a, Attr> {
    fn LocalName(self) -> DOMString {
        self.local_name().as_slice().to_string()
    }

    fn Value(self) -> DOMString {
        self.value().as_slice().to_string()
    }

    fn SetValue(self, value: DOMString) {
        let owner = self.owner.root();
        let value = owner.deref().parse_attribute(&self.namespace, self.local_name(), value);
        self.set_value(ReplacedAttr, value);
    }

    fn Name(self) -> DOMString {
        self.name.as_slice().to_string()
    }

    fn GetNamespaceURI(self) -> Option<DOMString> {
        match self.namespace.to_str() {
            "" => None,
            url => Some(url.to_string()),
        }
    }

    fn GetPrefix(self) -> Option<DOMString> {
        self.prefix.clone()
    }
}

pub trait AttrHelpers<'a> {
    fn set_value(self, set_type: AttrSettingType, value: AttrValue);
    fn value(self) -> Ref<'a, AttrValue>;
    fn local_name(self) -> &'a Atom;
    fn summarize(self) -> AttrInfo;
}

impl<'a> AttrHelpers<'a> for JSRef<'a, Attr> {
    fn set_value(self, set_type: AttrSettingType, value: AttrValue) {
        let owner = self.owner.root();
        let node: JSRef<Node> = NodeCast::from_ref(*owner);
        let namespace_is_null = self.namespace == namespace::Null;

        match set_type {
            ReplacedAttr => {
                if namespace_is_null {
                    vtable_for(&node).before_remove_attr(
                        self.local_name(),
                        self.value().as_slice().to_string())
                }
            }
            FirstSetAttr => {}
        }

        *self.value.deref().borrow_mut() = value;

        if namespace_is_null {
            vtable_for(&node).after_set_attr(
                self.local_name(),
                self.value().as_slice().to_string())
        }
    }

    fn value(self) -> Ref<'a, AttrValue> {
        self.extended_deref().value.borrow()
    }

    fn local_name(self) -> &'a Atom {
        &self.extended_deref().local_name
    }

    fn summarize(self) -> AttrInfo {
        AttrInfo {
            namespace: self.namespace.to_str().to_string(),
            name: self.Name(),
            value: self.Value(),
        }
    }
}

pub trait AttrHelpersForLayout {
    unsafe fn value_ref_forever(&self) -> &'static str;
    unsafe fn value_atom_forever(&self) -> Option<Atom>;
    unsafe fn value_tokens_forever(&self) -> Option<Items<Atom>>;
    unsafe fn local_name_atom_forever(&self) -> Atom;
}

impl AttrHelpersForLayout for Attr {
    unsafe fn value_ref_forever(&self) -> &'static str {
        // cast to point to T in RefCell<T> directly
        let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(self.value.deref());
        value.as_slice()
    }

    unsafe fn value_atom_forever(&self) -> Option<Atom> {
        // cast to point to T in RefCell<T> directly
        let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(self.value.deref());
        match *value {
            AtomAttrValue(ref val) => Some(val.clone()),
            _ => None,
        }
    }

    unsafe fn value_tokens_forever(&self) -> Option<Items<Atom>> {
        // cast to point to T in RefCell<T> directly
        let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(self.value.deref());
        match *value {
            TokenListAttrValue(_, ref tokens) => Some(tokens.iter()),
            _ => None,
        }
    }

    unsafe fn local_name_atom_forever(&self) -> Atom {
        self.local_name.clone()
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::ops::{Deref, DerefMut};

use toml::Value;
use uuid::Uuid;

use task_hookrs::task::Task as TTask;

use libimagstore::store::{FileLockEntry, Store};
use libimagstore::storeid::{IntoStoreId, StoreIdIterator, StoreId};
use module_path::ModuleEntryPath;

use error::{TodoError, TodoErrorKind};
use result::Result;

/// Task struct containing a `FileLockEntry`
#[derive(Debug)]
pub struct Task<'a>(FileLockEntry<'a>);

impl<'a> Task<'a> {

    /// Concstructs a new `Task` with a `FileLockEntry`
    pub fn new(fle: FileLockEntry<'a>) -> Task<'a> {
        Task(fle)
    }

    pub fn delete_by_uuid(store: &Store, uuid: Uuid) -> Result<()> {
        store.delete(ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid())
            .map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
    }

    pub fn all_as_ids(store: &Store) -> Result<StoreIdIterator> {
        store.retrieve_for_module("todo/taskwarrior")
            .map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
    }

    pub fn all(store: &Store) -> Result<TaskIterator> {
        Task::all_as_ids(store)
            .map(|iter| TaskIterator::new(store, iter))
    }

}

impl<'a> Deref for Task<'a> {
    type Target = FileLockEntry<'a>;

    fn deref(&self) -> &FileLockEntry<'a> {
        &self.0
    }

}

impl<'a> DerefMut for Task<'a> {

    fn deref_mut(&mut self) -> &mut FileLockEntry<'a> {
        &mut self.0
    }

}

/// A trait to get a `libimagtodo::task::Task` out of the implementing object.
/// This Task struct is merely a wrapper for a `FileLockEntry`, therefore the function name
/// `into_filelockentry`.
pub trait IntoTask<'a> {

    /// # Usage
    /// ```ignore
    /// use std::io::stdin;
    ///
    /// use task_hookrs::task::Task;
    /// use task_hookrs::import::import;
    /// use libimagstore::store::{Store, FileLockEntry};
    ///
    /// if let Ok(task_hookrs_task) = import(stdin()) {
    ///     // Store is given at runtime
    ///     let task = task_hookrs_task.into_filelockentry(store);
    ///     println!("Task with uuid: {}", task.flentry.get_header().get("todo.uuid"));
    /// }
    /// ```
    fn into_task(self, store : &'a Store) -> Result<Task<'a>>;

}

impl<'a> IntoTask<'a> for TTask {

    fn into_task(self, store : &'a Store) -> Result<Task<'a>> {
        let uuid     = self.uuid();
        let store_id = ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid();

        match store.retrieve(store_id) {
            Err(e) => return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))),
            Ok(mut fle) => {
                {
                    let mut header = fle.get_header_mut();
                    match header.read("todo") {
                        Ok(None) => {
                            if let Err(e) = header.set("todo", Value::Table(BTreeMap::new())) {
                                return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                            }
                        }
                        Ok(Some(_)) => { }
                        Err(e) => {
                            return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                        }
                    }

                    if let Err(e) = header.set("todo.uuid", Value::String(format!("{}",uuid))) {
                        return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                    }
                }

                // If none of the errors above have returned the function, everything is fine
                Ok(Task::new(fle))
            }
        }
    }

}

trait FromStoreId {
    fn from_storeid<'a>(&'a Store, StoreId) -> Result<Task<'a>>;
}

impl<'a> FromStoreId for Task<'a> {

    fn from_storeid<'b>(store: &'b Store, id: StoreId) -> Result<Task<'b>> {
        match store.retrieve(id) {
            Err(e) => Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))),
            Ok(c)  => Ok(Task::new( c )),
        }
    }
}

pub struct TaskIterator<'a> {
    store: &'a Store,
    iditer: StoreIdIterator,
}

impl<'a> TaskIterator<'a> {

    pub fn new(store: &'a Store, iditer: StoreIdIterator) -> TaskIterator<'a> {
        TaskIterator {
            store: store,
            iditer: iditer,
        }
    }

}

impl<'a> Iterator for TaskIterator<'a> {
    type Item = Result<Task<'a>>;

    fn next(&mut self) -> Option<Result<Task<'a>>> {
        self.iditer.next().map(|id| Task::from_storeid(self.store, id))
    }
}

<commit_msg>Add Task::import()<commit_after>use std::collections::BTreeMap;
use std::ops::{Deref, DerefMut};
use std::io::BufRead;

use toml::Value;
use uuid::Uuid;

use task_hookrs::task::Task as TTask;
use task_hookrs::import::{import_task, import_tasks};

use libimagstore::store::{FileLockEntry, Store};
use libimagstore::storeid::{IntoStoreId, StoreIdIterator, StoreId};
use module_path::ModuleEntryPath;

use error::{TodoError, TodoErrorKind, MapErrInto};
use result::Result;

/// Task struct containing a `FileLockEntry`
#[derive(Debug)]
pub struct Task<'a>(FileLockEntry<'a>);

impl<'a> Task<'a> {

    /// Concstructs a new `Task` with a `FileLockEntry`
    pub fn new(fle: FileLockEntry<'a>) -> Task<'a> {
        Task(fle)
    }

    pub fn import<R: BufRead>(store: &'a Store, mut r: R) -> Result<(Task<'a>, Uuid)> {
        let mut line = String::new();
        r.read_line(&mut line);
        import_task(&line.as_str())
            .map_err_into(TodoErrorKind::ImportError)
            .and_then(|t| {
                let uuid = t.uuid().clone();
                t.into_task(store).map(|t| (t, uuid))
            })
    }

    pub fn delete_by_uuid(store: &Store, uuid: Uuid) -> Result<()> {
        store.delete(ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid())
            .map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
    }

    pub fn all_as_ids(store: &Store) -> Result<StoreIdIterator> {
        store.retrieve_for_module("todo/taskwarrior")
            .map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
    }

    pub fn all(store: &Store) -> Result<TaskIterator> {
        Task::all_as_ids(store)
            .map(|iter| TaskIterator::new(store, iter))
    }

}

impl<'a> Deref for Task<'a> {
    type Target = FileLockEntry<'a>;

    fn deref(&self) -> &FileLockEntry<'a> {
        &self.0
    }

}

impl<'a> DerefMut for Task<'a> {

    fn deref_mut(&mut self) -> &mut FileLockEntry<'a> {
        &mut self.0
    }

}

/// A trait to get a `libimagtodo::task::Task` out of the implementing object.
/// This Task struct is merely a wrapper for a `FileLockEntry`, therefore the function name
/// `into_filelockentry`.
pub trait IntoTask<'a> {

    /// # Usage
    /// ```ignore
    /// use std::io::stdin;
    ///
    /// use task_hookrs::task::Task;
    /// use task_hookrs::import::import;
    /// use libimagstore::store::{Store, FileLockEntry};
    ///
    /// if let Ok(task_hookrs_task) = import(stdin()) {
    ///     // Store is given at runtime
    ///     let task = task_hookrs_task.into_filelockentry(store);
    ///     println!("Task with uuid: {}", task.flentry.get_header().get("todo.uuid"));
    /// }
    /// ```
    fn into_task(self, store : &'a Store) -> Result<Task<'a>>;

}

impl<'a> IntoTask<'a> for TTask {

    fn into_task(self, store : &'a Store) -> Result<Task<'a>> {
        let uuid     = self.uuid();
        let store_id = ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid();

        match store.retrieve(store_id) {
            Err(e) => return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))),
            Ok(mut fle) => {
                {
                    let mut header = fle.get_header_mut();
                    match header.read("todo") {
                        Ok(None) => {
                            if let Err(e) = header.set("todo", Value::Table(BTreeMap::new())) {
                                return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                            }
                        }
                        Ok(Some(_)) => { }
                        Err(e) => {
                            return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                        }
                    }

                    if let Err(e) = header.set("todo.uuid", Value::String(format!("{}",uuid))) {
                        return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                    }
                }

                // If none of the errors above have returned the function, everything is fine
                Ok(Task::new(fle))
            }
        }
    }

}

trait FromStoreId {
    fn from_storeid<'a>(&'a Store, StoreId) -> Result<Task<'a>>;
}

impl<'a> FromStoreId for Task<'a> {

    fn from_storeid<'b>(store: &'b Store, id: StoreId) -> Result<Task<'b>> {
        match store.retrieve(id) {
            Err(e) => Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))),
            Ok(c)  => Ok(Task::new( c )),
        }
    }
}

pub struct TaskIterator<'a> {
    store: &'a Store,
    iditer: StoreIdIterator,
}

impl<'a> TaskIterator<'a> {

    pub fn new(store: &'a Store, iditer: StoreIdIterator) -> TaskIterator<'a> {
        TaskIterator {
            store: store,
            iditer: iditer,
        }
    }

}

impl<'a> Iterator for TaskIterator<'a> {
    type Item = Result<Task<'a>>;

    fn next(&mut self) -> Option<Result<Task<'a>>> {
        self.iditer.next().map(|id| Task::from_storeid(self.store, id))
    }
}

<|endoftext|>