<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

//! Utilities for providing the goto definition feature

use crate::{
    location::to_lsp_location_of_graphql_literal,
    lsp::GotoDefinitionResponse,
    lsp_runtime_error::{LSPRuntimeError, LSPRuntimeResult},
    node_resolution_info::NodeKind,
    node_resolution_info::NodeResolutionInfo,
    server::LSPState,
    LSPExtraDataProvider,
};
use common::PerfLogger;
use graphql_ir::Program;
use interner::StringKey;
use lsp_types::{
    request::{GotoDefinition, Request},
    Url,
};
use schema::Schema;
use std::{
    collections::HashMap,
    path::PathBuf,
    str,
    sync::{Arc, RwLock},
};

fn get_goto_definition_response(
    node_resolution_info: NodeResolutionInfo,
    source_programs: &Arc<RwLock<HashMap<StringKey, Program>>>,
    root_dir: &PathBuf,
    // https://github.com/rust-lang/rust-clippy/issues/3971
    #[allow(clippy::borrowed_box)] extra_data_provider: &Box<dyn LSPExtraDataProvider + 'static>,
) -> LSPRuntimeResult<GotoDefinitionResponse> {
    match node_resolution_info.kind {
        NodeKind::FragmentSpread(fragment_name) => {
            let project_name = node_resolution_info.project_name;
            if let Some(source_program) = source_programs.read().unwrap().get(&project_name) {
                let fragment = source_program.fragment(fragment_name).ok_or_else(|| {
                    LSPRuntimeError::UnexpectedError(format!(
                        "Could not find fragment with name {}",
                        fragment_name
                    ))
                })?;

                Ok(GotoDefinitionResponse::Scalar(
                    to_lsp_location_of_graphql_literal(fragment.name.location, root_dir)?,
                ))
            } else {
                Err(LSPRuntimeError::UnexpectedError(format!(
                    "Project name {} not found",
                    project_name
                )))
            }
        }
        NodeKind::FieldName => {
            let project_name = node_resolution_info.project_name;
            let programs = source_programs.read().unwrap();
            let source_program = programs.get(&project_name).ok_or_else(|| {
                LSPRuntimeError::UnexpectedError(format!("Project name {} not found", project_name))
            })?;
            let field = node_resolution_info
                .type_path
                .resolve_current_field(&source_program.schema)
                .ok_or_else(|| {
                    LSPRuntimeError::UnexpectedError("Could not resolve field".to_string())
                })?;
            let parent_name = source_program
                .schema
                .get_type_name(field.parent_type.unwrap());

            let provider_response = extra_data_provider
                .resolve_field_definition(
                    project_name.to_string(),
                    root_dir,
                    parent_name.to_string(),
                    Some(field.name.to_string()),
                )
                .ok_or(LSPRuntimeError::ExpectedError)?;
            let (path, line) = provider_response.map_err(|e| -> LSPRuntimeError {
                LSPRuntimeError::UnexpectedError(format!(
                    "Error resolving field definition location: {}",
                    e
                ))
            })?;
            Ok(GotoDefinitionResponse::Scalar(get_location(&path, line)?))
        }
        NodeKind::TypeCondition(type_name) => {
            let project_name = node_resolution_info.project_name;
            let provider_response = extra_data_provider
                .resolve_field_definition(
                    project_name.to_string(),
                    root_dir,
                    type_name.to_string(),
                    None,
                )
                .ok_or(LSPRuntimeError::ExpectedError)?;
            let (path, line) = provider_response.map_err(|e| -> LSPRuntimeError {
                LSPRuntimeError::UnexpectedError(format!(
                    "Error resolving field definition location: {}",
                    e
                ))
            })?;
            Ok(GotoDefinitionResponse::Scalar(get_location(&path, line)?))
        }
        _ => Err(LSPRuntimeError::ExpectedError),
    }
}

pub(crate) fn on_goto_definition<TPerfLogger: PerfLogger + 'static>(
    state: &mut LSPState<TPerfLogger>,
    params: <GotoDefinition as Request>::Params,
) -> LSPRuntimeResult<<GotoDefinition as Request>::Result> {
    let node_resolution_info = state.resolve_node(params)?;

    let goto_definition_response = get_goto_definition_response(
        node_resolution_info,
        state.get_source_programs_ref(),
        state.root_dir(),
        &state.extra_data_provider,
    )?;

    Ok(Some(goto_definition_response))
}

fn get_location(path: &str, line: u64) -> Result<lsp_types::Location, LSPRuntimeError> {
    let start = lsp_types::Position { line, character: 0 };
    let range = lsp_types::Range { start, end: start };

    let uri = Url::parse(&format!("file://{}", path)).map_err(|e| {
        LSPRuntimeError::UnexpectedError(format!("Could not parse path as URL: {}", e))
    })?;

    Ok(lsp_types::Location { uri, range })
}
<commit_msg>Use ResolutionPath for goto_definition<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

//! Utilities for providing the goto definition feature

use crate::{
    location::to_lsp_location_of_graphql_literal,
    lsp::GotoDefinitionResponse,
    lsp_runtime_error::{LSPRuntimeError, LSPRuntimeResult},
    resolution_path::utils::find_selection_parent_type,
    resolution_path::{
        IdentParent, IdentPath, LinkedFieldPath, ResolutionPath, ResolvePosition, ScalarFieldPath,
        SelectionParent, TypeConditionPath,
    },
    server::LSPState,
    LSPExtraDataProvider,
};
use common::PerfLogger;
use graphql_ir::Program;
use interner::StringKey;
use lsp_types::{
    request::{GotoDefinition, Request},
    Url,
};
use schema::Schema;
use std::{
    collections::HashMap,
    path::PathBuf,
    str,
    sync::{Arc, RwLock},
};

fn get_goto_definition_response<'a>(
    node_path: ResolutionPath<'a>,
    project_name: StringKey,
    source_programs: &Arc<RwLock<HashMap<StringKey, Program>>>,
    root_dir: &PathBuf,
    // https://github.com/rust-lang/rust-clippy/issues/3971
    #[allow(clippy::borrowed_box)] extra_data_provider: &Box<dyn LSPExtraDataProvider + 'static>,
) -> LSPRuntimeResult<GotoDefinitionResponse> {
    match node_path {
        ResolutionPath::Ident(IdentPath {
            inner: fragment_name,
            parent: IdentParent::FragmentSpreadName(_),
        }) => {
            if let Some(source_program) = source_programs.read().unwrap().get(&project_name) {
                let fragment = source_program
                    .fragment(fragment_name.value)
                    .ok_or_else(|| {
                        LSPRuntimeError::UnexpectedError(format!(
                            "Could not find fragment with name {}",
                            fragment_name
                        ))
                    })?;

                Ok(GotoDefinitionResponse::Scalar(
                    to_lsp_location_of_graphql_literal(fragment.name.location, root_dir)?,
                ))
            } else {
                Err(LSPRuntimeError::UnexpectedError(format!(
                    "Project name {} not found",
                    project_name
                )))
            }
        }
        ResolutionPath::Ident(IdentPath {
            inner: field_name,
            parent:
                IdentParent::LinkedFieldName(LinkedFieldPath {
                    inner: _,
                    parent: selection_path,
                }),
        }) => resolve_field(
            field_name.value.to_string(),
            selection_path.parent,
            project_name,
            source_programs,
            root_dir,
            extra_data_provider,
        ),
        ResolutionPath::Ident(IdentPath {
            inner: field_name,
            parent:
                IdentParent::ScalarFieldName(ScalarFieldPath {
                    inner: _,
                    parent: selection_path,
                }),
        }) => resolve_field(
            field_name.value.to_string(),
            selection_path.parent,
            project_name,
            source_programs,
            root_dir,
            extra_data_provider,
        ),
        ResolutionPath::Ident(IdentPath {
            inner: _,
            parent:
                IdentParent::TypeConditionType(TypeConditionPath {
                    inner: type_condition,
                    parent: _,
                }),
        }) => {
            let provider_response = extra_data_provider
                .resolve_field_definition(
                    project_name.to_string(),
                    root_dir,
                    type_condition.type_.value.to_string(),
                    None,
                )
                .ok_or(LSPRuntimeError::ExpectedError)?;
            let (path, line) = provider_response.map_err(|e| -> LSPRuntimeError {
                LSPRuntimeError::UnexpectedError(format!(
                    "Error resolving field definition location: {}",
                    e
                ))
            })?;
            Ok(GotoDefinitionResponse::Scalar(get_location(&path, line)?))
        }
        _ => Err(LSPRuntimeError::ExpectedError),
    }
}

fn resolve_field<'a>(
    field_name: String,
    selection_parent: SelectionParent<'a>,
    project_name: StringKey,
    source_programs: &Arc<RwLock<HashMap<StringKey, Program>>>,
    root_dir: &PathBuf,
    // https://github.com/rust-lang/rust-clippy/issues/3971
    #[allow(clippy::borrowed_box)] extra_data_provider: &Box<dyn LSPExtraDataProvider + 'static>,
) -> LSPRuntimeResult<GotoDefinitionResponse> {
    let programs = source_programs.read().unwrap();
    let source_program = programs.get(&project_name).ok_or_else(|| {
        LSPRuntimeError::UnexpectedError(format!("Project name {} not found", project_name))
    })?;

    let parent_type = find_selection_parent_type(selection_parent, &source_program.schema)
        .ok_or(LSPRuntimeError::ExpectedError)?;

    let parent_name = source_program.schema.get_type_name(parent_type);

    let provider_response = extra_data_provider
        .resolve_field_definition(
            project_name.to_string(),
            root_dir,
            parent_name.to_string(),
            Some(field_name),
        )
        .ok_or(LSPRuntimeError::ExpectedError)?;
    let (path, line) = provider_response.map_err(|e| -> LSPRuntimeError {
        LSPRuntimeError::UnexpectedError(format!(
            "Error resolving field definition location: {}",
            e
        ))
    })?;
    Ok(GotoDefinitionResponse::Scalar(get_location(&path, line)?))
}

pub(crate) fn on_goto_definition<TPerfLogger: PerfLogger + 'static>(
    state: &mut LSPState<TPerfLogger>,
    params: <GotoDefinition as Request>::Params,
) -> LSPRuntimeResult<<GotoDefinition as Request>::Result> {
    let (document, position_span, project_name) =
        state.extract_executable_document_from_text(params, 1)?;
    let path = document.resolve((), position_span);

    let goto_definition_response = get_goto_definition_response(
        path,
        project_name,
        state.get_source_programs_ref(),
        state.root_dir(),
        &state.extra_data_provider,
    )?;

    Ok(Some(goto_definition_response))
}

fn get_location(path: &str, line: u64) -> Result<lsp_types::Location, LSPRuntimeError> {
    let start = lsp_types::Position { line, character: 0 };
    let range = lsp_types::Range { start, end: start };

    let uri = Url::parse(&format!("file://{}", path)).map_err(|e| {
        LSPRuntimeError::UnexpectedError(format!("Could not parse path as URL: {}", e))
    })?;

    Ok(lsp_types::Location { uri, range })
}
<|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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import Method %>

// Non-standard properties that Gecko uses for XUL elements.
<% data.new_style_struct("XUL", inherited=False) %>

${helpers.single_keyword("-moz-box-align", "stretch start center baseline end",
                         products="gecko", gecko_ffi_name="mBoxAlign",
                         gecko_enum_prefix="StyleBoxAlign",
                         gecko_inexhaustive=True,
                         animation_value_type="discrete",
                         alias="-webkit-box-align",
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-align)")}

${helpers.single_keyword("-moz-box-direction", "normal reverse",
                         products="gecko", gecko_ffi_name="mBoxDirection",
                         gecko_enum_prefix="StyleBoxDirection",
                         gecko_inexhaustive=True,
                         animation_value_type="discrete",
                         alias="-webkit-box-direction",
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-direction)")}

${helpers.predefined_type("-moz-box-flex", "Number", "0.0", "parse_non_negative",
                          products="gecko", gecko_ffi_name="mBoxFlex",
                          animation_value_type="none",
                          alias="-webkit-box-flex",
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-flex)")}

${helpers.single_keyword("-moz-box-orient", "horizontal vertical",
                         products="gecko", gecko_ffi_name="mBoxOrient",
                         extra_gecko_aliases="inline-axis=horizontal block-axis=vertical",
                         gecko_enum_prefix="StyleBoxOrient",
                         gecko_inexhaustive=True,
                         animation_value_type="discrete",
                         alias="-webkit-box-orient",
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-orient)")}

${helpers.single_keyword("-moz-box-pack", "start center end justify",
                         products="gecko", gecko_ffi_name="mBoxPack",
                         gecko_enum_prefix="StyleBoxPack",
                         gecko_inexhaustive=True,
                         animation_value_type="discrete",
                         alias="-webkit-box-pack",
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-pack)")}

${helpers.single_keyword("-moz-stack-sizing", "stretch-to-fit ignore ignore-horizontal ignore-vertical",
                         products="gecko", gecko_ffi_name="mStackSizing",
                         gecko_enum_prefix="StyleStackSizing",
                         gecko_inexhaustive=True,
                         animation_value_type="discrete",
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-stack-sizing)")}

${helpers.predefined_type("-moz-box-ordinal-group", "Integer", "0",
                          parse_method="parse_non_negative",
                          products="gecko",
                          alias="-webkit-box-ordinal-group",
                          gecko_ffi_name="mBoxOrdinal",
                          animation_value_type="none",
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-box-ordinal-group)")}
<commit_msg>Make -moz-box-flex animatable.<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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import Method %>

// Non-standard properties that Gecko uses for XUL elements.
<% data.new_style_struct("XUL", inherited=False) %>

${helpers.single_keyword("-moz-box-align", "stretch start center baseline end",
                         products="gecko", gecko_ffi_name="mBoxAlign",
                         gecko_enum_prefix="StyleBoxAlign",
                         gecko_inexhaustive=True,
                         animation_value_type="discrete",
                         alias="-webkit-box-align",
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-align)")}

${helpers.single_keyword("-moz-box-direction", "normal reverse",
                         products="gecko", gecko_ffi_name="mBoxDirection",
                         gecko_enum_prefix="StyleBoxDirection",
                         gecko_inexhaustive=True,
                         animation_value_type="discrete",
                         alias="-webkit-box-direction",
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-direction)")}

${helpers.predefined_type("-moz-box-flex", "Number", "0.0", "parse_non_negative",
                          products="gecko", gecko_ffi_name="mBoxFlex",
                          animation_value_type="ComputedValue",
                          alias="-webkit-box-flex",
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-flex)")}

${helpers.single_keyword("-moz-box-orient", "horizontal vertical",
                         products="gecko", gecko_ffi_name="mBoxOrient",
                         extra_gecko_aliases="inline-axis=horizontal block-axis=vertical",
                         gecko_enum_prefix="StyleBoxOrient",
                         gecko_inexhaustive=True,
                         animation_value_type="discrete",
                         alias="-webkit-box-orient",
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-orient)")}

${helpers.single_keyword("-moz-box-pack", "start center end justify",
                         products="gecko", gecko_ffi_name="mBoxPack",
                         gecko_enum_prefix="StyleBoxPack",
                         gecko_inexhaustive=True,
                         animation_value_type="discrete",
                         alias="-webkit-box-pack",
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-pack)")}

${helpers.single_keyword("-moz-stack-sizing", "stretch-to-fit ignore ignore-horizontal ignore-vertical",
                         products="gecko", gecko_ffi_name="mStackSizing",
                         gecko_enum_prefix="StyleStackSizing",
                         gecko_inexhaustive=True,
                         animation_value_type="discrete",
                         spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-stack-sizing)")}

${helpers.predefined_type("-moz-box-ordinal-group", "Integer", "0",
                          parse_method="parse_non_negative",
                          products="gecko",
                          alias="-webkit-box-ordinal-group",
                          gecko_ffi_name="mBoxOrdinal",
                          animation_value_type="none",
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-box-ordinal-group)")}
<|endoftext|>


<commit_before>use master_smasher::drawable::{Asset, Drawable};
use super::MeteorState;
use super::launched_meteor::LaunchedMeteor;

use glm;
use glm::ext::normalize_to;
use sdl2::rect;

pub struct UnlaunchedMeteor {
    asset: Asset,
    target: glm::IVec2,
}

impl UnlaunchedMeteor {
    pub fn new(asset: Asset) -> Self {
        UnlaunchedMeteor {
            asset: asset,
            target: glm::ivec2(0, 0),
        }
    }

    pub fn update(&mut self, target: glm::IVec2) {
        self.target = target;
    }

    pub fn drawables(&self) -> Vec<Drawable> {
        let target = glm::to_dvec2(self.target);
        let center = glm::to_dvec2(self.asset.center());
        let distance = target - center;
        let offset = self.asset.dst_rect.z / 2 + 10;
        let offset_vector = normalize_to(distance, offset as f64);
        let anchor_point = center + offset_vector;
        let step = (target - anchor_point) / 10.;

        let rects = (0..10)
            .map(|i| anchor_point + (step * i as f64))
            .map(|p| (p.x as i32, p.y as i32))
            .map(|p| rect::Rect::from_center(p, 5, 5))
            .collect();

        vec![Drawable::Asset(self.asset), Drawable::Rectangles(rects)]
    }

    pub fn launch(&self, max_coords: glm::UVec2) -> MeteorState {
        const FACTOR: f64 = 50.;
        let offset = self.target - glm::to_ivec2(self.asset.center());
        let velocity = glm::to_dvec2(offset) / FACTOR;
        MeteorState::LAUNCHED(LaunchedMeteor::new(self.asset, max_coords, velocity))
    }
}
<commit_msg>two less magic number on unlaunched meteor<commit_after>use master_smasher::drawable::{Asset, Drawable};
use super::MeteorState;
use super::launched_meteor::LaunchedMeteor;

use glm;
use glm::ext::normalize_to;
use sdl2::rect;

pub struct UnlaunchedMeteor {
    asset: Asset,
    target: glm::IVec2,
}

impl UnlaunchedMeteor {
    pub fn new(asset: Asset) -> Self {
        UnlaunchedMeteor {
            asset: asset,
            target: glm::ivec2(0, 0),
        }
    }

    pub fn update(&mut self, target: glm::IVec2) {
        self.target = target;
    }

    pub fn drawables(&self) -> Vec<Drawable> {
        const NUM_RECTS: u32 = 10;
        const SIDE_LEN: u32 = 5;

        let target = glm::to_dvec2(self.target);
        let center = glm::to_dvec2(self.asset.center());
        let distance = target - center;
        let offset = self.asset.dst_rect.z / 2 + 10;
        let offset_vector = normalize_to(distance, offset as f64);
        let anchor_point = center + offset_vector;
        let step = (target - anchor_point) / NUM_RECTS as f64;

        let rects = (0..NUM_RECTS)
            .map(|i| anchor_point + (step * i as f64))
            .map(|p| (p.x as i32, p.y as i32))
            .map(|p| rect::Rect::from_center(p, SIDE_LEN, SIDE_LEN))
            .collect();

        vec![Drawable::Asset(self.asset), Drawable::Rectangles(rects)]
    }

    pub fn launch(&self, max_coords: glm::UVec2) -> MeteorState {
        const FACTOR: f64 = 50.;
        let offset = self.target - glm::to_ivec2(self.asset.center());
        let velocity = glm::to_dvec2(offset) / FACTOR;
        MeteorState::LAUNCHED(LaunchedMeteor::new(self.asset, max_coords, velocity))
    }
}
<|endoftext|>


<commit_before>///! fff-lang
///!
///! semantic/traits

use std::fmt;
use std::cell::RefCell;

use codemap::Span;
use codemap::SymbolID;
use codemap::SourceCode;
use codemap::SymbolCollection;

use message::Message;
use message::MessageCollection;

use super::Formatter;
use super::ScopeType;
use super::Definition;
use super::SharedDefScope;
use super::DefinitionCollection;

// public because it used in public API
// allowed because this name is designed to used not directly, only like `format!("{}", node.display())`
#[allow(dead_code)]
pub struct Wrapper<'a, T: 'a>(&'a T);
impl<'a, T: ISemanticAnalyze> fmt::Display for Wrapper<'a, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "\n{}", self.0.format(Formatter::empty()))
    }
}

/// Parameter for `ISyntaxAnalyze::from_syntax`
///
/// stores ref to symbols, to format namable scope like fn and type,
/// stores ref to source code, to format unnamable scope with span like for and if
pub struct FromSession<'a, 'b> {
    scope: SharedDefScope,
    source: &'a SourceCode,
    symbols: &'b SymbolCollection,
}
impl<'a, 'b> FromSession<'a, 'b> {

    pub fn new(scope: SharedDefScope, source: &'a SourceCode, symbols: &'b SymbolCollection) -> Self {
        FromSession{ scope, source, symbols }
    }
    pub fn clone_scope(&self) -> Self {
        FromSession{ scope: self.scope.clone(), source: self.source, symbols: self.symbols }
    }

    /// Create sub scope with id for name,
    /// May panic on invalid ID
    pub fn sub_with_symbol(&self, id: SymbolID, scope_type: ScopeType) -> Self {
        FromSession{ scope: self.scope.sub(self.symbols.get(id).unwrap(), scope_type), source: self.source, symbols: self.symbols }
    }
    /// Create sub scope with not unique name like 'if', 'for', etc.
    /// which need adding span information to make unique
    pub fn sub_with_span(&self, name: &str, span: Span, scope_type: ScopeType) -> Self {
        FromSession{ scope: self.scope.sub(format!("<{}{}>", name, span.format(Some(self.source))), scope_type), source: self.source, symbols: self.symbols }
    }

    pub fn into_scope(self) -> SharedDefScope { self.scope }
}

/// Parameter for `ISemanticAnalyze::collect_definitions`
pub struct CollectSession<'a, 'b> {
    node_path: Vec<usize>,
    defs: &'a mut DefinitionCollection,
    messages: &'b mut MessageCollection,
}
impl<'a, 'b> CollectSession<'a, 'b> {

    pub fn new(defs: &'a mut DefinitionCollection, messages: &'b mut MessageCollection) -> Self {
        CollectSession{ defs, messages, node_path: Vec::new() }
    }

    pub fn push_path(&mut self, child_id: usize) {
        self.node_path.push(child_id);
    }
    pub fn pop_path(&mut self) {
        let _ = self.node_path.pop();
    }

    pub fn push_def(&mut self, name: SymbolID, name_span: Span, mut scope: SharedDefScope) {
        match self.defs.push(Definition::new(name, self.node_path.clone())) {
            Some(defid) => scope.push_definition(defid),
            None => self.messages.push(Message::new_by_str("name already defined", vec![(name_span, "")])),
        }
    }
    #[allow(dead_code)] // TODO: temp remove
    pub fn push_message(&mut self, message: Message) {
        self.messages.push(message);
    }
}

pub trait ISemanticAnalyze {

    // Display, which is actually Debug but I don't like `fn debug() -> String`, should not be implemented
    fn display<'a>(&'a self) -> Wrapper<'a, Self> where Self: Sized { Wrapper(self) }
    // format, should be implemented
    fn format(&self, f: Formatter) -> String;

    // phase 1: convert syntax node and collect definitions
    type SyntaxItem;
    // fn convert_collect(item: Self::SyntaxItem, sess: ConvertSession) -> Self;

    fn from_syntax(item: Self::SyntaxItem, sess: FromSession) -> Self;

    // TODO: an empty implement for compatibility temporarily, remove it in future
    // currently, only nodes with `this_scope` can add definition, which contains child node id in the node's child node list
    fn collect_definitions(&self, sess: &mut CollectSession) { let _ = sess; }
}

/// Parameter for `ISemanticAnalyze::convert_from`
#[allow(dead_code)]
pub struct ConvertSession<'a, 'b> {
    scope: SharedDefScope,
    source: &'a SourceCode,
    symbols: &'b SymbolCollection,
    global_defs: RefCell<DefinitionCollection>,
    messages: RefCell<MessageCollection>,
}
impl<'a, 'b> ConvertSession<'a, 'b> {

    pub fn new(scope: SharedDefScope, source: &'a SourceCode, symbols: &'b SymbolCollection, global_defs: DefinitionCollection, messages: MessageCollection) -> Self {
        ConvertSession{ scope, source, symbols, global_defs: RefCell::new(global_defs), messages: RefCell::new(messages) }
    }
}

#[cfg(test)] #[test]
fn p1_usage() {

    trait ISemanticAnalyze2 {
        fn convert_from(sess: &ConvertSession) -> Self;
    }

    let syntax_tree = syntax::SyntaxTree::new_modules(vec![syntax::Module::new])

}<commit_msg>[semantic] fix compile error<commit_after>///! fff-lang
///!
///! semantic/traits

use std::fmt;
use std::cell::RefCell;

use codemap::Span;
use codemap::SymbolID;
use codemap::SourceCode;
use codemap::SymbolCollection;

use message::Message;
use message::MessageCollection;

use super::Formatter;
use super::ScopeType;
use super::Definition;
use super::SharedDefScope;
use super::DefinitionCollection;

// public because it used in public API
// allowed because this name is designed to used not directly, only like `format!("{}", node.display())`
#[allow(dead_code)]
pub struct Wrapper<'a, T: 'a>(&'a T);
impl<'a, T: ISemanticAnalyze> fmt::Display for Wrapper<'a, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "\n{}", self.0.format(Formatter::empty()))
    }
}

/// Parameter for `ISyntaxAnalyze::from_syntax`
///
/// stores ref to symbols, to format namable scope like fn and type,
/// stores ref to source code, to format unnamable scope with span like for and if
pub struct FromSession<'a, 'b> {
    scope: SharedDefScope,
    source: &'a SourceCode,
    symbols: &'b SymbolCollection,
}
impl<'a, 'b> FromSession<'a, 'b> {

    pub fn new(scope: SharedDefScope, source: &'a SourceCode, symbols: &'b SymbolCollection) -> Self {
        FromSession{ scope, source, symbols }
    }
    pub fn clone_scope(&self) -> Self {
        FromSession{ scope: self.scope.clone(), source: self.source, symbols: self.symbols }
    }

    /// Create sub scope with id for name,
    /// May panic on invalid ID
    pub fn sub_with_symbol(&self, id: SymbolID, scope_type: ScopeType) -> Self {
        FromSession{ scope: self.scope.sub(self.symbols.get(id).unwrap(), scope_type), source: self.source, symbols: self.symbols }
    }
    /// Create sub scope with not unique name like 'if', 'for', etc.
    /// which need adding span information to make unique
    pub fn sub_with_span(&self, name: &str, span: Span, scope_type: ScopeType) -> Self {
        FromSession{ scope: self.scope.sub(format!("<{}{}>", name, span.format(Some(self.source))), scope_type), source: self.source, symbols: self.symbols }
    }

    pub fn into_scope(self) -> SharedDefScope { self.scope }
}

/// Parameter for `ISemanticAnalyze::collect_definitions`
pub struct CollectSession<'a, 'b> {
    node_path: Vec<usize>,
    defs: &'a mut DefinitionCollection,
    messages: &'b mut MessageCollection,
}
impl<'a, 'b> CollectSession<'a, 'b> {

    pub fn new(defs: &'a mut DefinitionCollection, messages: &'b mut MessageCollection) -> Self {
        CollectSession{ defs, messages, node_path: Vec::new() }
    }

    pub fn push_path(&mut self, child_id: usize) {
        self.node_path.push(child_id);
    }
    pub fn pop_path(&mut self) {
        let _ = self.node_path.pop();
    }

    pub fn push_def(&mut self, name: SymbolID, name_span: Span, mut scope: SharedDefScope) {
        match self.defs.push(Definition::new(name, self.node_path.clone())) {
            Some(defid) => scope.push_definition(defid),
            None => self.messages.push(Message::new_by_str("name already defined", vec![(name_span, "")])),
        }
    }
    #[allow(dead_code)] // TODO: temp remove
    pub fn push_message(&mut self, message: Message) {
        self.messages.push(message);
    }
}

pub trait ISemanticAnalyze {

    // Display, which is actually Debug but I don't like `fn debug() -> String`, should not be implemented
    fn display<'a>(&'a self) -> Wrapper<'a, Self> where Self: Sized { Wrapper(self) }
    // format, should be implemented
    fn format(&self, f: Formatter) -> String;

    // phase 1: convert syntax node and collect definitions
    type SyntaxItem;
    // fn convert_collect(item: Self::SyntaxItem, sess: ConvertSession) -> Self;

    fn from_syntax(item: Self::SyntaxItem, sess: FromSession) -> Self;

    // TODO: an empty implement for compatibility temporarily, remove it in future
    // currently, only nodes with `this_scope` can add definition, which contains child node id in the node's child node list
    fn collect_definitions(&self, sess: &mut CollectSession) { let _ = sess; }
}

/// Parameter for `ISemanticAnalyze::convert_from`
#[allow(dead_code)]
pub struct ConvertSession<'a, 'b> {
    scope: SharedDefScope,
    source: &'a SourceCode,
    symbols: &'b SymbolCollection,
    global_defs: RefCell<DefinitionCollection>,
    messages: RefCell<MessageCollection>,
}
impl<'a, 'b> ConvertSession<'a, 'b> {

    pub fn new(scope: SharedDefScope, source: &'a SourceCode, symbols: &'b SymbolCollection, global_defs: DefinitionCollection, messages: MessageCollection) -> Self {
        ConvertSession{ scope, source, symbols, global_defs: RefCell::new(global_defs), messages: RefCell::new(messages) }
    }
}

#[cfg(test)] #[test]
fn p1_usage() {

    trait ISemanticAnalyze2 {
        fn convert_from(sess: &ConvertSession) -> Self;
    }

    // let syntax_tree = syntax::SyntaxTree::new_modules(vec![syntax::Module::new], vec![]);
}<|endoftext|>


<commit_before>use calx::dijkstra::Dijkstra;
use world;
use location::{Location};
use dir6::Dir6;
use flags;
use mob;
use mob::status;
use mob::intrinsic;
use super::MobKind;
use spatial;
use action;
use fov::Fov;

/// Game object handle.
#[deriving(PartialEq, Eq, Clone, Hash, Show, Decodable, Encodable)]
pub struct Entity(pub uint);

impl Entity {
    /// Place the entity in a location in the game world.
    pub fn place(self, loc: Location) {
        world::get().borrow_mut().spatial.insert_at(self, loc);
        self.on_move_to(loc);
    }

    /// Remove the entity from all the game world systems. THE ENTITY VALUE
    /// WILL BE INVALID FROM HERE ON AND USING IT WILL LEAD TO BUGS. THE
    /// CALLER IS RESPONSIBLE FOR ENSUING THAT AN ENTITY WILL NOT BE
    /// USED FROM ANYWHERE AFTER THE DELETE OPERATION.
    pub fn delete(self) {
        // LABYRINTH OF COMPONENTS
        // This needs to call every toplevel component system.
        world::get().borrow_mut().comp.remove(self);
        world::get().borrow_mut().spatial.remove(self);
        world::get().borrow_mut().ecs.delete(self);
    }

    pub fn blocks_walk(self) -> bool { self.is_mob() }

    /// Return the kind of the entity.
    pub fn kind(self) -> ::EntityKind {
        // XXX: Will crash if an entity has no kind specified.
        *world::get().borrow().comp.kind.get(self).unwrap()
    }

// Spatial methods /////////////////////////////////////////////////////

    pub fn can_enter(self, loc: Location) -> bool {
        if self.is_mob() && loc.has_mobs() { return false; }
        if loc.blocks_walk() { return false; }
        true
    }

    /// Return whether the entity can move in a direction.
    pub fn can_step(self, dir: Dir6) -> bool {
        let place = world::get().borrow().spatial.get(self);
        if let Some(spatial::At(loc)) = place {
            let new_loc = loc + dir.to_v2();
            return self.can_enter(new_loc);
        }
        return false;
    }

    /// Try to move the entity in direction.
    pub fn step(self, dir: Dir6) {
        let place = world::get().borrow().spatial.get(self);
        if let Some(spatial::At(loc)) = place {
            let new_loc = loc + dir.to_v2();
            if self.can_enter(new_loc) {
                world::get().borrow_mut().spatial.insert_at(self, new_loc);
                self.on_move_to(new_loc);
            }
        }
    }

    pub fn location(self) -> Option<Location> {
        match world::get().borrow().spatial.get(self) {
            Some(spatial::At(loc)) => Some(loc),
            Some(spatial::In(e)) => e.location(),
            _ => None
        }
    }

    pub fn distance_from(self, other: Entity) -> Option<int> {
        if let (Some(loc1), Some(loc2)) = (self.location(), other.location()) {
            loc1.distance_from(loc2)
        } else {
            None
        }
    }

// Mob methods /////////////////////////////////////////////////////////

    pub fn is_mob(self) -> bool {
        if let Some(&MobKind(_)) = world::get().borrow().comp.kind.get(self) {
            return true;
        }
        return false;
    }

    /// Return whether this mob is the player avatar.
    pub fn is_player(self) -> bool {
        if let Some(&MobKind(mob::Player)) = world::get().borrow().comp.kind.get(self) {
            return true;
        }
        return false;
    }

    pub fn has_status(self, status: status::Status) -> bool {
        if let Some(&mob) = world::get().borrow().comp.mob.get(self) {
            return mob.has_status(status);
        }
        return false;
    }

    pub fn add_status(self, status: status::Status) {
        if !self.is_mob() { return; }
        world::get().borrow_mut().comp.mob.get_mut(self).unwrap().add_status(status);
        assert!(self.has_status(status));
    }

    pub fn remove_status(self, status: status::Status) {
        if !self.is_mob() { return; }
        world::get().borrow_mut().comp.mob.get_mut(self).unwrap().remove_status(status);
        assert!(!self.has_status(status));
    }

    pub fn has_intrinsic(self, intrinsic: mob::intrinsic::Intrinsic) -> bool {
        if let Some(&MobKind(mt)) = world::get().borrow().comp.kind.get(self) {
            return mob::SPECS[mt as uint].intrinsics & intrinsic as int != 0;
        }
        return false;
    }

    /// Return whether this entity is an awake mob.
    pub fn is_active(self) -> bool {
        self.is_mob() && !self.has_status(status::Asleep)
    }

    /// Return if the entity is a mob that should get an update this frame
    /// based on its speed properties. Does not check for status effects like
    /// sleep that might prevent actual action.
    pub fn ticks_this_frame(self) -> bool {
        if !self.is_mob() { return false; }

        let tick = flags::get_tick();
        // Go through a cycle of 5 phases to get 4 possible speeds.
        // System idea from Jeff Lait.
        let phase = tick % 5;
        match phase {
            0 => return true,
            1 => return self.has_intrinsic(intrinsic::Fast),
            2 => return true,
            3 => return self.has_status(status::Quick),
            4 => return !self.has_intrinsic(intrinsic::Slow)
                        && !self.has_status(status::Slow),
            _ => panic!("Invalid action phase"),
        }
    }

    /// Return whether the entity is a mob that will act this frame.
    pub fn acts_this_frame(self) -> bool {
        if !self.is_active() { return false; }
        return self.ticks_this_frame();
    }

    /// Return whether the entity is an awake non-player mob and should be
    /// animated with a bob.
    pub fn is_bobbing(self) -> bool {
        self.is_active() && !self.is_player()
    }

// AI methods /////////////////////////////////////////////////////////

    /// Top-level method called each frame to update the entity.
    pub fn update(self) {
        if self.is_mob() && !self.is_player() && self.ticks_this_frame() {
            self.mob_ai();
        }
    }

    /// AI routine for autonomous mobs.
    fn mob_ai(self) {
        assert!(self.is_mob());
        assert!(!self.is_player());
        assert!(self.ticks_this_frame());

        if self.has_status(status::Asleep) {
            if let Some(p) = action::player() {
                // TODO: Line-of-sight, stealth concerns, other enemies than
                // player etc.
                if let Some(d) = p.distance_from(self) {
                    if d < 6 {
                        self.remove_status(status::Asleep);
                    }
                }
            }

            return;
        }

        if let Some(p) = action::player() {
            let pathing = Dijkstra::new(vec![p.location().unwrap()], |&loc| !loc.blocks_walk(), 64);
            let loc = self.location().unwrap();

            let steps = pathing.sorted_neighbors(&loc);
            if steps.len() > 0 {
                self.step(loc.dir6_towards(steps[0]).unwrap());
            } else {
                // TODO: Random dir step
            }
        }
    }

// Callbacks ///////////////////////////////////////////////////////////

    /// Called after the entity is moved to a new location.
    pub fn on_move_to(self, _loc: Location) {
        self.do_fov();
    }

// Misc ////////////////////////////////////////////////////////////////

    fn has_map_memory(self) -> bool {
        world::get().borrow().comp.map_memory.get(self).is_some()
    }

    fn do_fov(self) {
        let range = 12u;
        if let Some(loc) = self.location() {
            if self.has_map_memory() {
                let seen: Vec<Location> = Fov::new(
                    |pt| (loc + pt).blocks_sight(), range)
                    .map(|pt| loc + pt)
                    .collect();
                if let Some(ref mut mm) = world::get().borrow_mut().comp.map_memory.get_mut(self) {
                    mm.seen.clear();
                    mm.seen.extend(seen.clone().into_iter());
                    mm.remembered.extend(seen.into_iter());
                } else {
                    panic!("Couldn't bind map memory");
                }
            }
        }
    }
}
<commit_msg>Dial down AI pathfinding depth<commit_after>use calx::dijkstra::Dijkstra;
use world;
use location::{Location};
use dir6::Dir6;
use flags;
use mob;
use mob::status;
use mob::intrinsic;
use super::MobKind;
use spatial;
use action;
use fov::Fov;

/// Game object handle.
#[deriving(PartialEq, Eq, Clone, Hash, Show, Decodable, Encodable)]
pub struct Entity(pub uint);

impl Entity {
    /// Place the entity in a location in the game world.
    pub fn place(self, loc: Location) {
        world::get().borrow_mut().spatial.insert_at(self, loc);
        self.on_move_to(loc);
    }

    /// Remove the entity from all the game world systems. THE ENTITY VALUE
    /// WILL BE INVALID FROM HERE ON AND USING IT WILL LEAD TO BUGS. THE
    /// CALLER IS RESPONSIBLE FOR ENSUING THAT AN ENTITY WILL NOT BE
    /// USED FROM ANYWHERE AFTER THE DELETE OPERATION.
    pub fn delete(self) {
        // LABYRINTH OF COMPONENTS
        // This needs to call every toplevel component system.
        world::get().borrow_mut().comp.remove(self);
        world::get().borrow_mut().spatial.remove(self);
        world::get().borrow_mut().ecs.delete(self);
    }

    pub fn blocks_walk(self) -> bool { self.is_mob() }

    /// Return the kind of the entity.
    pub fn kind(self) -> ::EntityKind {
        // XXX: Will crash if an entity has no kind specified.
        *world::get().borrow().comp.kind.get(self).unwrap()
    }

// Spatial methods /////////////////////////////////////////////////////

    pub fn can_enter(self, loc: Location) -> bool {
        if self.is_mob() && loc.has_mobs() { return false; }
        if loc.blocks_walk() { return false; }
        true
    }

    /// Return whether the entity can move in a direction.
    pub fn can_step(self, dir: Dir6) -> bool {
        let place = world::get().borrow().spatial.get(self);
        if let Some(spatial::At(loc)) = place {
            let new_loc = loc + dir.to_v2();
            return self.can_enter(new_loc);
        }
        return false;
    }

    /// Try to move the entity in direction.
    pub fn step(self, dir: Dir6) {
        let place = world::get().borrow().spatial.get(self);
        if let Some(spatial::At(loc)) = place {
            let new_loc = loc + dir.to_v2();
            if self.can_enter(new_loc) {
                world::get().borrow_mut().spatial.insert_at(self, new_loc);
                self.on_move_to(new_loc);
            }
        }
    }

    pub fn location(self) -> Option<Location> {
        match world::get().borrow().spatial.get(self) {
            Some(spatial::At(loc)) => Some(loc),
            Some(spatial::In(e)) => e.location(),
            _ => None
        }
    }

    pub fn distance_from(self, other: Entity) -> Option<int> {
        if let (Some(loc1), Some(loc2)) = (self.location(), other.location()) {
            loc1.distance_from(loc2)
        } else {
            None
        }
    }

// Mob methods /////////////////////////////////////////////////////////

    pub fn is_mob(self) -> bool {
        if let Some(&MobKind(_)) = world::get().borrow().comp.kind.get(self) {
            return true;
        }
        return false;
    }

    /// Return whether this mob is the player avatar.
    pub fn is_player(self) -> bool {
        if let Some(&MobKind(mob::Player)) = world::get().borrow().comp.kind.get(self) {
            return true;
        }
        return false;
    }

    pub fn has_status(self, status: status::Status) -> bool {
        if let Some(&mob) = world::get().borrow().comp.mob.get(self) {
            return mob.has_status(status);
        }
        return false;
    }

    pub fn add_status(self, status: status::Status) {
        if !self.is_mob() { return; }
        world::get().borrow_mut().comp.mob.get_mut(self).unwrap().add_status(status);
        assert!(self.has_status(status));
    }

    pub fn remove_status(self, status: status::Status) {
        if !self.is_mob() { return; }
        world::get().borrow_mut().comp.mob.get_mut(self).unwrap().remove_status(status);
        assert!(!self.has_status(status));
    }

    pub fn has_intrinsic(self, intrinsic: mob::intrinsic::Intrinsic) -> bool {
        if let Some(&MobKind(mt)) = world::get().borrow().comp.kind.get(self) {
            return mob::SPECS[mt as uint].intrinsics & intrinsic as int != 0;
        }
        return false;
    }

    /// Return whether this entity is an awake mob.
    pub fn is_active(self) -> bool {
        self.is_mob() && !self.has_status(status::Asleep)
    }

    /// Return if the entity is a mob that should get an update this frame
    /// based on its speed properties. Does not check for status effects like
    /// sleep that might prevent actual action.
    pub fn ticks_this_frame(self) -> bool {
        if !self.is_mob() { return false; }

        let tick = flags::get_tick();
        // Go through a cycle of 5 phases to get 4 possible speeds.
        // System idea from Jeff Lait.
        let phase = tick % 5;
        match phase {
            0 => return true,
            1 => return self.has_intrinsic(intrinsic::Fast),
            2 => return true,
            3 => return self.has_status(status::Quick),
            4 => return !self.has_intrinsic(intrinsic::Slow)
                        && !self.has_status(status::Slow),
            _ => panic!("Invalid action phase"),
        }
    }

    /// Return whether the entity is a mob that will act this frame.
    pub fn acts_this_frame(self) -> bool {
        if !self.is_active() { return false; }
        return self.ticks_this_frame();
    }

    /// Return whether the entity is an awake non-player mob and should be
    /// animated with a bob.
    pub fn is_bobbing(self) -> bool {
        self.is_active() && !self.is_player()
    }

// AI methods /////////////////////////////////////////////////////////

    /// Top-level method called each frame to update the entity.
    pub fn update(self) {
        if self.is_mob() && !self.is_player() && self.ticks_this_frame() {
            self.mob_ai();
        }
    }

    /// AI routine for autonomous mobs.
    fn mob_ai(self) {
        assert!(self.is_mob());
        assert!(!self.is_player());
        assert!(self.ticks_this_frame());

        if self.has_status(status::Asleep) {
            if let Some(p) = action::player() {
                // TODO: Line-of-sight, stealth concerns, other enemies than
                // player etc.
                if let Some(d) = p.distance_from(self) {
                    if d < 6 {
                        self.remove_status(status::Asleep);
                    }
                }
            }

            return;
        }

        if let Some(p) = action::player() {
            let pathing = Dijkstra::new(vec![p.location().unwrap()], |&loc| !loc.blocks_walk(), 16);
            let loc = self.location().unwrap();

            let steps = pathing.sorted_neighbors(&loc);
            if steps.len() > 0 {
                self.step(loc.dir6_towards(steps[0]).unwrap());
            } else {
                // TODO: Random dir step
            }
        }
    }

// Callbacks ///////////////////////////////////////////////////////////

    /// Called after the entity is moved to a new location.
    pub fn on_move_to(self, _loc: Location) {
        self.do_fov();
    }

// Misc ////////////////////////////////////////////////////////////////

    fn has_map_memory(self) -> bool {
        world::get().borrow().comp.map_memory.get(self).is_some()
    }

    fn do_fov(self) {
        let range = 12u;
        if let Some(loc) = self.location() {
            if self.has_map_memory() {
                let seen: Vec<Location> = Fov::new(
                    |pt| (loc + pt).blocks_sight(), range)
                    .map(|pt| loc + pt)
                    .collect();
                if let Some(ref mut mm) = world::get().borrow_mut().comp.map_memory.get_mut(self) {
                    mm.seen.clear();
                    mm.seen.extend(seen.clone().into_iter());
                    mm.remembered.extend(seen.into_iter());
                } else {
                    panic!("Couldn't bind map memory");
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use types::*;

use cookie_factory::*;

pub fn gen_value<'a>(x: (&'a mut [u8], usize), v: &AMQPValue) -> Result<(&'a mut [u8], usize), GenError> {
    match *v {
        AMQPValue::Boolean(ref b)        => do_gen!(x, gen_type(&v.get_type()) >> gen_boolean(b)),
        AMQPValue::ShortShortInt(ref i)  => do_gen!(x, gen_type(&v.get_type()) >> gen_short_short_int(i)),
        AMQPValue::ShortShortUInt(ref u) => do_gen!(x, gen_type(&v.get_type()) >> gen_short_short_uint(u)),
        AMQPValue::ShortInt(ref i)       => do_gen!(x, gen_type(&v.get_type()) >> gen_short_int(i)),
        AMQPValue::ShortUInt(ref u)      => do_gen!(x, gen_type(&v.get_type()) >> gen_short_uint(u)),
        AMQPValue::LongInt(ref i)        => do_gen!(x, gen_type(&v.get_type()) >> gen_long_int(i)),
        AMQPValue::LongUInt(ref u)       => do_gen!(x, gen_type(&v.get_type()) >> gen_long_uint(u)),
        AMQPValue::LongLongInt(ref i)    => do_gen!(x, gen_type(&v.get_type()) >> gen_long_long_int(i)),
        AMQPValue::LongLongUInt(ref u)   => do_gen!(x, gen_type(&v.get_type()) >> gen_long_long_uint(u)),
        AMQPValue::Float(ref f)          => do_gen!(x, gen_type(&v.get_type()) >> gen_float(f)),
        AMQPValue::Double(ref d)         => do_gen!(x, gen_type(&v.get_type()) >> gen_double(d)),
        AMQPValue::DecimalValue(ref d)   => do_gen!(x, gen_type(&v.get_type()) >> gen_decimal_value(d)),
        AMQPValue::ShortString(ref s)    => do_gen!(x, gen_type(&v.get_type()) >> gen_short_string(s)),
        AMQPValue::LongString(ref s)     => do_gen!(x, gen_type(&v.get_type()) >> gen_long_string(s)),
        AMQPValue::FieldArray(ref a)     => do_gen!(x, gen_type(&v.get_type()) >> gen_field_array(a)),
        AMQPValue::Timestamp(ref t)      => do_gen!(x, gen_type(&v.get_type()) >> gen_timestamp(t)),
        AMQPValue::FieldTable(ref t)     => do_gen!(x, gen_type(&v.get_type()) >> gen_field_table(t)),
        AMQPValue::Void                  => gen_type(x, &v.get_type()),
    }
}

pub fn gen_type<'a>(x: (&'a mut [u8], usize), t: &AMQPType) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_u8!(x, t.get_id() as u8)
}

pub fn gen_boolean<'a>(x: (&'a mut [u8], usize), b: &Boolean) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_u8!(x, if *b { 1 } else { 0 })
}

pub fn gen_short_short_int<'a>(x: (&'a mut [u8], usize), i: &ShortShortInt) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_i8!(x, *i)
}

pub fn gen_short_short_uint<'a>(x: (&'a mut [u8], usize), u: &ShortShortUInt) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_u8!(x, *u)
}

pub fn gen_short_int<'a>(x: (&'a mut [u8], usize), i: &ShortInt) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_i16!(x, *i)
}

pub fn gen_short_uint<'a>(x: (&'a mut [u8], usize), u: &ShortUInt) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_u16!(x, *u)
}

pub fn gen_long_int<'a>(x: (&'a mut [u8], usize), i: &LongInt) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_i32!(x, *i)
}

pub fn gen_long_uint<'a>(x: (&'a mut [u8], usize), u: &LongUInt) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_u32!(x, *u)
}

pub fn gen_long_long_int<'a>(x: (&'a mut [u8], usize), i: &LongLongInt) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_i64!(x, *i)
}

pub fn gen_long_long_uint<'a>(x: (&'a mut [u8], usize), u: &LongLongUInt) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_u64!(x, *u)
}

pub fn gen_float<'a>(x: (&'a mut [u8], usize), f: &Float) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_f32!(x, *f)
}

pub fn gen_double<'a>(x: (&'a mut [u8], usize), d: &Double) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_f64!(x, *d)
}

pub fn gen_decimal_value<'a>(x: (&'a mut [u8], usize), d: &DecimalValue) -> Result<(&'a mut [u8], usize), GenError> {
    do_gen!(x, gen_short_short_uint(&d.scale) >> gen_long_uint(&d.value))
}

pub fn gen_short_string<'a>(x: (&'a mut [u8], usize), s: &ShortString) -> Result<(&'a mut [u8], usize), GenError> {
    do_gen!(x, gen_short_short_uint(&(s.len() as ShortShortUInt)) >> gen_slice!(s.as_bytes()))
}

pub fn gen_long_string<'a>(x: (&'a mut [u8], usize), s: &LongString) -> Result<(&'a mut [u8], usize), GenError> {
    do_gen!(x, gen_long_uint(&(s.len() as LongUInt)) >> gen_slice!(s.as_bytes()))
}

pub fn gen_field_array<'a>(x: (&'a mut [u8], usize), a: &FieldArray) -> Result<(&'a mut [u8], usize), GenError> {
    let (x1, index1) = x;
    gen_many_ref!((x1, index1 + 4), a, gen_value).and_then(|(x2, index2)| {
        gen_long_uint((x2, index1), &((index2 - index1 - 4) as LongUInt)).and_then(|(x3, _)| Ok((x3, index2)))
    })
}

pub fn gen_timestamp<'a>(x: (&'a mut [u8], usize), t: &Timestamp) -> Result<(&'a mut [u8], usize), GenError> {
    gen_long_long_uint(x, t)
}

pub fn gen_field_table<'a>(x: (&'a mut [u8], usize), t: &FieldTable) -> Result<(&'a mut [u8], usize), GenError> {
    let (x1, index1) = x;
    gen_many_ref!((x1, index1 + 4), t, gen_field_entry).and_then(|(x2, index2)| {
        gen_long_uint((x2, index1), &((index2 - index1 - 4) as LongUInt)).and_then(|(x3, _)| Ok((x3, index2)))
    })
}

pub fn gen_field_entry<'a>(x: (&'a mut [u8], usize), e: &(&ShortString, &AMQPValue)) -> Result<(&'a mut [u8], usize), GenError> {
    do_gen!(x, gen_short_string(e.0) >> gen_value(e.1))
}
<commit_msg>types: add basic generation tests<commit_after>use types::*;

use cookie_factory::*;

pub fn gen_value<'a>(x: (&'a mut [u8], usize), v: &AMQPValue) -> Result<(&'a mut [u8], usize), GenError> {
    match *v {
        AMQPValue::Boolean(ref b)        => do_gen!(x, gen_type(&v.get_type()) >> gen_boolean(b)),
        AMQPValue::ShortShortInt(ref i)  => do_gen!(x, gen_type(&v.get_type()) >> gen_short_short_int(i)),
        AMQPValue::ShortShortUInt(ref u) => do_gen!(x, gen_type(&v.get_type()) >> gen_short_short_uint(u)),
        AMQPValue::ShortInt(ref i)       => do_gen!(x, gen_type(&v.get_type()) >> gen_short_int(i)),
        AMQPValue::ShortUInt(ref u)      => do_gen!(x, gen_type(&v.get_type()) >> gen_short_uint(u)),
        AMQPValue::LongInt(ref i)        => do_gen!(x, gen_type(&v.get_type()) >> gen_long_int(i)),
        AMQPValue::LongUInt(ref u)       => do_gen!(x, gen_type(&v.get_type()) >> gen_long_uint(u)),
        AMQPValue::LongLongInt(ref i)    => do_gen!(x, gen_type(&v.get_type()) >> gen_long_long_int(i)),
        AMQPValue::LongLongUInt(ref u)   => do_gen!(x, gen_type(&v.get_type()) >> gen_long_long_uint(u)),
        AMQPValue::Float(ref f)          => do_gen!(x, gen_type(&v.get_type()) >> gen_float(f)),
        AMQPValue::Double(ref d)         => do_gen!(x, gen_type(&v.get_type()) >> gen_double(d)),
        AMQPValue::DecimalValue(ref d)   => do_gen!(x, gen_type(&v.get_type()) >> gen_decimal_value(d)),
        AMQPValue::ShortString(ref s)    => do_gen!(x, gen_type(&v.get_type()) >> gen_short_string(s)),
        AMQPValue::LongString(ref s)     => do_gen!(x, gen_type(&v.get_type()) >> gen_long_string(s)),
        AMQPValue::FieldArray(ref a)     => do_gen!(x, gen_type(&v.get_type()) >> gen_field_array(a)),
        AMQPValue::Timestamp(ref t)      => do_gen!(x, gen_type(&v.get_type()) >> gen_timestamp(t)),
        AMQPValue::FieldTable(ref t)     => do_gen!(x, gen_type(&v.get_type()) >> gen_field_table(t)),
        AMQPValue::Void                  => gen_type(x, &v.get_type()),
    }
}

pub fn gen_type<'a>(x: (&'a mut [u8], usize), t: &AMQPType) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_u8!(x, t.get_id() as u8)
}

pub fn gen_boolean<'a>(x: (&'a mut [u8], usize), b: &Boolean) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_u8!(x, if *b { 1 } else { 0 })
}

pub fn gen_short_short_int<'a>(x: (&'a mut [u8], usize), i: &ShortShortInt) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_i8!(x, *i)
}

pub fn gen_short_short_uint<'a>(x: (&'a mut [u8], usize), u: &ShortShortUInt) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_u8!(x, *u)
}

pub fn gen_short_int<'a>(x: (&'a mut [u8], usize), i: &ShortInt) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_i16!(x, *i)
}

pub fn gen_short_uint<'a>(x: (&'a mut [u8], usize), u: &ShortUInt) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_u16!(x, *u)
}

pub fn gen_long_int<'a>(x: (&'a mut [u8], usize), i: &LongInt) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_i32!(x, *i)
}

pub fn gen_long_uint<'a>(x: (&'a mut [u8], usize), u: &LongUInt) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_u32!(x, *u)
}

pub fn gen_long_long_int<'a>(x: (&'a mut [u8], usize), i: &LongLongInt) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_i64!(x, *i)
}

pub fn gen_long_long_uint<'a>(x: (&'a mut [u8], usize), u: &LongLongUInt) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_u64!(x, *u)
}

pub fn gen_float<'a>(x: (&'a mut [u8], usize), f: &Float) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_f32!(x, *f)
}

pub fn gen_double<'a>(x: (&'a mut [u8], usize), d: &Double) -> Result<(&'a mut [u8], usize), GenError> {
    gen_be_f64!(x, *d)
}

pub fn gen_decimal_value<'a>(x: (&'a mut [u8], usize), d: &DecimalValue) -> Result<(&'a mut [u8], usize), GenError> {
    do_gen!(x, gen_short_short_uint(&d.scale) >> gen_long_uint(&d.value))
}

pub fn gen_short_string<'a>(x: (&'a mut [u8], usize), s: &ShortString) -> Result<(&'a mut [u8], usize), GenError> {
    do_gen!(x, gen_short_short_uint(&(s.len() as ShortShortUInt)) >> gen_slice!(s.as_bytes()))
}

pub fn gen_long_string<'a>(x: (&'a mut [u8], usize), s: &LongString) -> Result<(&'a mut [u8], usize), GenError> {
    do_gen!(x, gen_long_uint(&(s.len() as LongUInt)) >> gen_slice!(s.as_bytes()))
}

pub fn gen_field_array<'a>(x: (&'a mut [u8], usize), a: &FieldArray) -> Result<(&'a mut [u8], usize), GenError> {
    let (x1, index1) = x;
    gen_many_ref!((x1, index1 + 4), a, gen_value).and_then(|(x2, index2)| {
        gen_long_uint((x2, index1), &((index2 - index1 - 4) as LongUInt)).and_then(|(x3, _)| Ok((x3, index2)))
    })
}

pub fn gen_timestamp<'a>(x: (&'a mut [u8], usize), t: &Timestamp) -> Result<(&'a mut [u8], usize), GenError> {
    gen_long_long_uint(x, t)
}

pub fn gen_field_table<'a>(x: (&'a mut [u8], usize), t: &FieldTable) -> Result<(&'a mut [u8], usize), GenError> {
    let (x1, index1) = x;
    gen_many_ref!((x1, index1 + 4), t, gen_field_entry).and_then(|(x2, index2)| {
        gen_long_uint((x2, index1), &((index2 - index1 - 4) as LongUInt)).and_then(|(x3, _)| Ok((x3, index2)))
    })
}

pub fn gen_field_entry<'a>(x: (&'a mut [u8], usize), e: &(&ShortString, &AMQPValue)) -> Result<(&'a mut [u8], usize), GenError> {
    do_gen!(x, gen_short_string(e.0) >> gen_value(e.1))
}

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

    #[test]
    fn test_gen_type() {
        assert_eq!(gen_type((&mut [0], 0), &AMQPType::ShortShortInt).unwrap(), (&mut [98][..], 1));
        assert_eq!(gen_type((&mut [0], 0), &AMQPType::ShortInt).unwrap(),      (&mut [85][..], 1));
    }

    #[test]
    fn test_gen_boolean() {
        assert_eq!(gen_boolean((&mut [0], 0), &false).unwrap(), (&mut [0][..], 1));
        assert_eq!(gen_boolean((&mut [0], 0), &true).unwrap(),  (&mut [1][..], 1));
    }

    #[test]
    fn test_gen_short_short_int() {
        assert_eq!(gen_short_short_int((&mut [0], 0), &0).unwrap(),  (&mut [0][..],   1));
        assert_eq!(gen_short_short_int((&mut [0], 0), &-1).unwrap(), (&mut [255][..], 1));
    }

    #[test]
    fn test_gen_short_short_uint() {
        assert_eq!(gen_short_short_uint((&mut [0], 0), &0).unwrap(),   (&mut [0][..],   1));
        assert_eq!(gen_short_short_uint((&mut [0], 0), &255).unwrap(), (&mut [255][..], 1));
    }

    #[test]
    fn test_gen_short_int() {
        assert_eq!(gen_short_int((&mut [0, 0], 0), &0).unwrap(),  (&mut [0,   0][..],   2));
        assert_eq!(gen_short_int((&mut [0, 0], 0), &-1).unwrap(), (&mut [255, 255][..], 2));
    }

    #[test]
    fn test_gen_short_uint() {
        assert_eq!(gen_short_uint((&mut [0, 0], 0), &0).unwrap(),     (&mut [0,   0][..],   2));
        assert_eq!(gen_short_uint((&mut [0, 0], 0), &65535).unwrap(), (&mut [255, 255][..], 2));
    }

    #[test]
    fn test_gen_long_int() {
        assert_eq!(gen_long_int((&mut [0, 0, 0, 0], 0), &0).unwrap(),  (&mut [0,   0,   0,   0][..],   4));
        assert_eq!(gen_long_int((&mut [0, 0, 0, 0], 0), &-1).unwrap(), (&mut [255, 255, 255, 255][..], 4));
    }

    #[test]
    fn test_gen_long_uint() {
        assert_eq!(gen_long_uint((&mut [0, 0, 0, 0], 0), &0).unwrap(),          (&mut [0,   0,   0,   0][..],   4));
        assert_eq!(gen_long_uint((&mut [0, 0, 0, 0], 0), &4294967295).unwrap(), (&mut [255, 255, 255, 255][..], 4));
    }

    #[test]
    fn test_gen_long_long_int() {
        assert_eq!(gen_long_long_int((&mut [0, 0, 0, 0, 0, 0, 0, 0], 0), &0).unwrap(),  (&mut [0,   0,   0,   0,   0,   0,   0,   0][..],   8));
        assert_eq!(gen_long_long_int((&mut [0, 0, 0, 0, 0, 0, 0, 0], 0), &-1).unwrap(), (&mut [255, 255, 255, 255, 255, 255, 255, 255][..], 8));
    }

    #[test]
    fn test_gen_long_long_uint() {
        assert_eq!(gen_long_long_uint((&mut [0, 0, 0, 0, 0, 0, 0, 0], 0), &0).unwrap(),                    (&mut [0,   0,   0,   0,   0,   0,   0,   0][..],   8));
        assert_eq!(gen_long_long_uint((&mut [0, 0, 0, 0, 0, 0, 0, 0], 0), &18446744073709551615).unwrap(), (&mut [255, 255, 255, 255, 255, 255, 255, 255][..], 8));
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::ops::{Deref, DerefMut};
use std::io::BufRead;
use std::result::Result as RResult;

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>, String, 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, line, uuid))
            })
    }

    /// Get a task from an import string. That is: read the imported string, get the UUID from it
    /// and try to load this UUID from store.
    ///
    /// Possible return values are:
    ///
    /// * Ok(Ok(Task))
    /// * Ok(Err(String)) - where the String is the String read from the `r` parameter
    /// * Err(_)          - where the error is an error that happened during evaluation
    ///
    pub fn get_from_import<R: BufRead>(store: &'a Store, mut r: R) -> Result<RResult<Task<'a>, String>>
    {
        let mut line = String::new();
        r.read_line(&mut line);
        Task::get_from_string(store, line)
    }

    /// Get a task from a String. The String is expected to contain the JSON-representation of the
    /// Task to get from the store (only the UUID really matters in this case)
    ///
    /// For an explanation on the return values see `Task::get_from_import()`.
    pub fn get_from_string(store: &'a Store, s: String) -> Result<RResult<Task<'a>, String>> {
        import_task(s.as_str())
            .map_err_into(TodoErrorKind::ImportError)
            .map(|t| t.uuid().clone())
            .and_then(|uuid| Task::get_from_uuid(store, uuid))
            .and_then(|o| match o {
                None    => Ok(Err(s)),
                Some(t) => Ok(Ok(t)),
            })
    }

    /// Get a task from an UUID.
    ///
    /// If there is no task with this UUID, this returns `Ok(None)`.
    pub fn get_from_uuid(store: &'a Store, uuid: Uuid) -> Result<Option<Task<'a>>> {
        ModuleEntryPath::new(format!("taskwarrior/{}", uuid))
            .into_storeid()
            .and_then(|store_id| store.get(store_id))
            .map(|o| o.map(Task::new))
            .map_err_into(TodoErrorKind::StoreError)
    }

    /// Same as Task::get_from_import() but uses Store::retrieve() rather than Store::get(), to
    /// implicitely create the task if it does not exist.
    pub fn retrieve_from_import<R: BufRead>(store: &'a Store, mut r: R) -> Result<Task<'a>> {
        let mut line = String::new();
        r.read_line(&mut line);
        Task::retrieve_from_string(store, line)
    }

    /// Retrieve a task from a String. The String is expected to contain the JSON-representation of
    /// the Task to retrieve from the store (only the UUID really matters in this case)
    pub fn retrieve_from_string(store: &'a Store, s: String) -> Result<Task<'a>> {
        Task::get_from_string(store, s)
            .and_then(|opt| match opt {
                Ok(task)    => Ok(task),
                Err(string) => import_task(string.as_str())
                    .map_err_into(TodoErrorKind::ImportError)
                    .and_then(|t| t.into_task(store)),
            })
    }

    pub fn delete_by_imports<R: BufRead>(store: &Store, r: R) -> Result<()> {
        use serde_json::ser::to_string as serde_to_string;
        use task_hookrs::status::TaskStatus;

        for (counter, res_ttask) in import_tasks(r).into_iter().enumerate() {
            match res_ttask {
                Ok(ttask) => {
                    if counter % 2 == 1 {
                        // Only every second task is needed, the first one is the
                        // task before the change, and the second one after
                        // the change. The (maybe modified) second one is
                        // expected by taskwarrior.
                        match serde_to_string(&ttask).map_err_into(TodoErrorKind::ImportError) {
                            // use println!() here, as we talk with TW
                            Ok(val) => println!("{}", val),
                            Err(e)  => return Err(e),
                        }

                        // Taskwarrior does not have the concept of deleted tasks, but only modified
                        // ones.
                        //
                        // Here we check if the status of a task is deleted and if yes, we delete it
                        // from the store.
                        if *ttask.status() == TaskStatus::Deleted {
                            match Task::delete_by_uuid(store, *ttask.uuid()) {
                                Ok(_)  => info!("Deleted task {}", *ttask.uuid()),
                                Err(e) => return Err(e),
                            }
                        }
                    } // end if c % 2
                },
                Err(e) => return Err(e).map_err_into(TodoErrorKind::ImportError),
            }
        }
        Ok(())
    }

    pub fn delete_by_uuid(store: &Store, uuid: Uuid) -> Result<()> {
        ModuleEntryPath::new(format!("taskwarrior/{}", uuid))
            .into_storeid()
            .and_then(|id| store.delete(id))
            .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.
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();
        ModuleEntryPath::new(format!("taskwarrior/{}", uuid))
            .into_storeid()
            .map_err_into(TodoErrorKind::StoreIdError)
            .and_then(|id| {
                store.retrieve(id)
                    .map_err_into(TodoErrorKind::StoreError)
                    .and_then(|mut fle| {
                        {
                            let mut hdr = fle.get_header_mut();
                            if try!(hdr.read("todo").map_err_into(TodoErrorKind::StoreError))
                                .is_none()
                            {
                                try!(hdr
                                    .set("todo", Value::Table(BTreeMap::new()))
                                    .map_err_into(TodoErrorKind::StoreError));
                            }

                            try!(hdr.set("todo.uuid", Value::String(format!("{}",uuid)))
                                 .map_err_into(TodoErrorKind::StoreError));
                        }

                        // 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>Refactor long line for more readability<commit_after>use std::collections::BTreeMap;
use std::ops::{Deref, DerefMut};
use std::io::BufRead;
use std::result::Result as RResult;

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>, String, 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, line, uuid))
            })
    }

    /// Get a task from an import string. That is: read the imported string, get the UUID from it
    /// and try to load this UUID from store.
    ///
    /// Possible return values are:
    ///
    /// * Ok(Ok(Task))
    /// * Ok(Err(String)) - where the String is the String read from the `r` parameter
    /// * Err(_)          - where the error is an error that happened during evaluation
    ///
    pub fn get_from_import<R: BufRead>(store: &'a Store, mut r: R) -> Result<RResult<Task<'a>, String>>
    {
        let mut line = String::new();
        r.read_line(&mut line);
        Task::get_from_string(store, line)
    }

    /// Get a task from a String. The String is expected to contain the JSON-representation of the
    /// Task to get from the store (only the UUID really matters in this case)
    ///
    /// For an explanation on the return values see `Task::get_from_import()`.
    pub fn get_from_string(store: &'a Store, s: String) -> Result<RResult<Task<'a>, String>> {
        import_task(s.as_str())
            .map_err_into(TodoErrorKind::ImportError)
            .map(|t| t.uuid().clone())
            .and_then(|uuid| Task::get_from_uuid(store, uuid))
            .and_then(|o| match o {
                None    => Ok(Err(s)),
                Some(t) => Ok(Ok(t)),
            })
    }

    /// Get a task from an UUID.
    ///
    /// If there is no task with this UUID, this returns `Ok(None)`.
    pub fn get_from_uuid(store: &'a Store, uuid: Uuid) -> Result<Option<Task<'a>>> {
        ModuleEntryPath::new(format!("taskwarrior/{}", uuid))
            .into_storeid()
            .and_then(|store_id| store.get(store_id))
            .map(|o| o.map(Task::new))
            .map_err_into(TodoErrorKind::StoreError)
    }

    /// Same as Task::get_from_import() but uses Store::retrieve() rather than Store::get(), to
    /// implicitely create the task if it does not exist.
    pub fn retrieve_from_import<R: BufRead>(store: &'a Store, mut r: R) -> Result<Task<'a>> {
        let mut line = String::new();
        r.read_line(&mut line);
        Task::retrieve_from_string(store, line)
    }

    /// Retrieve a task from a String. The String is expected to contain the JSON-representation of
    /// the Task to retrieve from the store (only the UUID really matters in this case)
    pub fn retrieve_from_string(store: &'a Store, s: String) -> Result<Task<'a>> {
        Task::get_from_string(store, s)
            .and_then(|opt| match opt {
                Ok(task)    => Ok(task),
                Err(string) => import_task(string.as_str())
                    .map_err_into(TodoErrorKind::ImportError)
                    .and_then(|t| t.into_task(store)),
            })
    }

    pub fn delete_by_imports<R: BufRead>(store: &Store, r: R) -> Result<()> {
        use serde_json::ser::to_string as serde_to_string;
        use task_hookrs::status::TaskStatus;

        for (counter, res_ttask) in import_tasks(r).into_iter().enumerate() {
            match res_ttask {
                Ok(ttask) => {
                    if counter % 2 == 1 {
                        // Only every second task is needed, the first one is the
                        // task before the change, and the second one after
                        // the change. The (maybe modified) second one is
                        // expected by taskwarrior.
                        match serde_to_string(&ttask).map_err_into(TodoErrorKind::ImportError) {
                            // use println!() here, as we talk with TW
                            Ok(val) => println!("{}", val),
                            Err(e)  => return Err(e),
                        }

                        // Taskwarrior does not have the concept of deleted tasks, but only modified
                        // ones.
                        //
                        // Here we check if the status of a task is deleted and if yes, we delete it
                        // from the store.
                        if *ttask.status() == TaskStatus::Deleted {
                            match Task::delete_by_uuid(store, *ttask.uuid()) {
                                Ok(_)  => info!("Deleted task {}", *ttask.uuid()),
                                Err(e) => return Err(e),
                            }
                        }
                    } // end if c % 2
                },
                Err(e) => return Err(e).map_err_into(TodoErrorKind::ImportError),
            }
        }
        Ok(())
    }

    pub fn delete_by_uuid(store: &Store, uuid: Uuid) -> Result<()> {
        ModuleEntryPath::new(format!("taskwarrior/{}", uuid))
            .into_storeid()
            .and_then(|id| store.delete(id))
            .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.
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();
        ModuleEntryPath::new(format!("taskwarrior/{}", uuid))
            .into_storeid()
            .map_err_into(TodoErrorKind::StoreIdError)
            .and_then(|id| {
                store.retrieve(id)
                    .map_err_into(TodoErrorKind::StoreError)
                    .and_then(|mut fle| {
                        {
                            let mut hdr = fle.get_header_mut();
                            let read = hdr.read("todo").map_err_into(TodoErrorKind::StoreError);
                            if try!(read).is_none() {
                                try!(hdr
                                    .set("todo", Value::Table(BTreeMap::new()))
                                    .map_err_into(TodoErrorKind::StoreError));
                            }

                            try!(hdr.set("todo.uuid", Value::String(format!("{}",uuid)))
                                 .map_err_into(TodoErrorKind::StoreError));
                        }

                        // 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|>


<commit_before>use std::env;

fn main() {
    println!("cargo:rerun-if-changed=build.rs");
    let target = env::var("TARGET").expect("TARGET was not set");

    if cfg!(feature = "llvm-libunwind")
        && ((target.contains("linux") && !target.contains("musl")) || target.contains("fuchsia"))
    {
        // Build the unwinding from libunwind C/C++ source code.
        llvm_libunwind::compile();
    } else if target.contains("x86_64-fortanix-unknown-sgx") {
        llvm_libunwind::compile();
    } else if target.contains("linux") {
        // linking for Linux is handled in lib.rs
        if target.contains("musl") {
            llvm_libunwind::compile();
        }
    } else if target.contains("freebsd") {
        println!("cargo:rustc-link-lib=gcc_s");
    } else if target.contains("rumprun") {
        println!("cargo:rustc-link-lib=unwind");
    } else if target.contains("netbsd") {
        println!("cargo:rustc-link-lib=gcc_s");
    } else if target.contains("openbsd") {
        if target.contains("sparc64") {
            println!("cargo:rustc-link-lib=gcc");
        } else {
            println!("cargo:rustc-link-lib=c++abi");
        }
    } else if target.contains("solaris") {
        println!("cargo:rustc-link-lib=gcc_s");
    } else if target.contains("illumos") {
        println!("cargo:rustc-link-lib=gcc_s");
    } else if target.contains("dragonfly") {
        println!("cargo:rustc-link-lib=gcc_pic");
    } else if target.contains("pc-windows-gnu") {
        // This is handled in the target spec with late_link_args_[static|dynamic]
    } else if target.contains("uwp-windows-gnu") {
        println!("cargo:rustc-link-lib=unwind");
    } else if target.contains("fuchsia") {
        println!("cargo:rustc-link-lib=unwind");
    } else if target.contains("haiku") {
        println!("cargo:rustc-link-lib=gcc_s");
    } else if target.contains("redox") {
        // redox is handled in lib.rs
    }
}

mod llvm_libunwind {
    use std::env;
    use std::path::Path;

    /// Compile the libunwind C/C++ source code.
    pub fn compile() {
        let target = env::var("TARGET").expect("TARGET was not set");
        let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
        let target_vendor = env::var("CARGO_CFG_TARGET_VENDOR").unwrap();
        let target_endian_little = env::var("CARGO_CFG_TARGET_ENDIAN").unwrap() != "big";
        let cfg = &mut cc::Build::new();

        cfg.cpp(true);
        cfg.cpp_set_stdlib(None);
        cfg.warnings(false);

        // libunwind expects a __LITTLE_ENDIAN__ macro to be set for LE archs, cf. #65765
        if target_endian_little {
            cfg.define("__LITTLE_ENDIAN__", Some("1"));
        }

        if target_env == "msvc" {
            // Don't pull in extra libraries on MSVC
            cfg.flag("/Zl");
            cfg.flag("/EHsc");
            cfg.define("_CRT_SECURE_NO_WARNINGS", None);
            cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None);
        } else if target.contains("x86_64-fortanix-unknown-sgx") {
            cfg.cpp(false);

            cfg.static_flag(true);
            cfg.opt_level(3);

            cfg.flag("-nostdinc++");
            cfg.flag("-fno-exceptions");
            cfg.flag("-fno-rtti");
            cfg.flag("-fstrict-aliasing");
            cfg.flag("-funwind-tables");
            cfg.flag("-fvisibility=hidden");
            cfg.flag("-fno-stack-protector");
            cfg.flag("-ffreestanding");
            cfg.flag("-fexceptions");

            // easiest way to undefine since no API available in cc::Build to undefine
            cfg.flag("-U_FORTIFY_SOURCE");
            cfg.define("_FORTIFY_SOURCE", "0");

            cfg.flag_if_supported("-fvisibility-global-new-delete-hidden");

            cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None);
            cfg.define("RUST_SGX", "1");
            cfg.define("__NO_STRING_INLINES", None);
            cfg.define("__NO_MATH_INLINES", None);
            cfg.define("_LIBUNWIND_IS_BAREMETAL", None);
            cfg.define("__LIBUNWIND_IS_NATIVE_ONLY", None);
            cfg.define("NDEBUG", None);
        } else {
            cfg.flag("-std=c99");
            cfg.flag("-std=c++11");
            cfg.flag("-nostdinc++");
            cfg.flag("-fno-exceptions");
            cfg.flag("-fno-rtti");
            cfg.flag("-fstrict-aliasing");
            cfg.flag("-funwind-tables");
            cfg.flag("-fvisibility=hidden");
            cfg.flag_if_supported("-fvisibility-global-new-delete-hidden");
            cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None);
        }

        let mut unwind_sources = vec![
            "Unwind-EHABI.cpp",
            "Unwind-seh.cpp",
            "Unwind-sjlj.c",
            "UnwindLevel1-gcc-ext.c",
            "UnwindLevel1.c",
            "UnwindRegistersRestore.S",
            "UnwindRegistersSave.S",
            "libunwind.cpp",
        ];

        if target_vendor == "apple" {
            unwind_sources.push("Unwind_AppleExtras.cpp");
        }

        if target.contains("x86_64-fortanix-unknown-sgx") {
            unwind_sources.push("UnwindRustSgx.c");
        }

        let root = Path::new("../../src/llvm-project/libunwind");
        cfg.include(root.join("include"));
        for src in unwind_sources {
            cfg.file(root.join("src").join(src));
        }

        if target_env == "musl" {
            // use the same C compiler command to compile C++ code so we do not need to setup the
            // C++ compiler env variables on the builders
            cfg.cpp(false);
            // linking for musl is handled in lib.rs
            cfg.cargo_metadata(false);
            println!("cargo:rustc-link-search=native={}", env::var("OUT_DIR").unwrap());
        }

        cfg.compile("unwind");
    }
}
<commit_msg>Don't build in-tree llvm-libunwind if system-llvm-libunwind is enable<commit_after>use std::env;

fn main() {
    println!("cargo:rerun-if-changed=build.rs");
    let target = env::var("TARGET").expect("TARGET was not set");

    if cfg!(feature = "system-llvm-libunwind") {
        return;
    }

    if cfg!(feature = "llvm-libunwind")
        && ((target.contains("linux") && !target.contains("musl")) || target.contains("fuchsia"))
    {
        // Build the unwinding from libunwind C/C++ source code.
        llvm_libunwind::compile();
    } else if target.contains("x86_64-fortanix-unknown-sgx") {
        llvm_libunwind::compile();
    } else if target.contains("linux") {
        // linking for Linux is handled in lib.rs
        if target.contains("musl") {
            llvm_libunwind::compile();
        }
    } else if target.contains("freebsd") {
        println!("cargo:rustc-link-lib=gcc_s");
    } else if target.contains("rumprun") {
        println!("cargo:rustc-link-lib=unwind");
    } else if target.contains("netbsd") {
        println!("cargo:rustc-link-lib=gcc_s");
    } else if target.contains("openbsd") {
        if target.contains("sparc64") {
            println!("cargo:rustc-link-lib=gcc");
        } else {
            println!("cargo:rustc-link-lib=c++abi");
        }
    } else if target.contains("solaris") {
        println!("cargo:rustc-link-lib=gcc_s");
    } else if target.contains("illumos") {
        println!("cargo:rustc-link-lib=gcc_s");
    } else if target.contains("dragonfly") {
        println!("cargo:rustc-link-lib=gcc_pic");
    } else if target.contains("pc-windows-gnu") {
        // This is handled in the target spec with late_link_args_[static|dynamic]
    } else if target.contains("uwp-windows-gnu") {
        println!("cargo:rustc-link-lib=unwind");
    } else if target.contains("fuchsia") {
        println!("cargo:rustc-link-lib=unwind");
    } else if target.contains("haiku") {
        println!("cargo:rustc-link-lib=gcc_s");
    } else if target.contains("redox") {
        // redox is handled in lib.rs
    }
}

mod llvm_libunwind {
    use std::env;
    use std::path::Path;

    /// Compile the libunwind C/C++ source code.
    pub fn compile() {
        let target = env::var("TARGET").expect("TARGET was not set");
        let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
        let target_vendor = env::var("CARGO_CFG_TARGET_VENDOR").unwrap();
        let target_endian_little = env::var("CARGO_CFG_TARGET_ENDIAN").unwrap() != "big";
        let cfg = &mut cc::Build::new();

        cfg.cpp(true);
        cfg.cpp_set_stdlib(None);
        cfg.warnings(false);

        // libunwind expects a __LITTLE_ENDIAN__ macro to be set for LE archs, cf. #65765
        if target_endian_little {
            cfg.define("__LITTLE_ENDIAN__", Some("1"));
        }

        if target_env == "msvc" {
            // Don't pull in extra libraries on MSVC
            cfg.flag("/Zl");
            cfg.flag("/EHsc");
            cfg.define("_CRT_SECURE_NO_WARNINGS", None);
            cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None);
        } else if target.contains("x86_64-fortanix-unknown-sgx") {
            cfg.cpp(false);

            cfg.static_flag(true);
            cfg.opt_level(3);

            cfg.flag("-nostdinc++");
            cfg.flag("-fno-exceptions");
            cfg.flag("-fno-rtti");
            cfg.flag("-fstrict-aliasing");
            cfg.flag("-funwind-tables");
            cfg.flag("-fvisibility=hidden");
            cfg.flag("-fno-stack-protector");
            cfg.flag("-ffreestanding");
            cfg.flag("-fexceptions");

            // easiest way to undefine since no API available in cc::Build to undefine
            cfg.flag("-U_FORTIFY_SOURCE");
            cfg.define("_FORTIFY_SOURCE", "0");

            cfg.flag_if_supported("-fvisibility-global-new-delete-hidden");

            cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None);
            cfg.define("RUST_SGX", "1");
            cfg.define("__NO_STRING_INLINES", None);
            cfg.define("__NO_MATH_INLINES", None);
            cfg.define("_LIBUNWIND_IS_BAREMETAL", None);
            cfg.define("__LIBUNWIND_IS_NATIVE_ONLY", None);
            cfg.define("NDEBUG", None);
        } else {
            cfg.flag("-std=c99");
            cfg.flag("-std=c++11");
            cfg.flag("-nostdinc++");
            cfg.flag("-fno-exceptions");
            cfg.flag("-fno-rtti");
            cfg.flag("-fstrict-aliasing");
            cfg.flag("-funwind-tables");
            cfg.flag("-fvisibility=hidden");
            cfg.flag_if_supported("-fvisibility-global-new-delete-hidden");
            cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None);
        }

        let mut unwind_sources = vec![
            "Unwind-EHABI.cpp",
            "Unwind-seh.cpp",
            "Unwind-sjlj.c",
            "UnwindLevel1-gcc-ext.c",
            "UnwindLevel1.c",
            "UnwindRegistersRestore.S",
            "UnwindRegistersSave.S",
            "libunwind.cpp",
        ];

        if target_vendor == "apple" {
            unwind_sources.push("Unwind_AppleExtras.cpp");
        }

        if target.contains("x86_64-fortanix-unknown-sgx") {
            unwind_sources.push("UnwindRustSgx.c");
        }

        let root = Path::new("../../src/llvm-project/libunwind");
        cfg.include(root.join("include"));
        for src in unwind_sources {
            cfg.file(root.join("src").join(src));
        }

        if target_env == "musl" {
            // use the same C compiler command to compile C++ code so we do not need to setup the
            // C++ compiler env variables on the builders
            cfg.cpp(false);
            // linking for musl is handled in lib.rs
            cfg.cargo_metadata(false);
            println!("cargo:rustc-link-search=native={}", env::var("OUT_DIR").unwrap());
        }

        cfg.compile("unwind");
    }
}
<|endoftext|>