<commit_before>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashMap;

use syn::{parse_quote, FnArg, PatType, Type, TypePtr};

use crate::conversion::analysis::fun::{FnKind, MethodKind, ReceiverMutability};
use crate::conversion::analysis::pod::PodPhase;
use crate::conversion::api::{
    CppVisibility, FuncToConvert, RustSubclassFnDetails, SubclassName, Synthesis, Virtualness,
};
use crate::{
    conversion::{
        analysis::fun::function_wrapper::{
            CppFunction, CppFunctionBody, CppFunctionKind, TypeConversionPolicy,
        },
        api::{Api, ApiName},
    },
    types::{make_ident, Namespace, QualifiedName},
};

use super::{FnAnalysis, FnPhase};

pub(super) fn subclasses_by_superclass(
    apis: &[Api<PodPhase>],
) -> HashMap<QualifiedName, Vec<SubclassName>> {
    let mut subclasses_per_superclass: HashMap<QualifiedName, Vec<SubclassName>> = HashMap::new();

    for api in apis.iter() {
        if let Api::Subclass { name, superclass } = api {
            subclasses_per_superclass
                .entry(superclass.clone())
                .or_default()
                .push(name.clone());
        }
    }
    subclasses_per_superclass
}

pub(super) fn create_subclass_fn_wrapper(
    sub: SubclassName,
    super_fn_name: &QualifiedName,
    fun: &FuncToConvert,
) -> Box<FuncToConvert> {
    let self_ty = Some(sub.cpp());
    Box::new(FuncToConvert {
        synthesized_this_type: self_ty.clone(),
        self_ty,
        ident: super_fn_name.get_final_ident(),
        doc_attr: fun.doc_attr.clone(),
        inputs: fun.inputs.clone(),
        output: fun.output.clone(),
        vis: fun.vis.clone(),
        virtualness: Virtualness::None,
        cpp_vis: CppVisibility::Public,
        is_move_constructor: false,
        unused_template_param: fun.unused_template_param,
        original_name: None,
        return_type_is_reference: fun.return_type_is_reference,
        reference_args: fun.reference_args.clone(),
        synthesis: fun.synthesis.clone(),
    })
}

pub(super) fn create_subclass_function(
    sub: &SubclassName,
    analysis: &super::FnAnalysis,
    name: &ApiName,
    receiver_mutability: &ReceiverMutability,
    superclass: &QualifiedName,
    dependency: Option<&QualifiedName>,
) -> Api<FnPhase> {
    let cpp = sub.cpp();
    let holder_name = sub.holder();
    let rust_call_name = make_ident(format!(
        "{}_{}",
        sub.0.name.get_final_item(),
        name.name.get_final_item()
    ));
    let params = std::iter::once(parse_quote! {
        me: & #holder_name
    })
    .chain(analysis.params.iter().skip(1).cloned())
    .collect();
    let kind = if matches!(receiver_mutability, ReceiverMutability::Mutable) {
        CppFunctionKind::Method
    } else {
        CppFunctionKind::ConstMethod
    };
    let subclass_function: Api<FnPhase> = Api::RustSubclassFn {
        name: ApiName::new_in_root_namespace(rust_call_name.clone()),
        subclass: sub.clone(),
        details: Box::new(RustSubclassFnDetails {
            params,
            ret: analysis.ret_type.clone(),
            method_name: make_ident(&analysis.rust_name),
            cpp_impl: CppFunction {
                payload: CppFunctionBody::FunctionCall(Namespace::new(), rust_call_name),
                wrapper_function_name: name.name.get_final_ident(),
                original_cpp_name: name.cpp_name(),
                return_conversion: analysis.ret_conversion.clone(),
                argument_conversion: analysis
                    .param_details
                    .iter()
                    .skip(1)
                    .map(|p| p.conversion.clone())
                    .collect(),
                kind,
                pass_obs_field: true,
                qualification: Some(cpp),
            },
            superclass: superclass.clone(),
            receiver_mutability: receiver_mutability.clone(),
            dependency: dependency.cloned(),
            requires_unsafe: analysis.param_details.iter().any(|pd| pd.requires_unsafe),
            is_pure_virtual: matches!(
                analysis.kind,
                FnKind::Method(_, MethodKind::PureVirtual(..))
            ),
        }),
    };
    subclass_function
}

pub(super) fn create_subclass_constructor(
    sub: SubclassName,
    analysis: &FnAnalysis,
    sup: &QualifiedName,
    fun: &FuncToConvert,
) -> (Box<FuncToConvert>, ApiName) {
    let synthesis = Some({
        let sub = ⊂
        let holder_name = sub.holder();
        let cpp = sub.cpp();
        let wrapper_function_name = cpp.get_final_ident();
        let initial_arg = TypeConversionPolicy::new_unconverted(parse_quote! {
            rust::Box< #holder_name >
        });
        let args = std::iter::once(initial_arg).chain(
            analysis
                .param_details
                .iter()
                .skip(1) // skip placement new destination
                .map(|aa| aa.conversion.clone()),
        );
        let cpp_impl = CppFunction {
            payload: CppFunctionBody::ConstructSuperclass(sup.to_cpp_name()),
            wrapper_function_name,
            return_conversion: None,
            argument_conversion: args.collect(),
            kind: CppFunctionKind::SynthesizedConstructor,
            pass_obs_field: false,
            qualification: Some(cpp.clone()),
            original_cpp_name: cpp.to_cpp_name(),
        };
        Synthesis::SubclassConstructor {
            subclass: sub.clone(),
            cpp_impl: Box::new(cpp_impl),
            is_trivial: analysis.param_details.len() == 1, // just placement new
                                                           // destination, no other parameters
        }
    });

    let subclass_constructor_name = make_ident(format!(
        "{}_{}",
        sub.cpp().get_final_item(),
        sub.cpp().get_final_item()
    ));
    let mut existing_params = fun.inputs.clone();
    if let Some(FnArg::Typed(PatType { ty, .. })) = existing_params.first_mut() {
        if let Type::Ptr(TypePtr { elem, .. }) = &mut **ty {
            *elem = Box::new(Type::Path(sub.cpp().to_type_path()));
        } else {
            panic!("Unexpected self type parameter when creating subclass constructor");
        }
    } else {
        panic!("Unexpected self type parameter when creating subclass constructor");
    }
    let mut existing_params = existing_params.into_iter();
    let self_param = existing_params.next();
    let holder = sub.holder();
    let boxed_holder_param: FnArg = parse_quote! {
        peer: rust::Box<#holder>
    };
    let inputs = self_param
        .into_iter()
        .chain(std::iter::once(boxed_holder_param))
        .chain(existing_params)
        .collect();
    let self_ty = Some(sub.cpp());
    let maybe_wrap = Box::new(FuncToConvert {
        ident: subclass_constructor_name.clone(),
        doc_attr: fun.doc_attr.clone(),
        inputs,
        output: fun.output.clone(),
        vis: fun.vis.clone(),
        virtualness: Virtualness::None,
        cpp_vis: CppVisibility::Public,
        is_move_constructor: false,
        original_name: None,
        unused_template_param: fun.unused_template_param,
        return_type_is_reference: fun.return_type_is_reference,
        reference_args: fun.reference_args.clone(),
        synthesized_this_type: self_ty.clone(),
        self_ty,
        synthesis,
    });
    let subclass_constructor_name = ApiName::new_with_cpp_name(
        &Namespace::new(),
        subclass_constructor_name,
        Some(sub.cpp().get_final_item().to_string()),
    );
    (maybe_wrap, subclass_constructor_name)
}
<commit_msg>Deduplicate a little.<commit_after>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashMap;

use syn::{parse_quote, FnArg, PatType, Type, TypePtr};

use crate::conversion::analysis::fun::{FnKind, MethodKind, ReceiverMutability};
use crate::conversion::analysis::pod::PodPhase;
use crate::conversion::api::{
    CppVisibility, FuncToConvert, RustSubclassFnDetails, SubclassName, Synthesis, Virtualness,
};
use crate::{
    conversion::{
        analysis::fun::function_wrapper::{
            CppFunction, CppFunctionBody, CppFunctionKind, TypeConversionPolicy,
        },
        api::{Api, ApiName},
    },
    types::{make_ident, Namespace, QualifiedName},
};

use super::{FnAnalysis, FnPhase};

pub(super) fn subclasses_by_superclass(
    apis: &[Api<PodPhase>],
) -> HashMap<QualifiedName, Vec<SubclassName>> {
    let mut subclasses_per_superclass: HashMap<QualifiedName, Vec<SubclassName>> = HashMap::new();

    for api in apis.iter() {
        if let Api::Subclass { name, superclass } = api {
            subclasses_per_superclass
                .entry(superclass.clone())
                .or_default()
                .push(name.clone());
        }
    }
    subclasses_per_superclass
}

pub(super) fn create_subclass_fn_wrapper(
    sub: SubclassName,
    super_fn_name: &QualifiedName,
    fun: &FuncToConvert,
) -> Box<FuncToConvert> {
    let self_ty = Some(sub.cpp());
    Box::new(FuncToConvert {
        synthesized_this_type: self_ty.clone(),
        self_ty,
        ident: super_fn_name.get_final_ident(),
        doc_attr: fun.doc_attr.clone(),
        inputs: fun.inputs.clone(),
        output: fun.output.clone(),
        vis: fun.vis.clone(),
        virtualness: Virtualness::None,
        cpp_vis: CppVisibility::Public,
        is_move_constructor: false,
        unused_template_param: fun.unused_template_param,
        original_name: None,
        return_type_is_reference: fun.return_type_is_reference,
        reference_args: fun.reference_args.clone(),
        synthesis: fun.synthesis.clone(),
    })
}

pub(super) fn create_subclass_function(
    sub: &SubclassName,
    analysis: &super::FnAnalysis,
    name: &ApiName,
    receiver_mutability: &ReceiverMutability,
    superclass: &QualifiedName,
    dependency: Option<&QualifiedName>,
) -> Api<FnPhase> {
    let cpp = sub.cpp();
    let holder_name = sub.holder();
    let rust_call_name = make_ident(format!(
        "{}_{}",
        sub.0.name.get_final_item(),
        name.name.get_final_item()
    ));
    let params = std::iter::once(parse_quote! {
        me: & #holder_name
    })
    .chain(analysis.params.iter().skip(1).cloned())
    .collect();
    let kind = if matches!(receiver_mutability, ReceiverMutability::Mutable) {
        CppFunctionKind::Method
    } else {
        CppFunctionKind::ConstMethod
    };
    let subclass_function: Api<FnPhase> = Api::RustSubclassFn {
        name: ApiName::new_in_root_namespace(rust_call_name.clone()),
        subclass: sub.clone(),
        details: Box::new(RustSubclassFnDetails {
            params,
            ret: analysis.ret_type.clone(),
            method_name: make_ident(&analysis.rust_name),
            cpp_impl: CppFunction {
                payload: CppFunctionBody::FunctionCall(Namespace::new(), rust_call_name),
                wrapper_function_name: name.name.get_final_ident(),
                original_cpp_name: name.cpp_name(),
                return_conversion: analysis.ret_conversion.clone(),
                argument_conversion: analysis
                    .param_details
                    .iter()
                    .skip(1)
                    .map(|p| p.conversion.clone())
                    .collect(),
                kind,
                pass_obs_field: true,
                qualification: Some(cpp),
            },
            superclass: superclass.clone(),
            receiver_mutability: receiver_mutability.clone(),
            dependency: dependency.cloned(),
            requires_unsafe: analysis.param_details.iter().any(|pd| pd.requires_unsafe),
            is_pure_virtual: matches!(
                analysis.kind,
                FnKind::Method(_, MethodKind::PureVirtual(..))
            ),
        }),
    };
    subclass_function
}

pub(super) fn create_subclass_constructor(
    sub: SubclassName,
    analysis: &FnAnalysis,
    sup: &QualifiedName,
    fun: &FuncToConvert,
) -> (Box<FuncToConvert>, ApiName) {
    let holder = sub.holder();
    let cpp = sub.cpp();
    let synthesis = Some({
        let wrapper_function_name = cpp.get_final_ident();
        let initial_arg = TypeConversionPolicy::new_unconverted(parse_quote! {
            rust::Box< #holder >
        });
        let args = std::iter::once(initial_arg).chain(
            analysis
                .param_details
                .iter()
                .skip(1) // skip placement new destination
                .map(|aa| aa.conversion.clone()),
        );
        let cpp_impl = CppFunction {
            payload: CppFunctionBody::ConstructSuperclass(sup.to_cpp_name()),
            wrapper_function_name,
            return_conversion: None,
            argument_conversion: args.collect(),
            kind: CppFunctionKind::SynthesizedConstructor,
            pass_obs_field: false,
            qualification: Some(cpp.clone()),
            original_cpp_name: cpp.to_cpp_name(),
        };
        Synthesis::SubclassConstructor {
            subclass: sub.clone(),
            cpp_impl: Box::new(cpp_impl),
            is_trivial: analysis.param_details.len() == 1, // just placement new
                                                           // destination, no other parameters
        }
    });

    let subclass_constructor_name =
        make_ident(format!("{}_{}", cpp.get_final_item(), cpp.get_final_item()));
    let mut existing_params = fun.inputs.clone();
    if let Some(FnArg::Typed(PatType { ty, .. })) = existing_params.first_mut() {
        if let Type::Ptr(TypePtr { elem, .. }) = &mut **ty {
            *elem = Box::new(Type::Path(sub.cpp().to_type_path()));
        } else {
            panic!("Unexpected self type parameter when creating subclass constructor");
        }
    } else {
        panic!("Unexpected self type parameter when creating subclass constructor");
    }
    let mut existing_params = existing_params.into_iter();
    let self_param = existing_params.next();
    let boxed_holder_param: FnArg = parse_quote! {
        peer: rust::Box<#holder>
    };
    let inputs = self_param
        .into_iter()
        .chain(std::iter::once(boxed_holder_param))
        .chain(existing_params)
        .collect();
    let maybe_wrap = Box::new(FuncToConvert {
        ident: subclass_constructor_name.clone(),
        doc_attr: fun.doc_attr.clone(),
        inputs,
        output: fun.output.clone(),
        vis: fun.vis.clone(),
        virtualness: Virtualness::None,
        cpp_vis: CppVisibility::Public,
        is_move_constructor: false,
        original_name: None,
        unused_template_param: fun.unused_template_param,
        return_type_is_reference: fun.return_type_is_reference,
        reference_args: fun.reference_args.clone(),
        synthesized_this_type: Some(cpp.clone()),
        self_ty: Some(cpp.clone()),
        synthesis,
    });
    let subclass_constructor_name = ApiName::new_with_cpp_name(
        &Namespace::new(),
        subclass_constructor_name,
        Some(sub.cpp().get_final_item().to_string()),
    );
    (maybe_wrap, subclass_constructor_name)
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::mem;
use std::rc::Rc;

use serde::{Serialize, Serializer};

use core::interning::InternedString;
use core::{Dependency, PackageId, SourceId};
use semver::Version;

use util::CargoResult;

/// Subset of a `Manifest`. Contains only the most important information about
/// a package.
///
/// Summaries are cloned, and should not be mutated after creation
#[derive(Debug, Clone)]
pub struct Summary {
    inner: Rc<Inner>,
}

#[derive(Debug, Clone)]
struct Inner {
    package_id: PackageId,
    dependencies: Vec<Dependency>,
    features: FeatureMap,
    checksum: Option<String>,
    links: Option<InternedString>,
}

impl Summary {
    pub fn new(
        pkg_id: PackageId,
        dependencies: Vec<Dependency>,
        features: BTreeMap<String, Vec<String>>,
        links: Option<String>,
    ) -> CargoResult<Summary> {
        for dep in dependencies.iter() {
            if features.get(&*dep.name()).is_some() {
                bail!(
                    "Features and dependencies cannot have the \
                     same name: `{}`",
                    dep.name()
                )
            }
            if dep.is_optional() && !dep.is_transitive() {
                bail!(
                    "Dev-dependencies are not allowed to be optional: `{}`",
                    dep.name()
                )
            }
        }
        let feature_map = build_feature_map(features, &dependencies)?;
        Ok(Summary {
            inner: Rc::new(Inner {
                package_id: pkg_id,
                dependencies,
                features: feature_map,
                checksum: None,
                links: links.map(|l| InternedString::new(&l)),
            }),
        })
    }

    pub fn package_id(&self) -> &PackageId {
        &self.inner.package_id
    }
    pub fn name(&self) -> InternedString {
        self.package_id().name()
    }
    pub fn version(&self) -> &Version {
        self.package_id().version()
    }
    pub fn source_id(&self) -> &SourceId {
        self.package_id().source_id()
    }
    pub fn dependencies(&self) -> &[Dependency] {
        &self.inner.dependencies
    }
    pub fn features(&self) -> &FeatureMap {
        &self.inner.features
    }
    pub fn checksum(&self) -> Option<&str> {
        self.inner.checksum.as_ref().map(|s| &s[..])
    }
    pub fn links(&self) -> Option<InternedString> {
        self.inner.links
    }

    pub fn override_id(mut self, id: PackageId) -> Summary {
        Rc::make_mut(&mut self.inner).package_id = id;
        self
    }

    pub fn set_checksum(mut self, cksum: String) -> Summary {
        Rc::make_mut(&mut self.inner).checksum = Some(cksum);
        self
    }

    pub fn map_dependencies<F>(mut self, f: F) -> Summary
    where
        F: FnMut(Dependency) -> Dependency,
    {
        {
            let slot = &mut Rc::make_mut(&mut self.inner).dependencies;
            let deps = mem::replace(slot, Vec::new());
            *slot = deps.into_iter().map(f).collect();
        }
        self
    }

    pub fn map_source(self, to_replace: &SourceId, replace_with: &SourceId) -> Summary {
        let me = if self.package_id().source_id() == to_replace {
            let new_id = self.package_id().with_source_id(replace_with);
            self.override_id(new_id)
        } else {
            self
        };
        me.map_dependencies(|dep| dep.map_source(to_replace, replace_with))
    }
}

impl PartialEq for Summary {
    fn eq(&self, other: &Summary) -> bool {
        self.inner.package_id == other.inner.package_id
    }
}

// Checks features for errors, bailing out a CargoResult:Err if invalid,
// and creates FeatureValues for each feature.
fn build_feature_map(
    features: BTreeMap<String, Vec<String>>,
    dependencies: &[Dependency],
) -> CargoResult<FeatureMap> {
    use self::FeatureValue::*;
    let mut map = BTreeMap::new();
    for (feature, list) in features.iter() {
        let mut values = vec![];
        for dep in list {
            let val = FeatureValue::build(InternedString::new(dep), |fs| features.contains_key(fs));
            if let &Feature(_) = &val {
                values.push(val);
                continue;
            }

            // Find data for the referenced dependency...
            let dep_data = {
                match val {
                    Feature(_) => None,
                    Crate(ref dep_name) | CrateFeature(ref dep_name, _) => {
                        dependencies.iter().find(|d| d.name() == *dep_name)
                    }
                }
            };

            match (&val, dep_data) {
                (&Crate(ref dep), Some(d)) => {
                    if !d.is_optional() {
                        bail!(
                            "Feature `{}` depends on `{}` which is not an \
                             optional dependency.\nConsider adding \
                             `optional = true` to the dependency",
                            feature,
                            dep
                        )
                    }
                }
                (&CrateFeature(ref dep_name, _), None) => bail!(
                    "Feature `{}` requires a feature of `{}` which is not a \
                     dependency",
                    feature,
                    dep_name
                ),
                (&Crate(ref dep), None) => bail!(
                    "Feature `{}` includes `{}` which is neither \
                     a dependency nor another feature",
                    feature,
                    dep
                ),
                (&CrateFeature(_, _), Some(_)) | (&Feature(_), _) => {}
            }
            values.push(val);
        }
        map.insert(feature.clone(), values);
    }
    Ok(map)
}

/// FeatureValue represents the types of dependencies a feature can have:
///
/// * Another feature
/// * An optional dependency
/// * A feature in a depedency
///
/// The selection between these 3 things happens as part of the construction of the FeatureValue.
#[derive(Clone, Debug)]
pub enum FeatureValue {
    Feature(InternedString),
    Crate(InternedString),
    CrateFeature(InternedString, InternedString),
}

impl FeatureValue {
    fn build<T>(feature: InternedString, is_feature: T) -> FeatureValue
    where
        T: Fn(&str) -> bool,
    {
        match feature.find('/') {
            Some(pos) => {
                let (dep, dep_feat) = feature.split_at(pos);
                let dep_feat = &dep_feat[1..];
                FeatureValue::CrateFeature(InternedString::new(dep), InternedString::new(dep_feat))
            }
            None if is_feature(&feature) => FeatureValue::Feature(feature),
            None => FeatureValue::Crate(feature),
        }
    }

    pub fn new(feature: InternedString, s: &Summary) -> FeatureValue {
        Self::build(feature, |fs| s.features().contains_key(fs))
    }

    pub fn to_string(&self) -> String {
        use self::FeatureValue::*;
        match *self {
            Feature(ref f) => f.to_string(),
            Crate(ref c) => c.to_string(),
            CrateFeature(ref c, ref f) => [c.as_ref(), f.as_ref()].join("/"),
        }
    }
}

impl Serialize for FeatureValue {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        use self::FeatureValue::*;
        match *self {
            Feature(ref f) => serializer.serialize_str(f),
            Crate(ref c) => serializer.serialize_str(c),
            CrateFeature(ref c, ref f) => {
                serializer.serialize_str(&[c.as_ref(), f.as_ref()].join("/"))
            }
        }
    }
}

pub type FeatureMap = BTreeMap<String, Vec<FeatureValue>>;
<commit_msg>Make a dependencies map while building feature map<commit_after>use std::collections::{BTreeMap, HashMap};
use std::mem;
use std::rc::Rc;

use serde::{Serialize, Serializer};

use core::interning::InternedString;
use core::{Dependency, PackageId, SourceId};
use semver::Version;

use util::CargoResult;

/// Subset of a `Manifest`. Contains only the most important information about
/// a package.
///
/// Summaries are cloned, and should not be mutated after creation
#[derive(Debug, Clone)]
pub struct Summary {
    inner: Rc<Inner>,
}

#[derive(Debug, Clone)]
struct Inner {
    package_id: PackageId,
    dependencies: Vec<Dependency>,
    features: FeatureMap,
    checksum: Option<String>,
    links: Option<InternedString>,
}

impl Summary {
    pub fn new(
        pkg_id: PackageId,
        dependencies: Vec<Dependency>,
        features: BTreeMap<String, Vec<String>>,
        links: Option<String>,
    ) -> CargoResult<Summary> {
        for dep in dependencies.iter() {
            if features.get(&*dep.name()).is_some() {
                bail!(
                    "Features and dependencies cannot have the \
                     same name: `{}`",
                    dep.name()
                )
            }
            if dep.is_optional() && !dep.is_transitive() {
                bail!(
                    "Dev-dependencies are not allowed to be optional: `{}`",
                    dep.name()
                )
            }
        }
        let feature_map = build_feature_map(features, &dependencies)?;
        Ok(Summary {
            inner: Rc::new(Inner {
                package_id: pkg_id,
                dependencies,
                features: feature_map,
                checksum: None,
                links: links.map(|l| InternedString::new(&l)),
            }),
        })
    }

    pub fn package_id(&self) -> &PackageId {
        &self.inner.package_id
    }
    pub fn name(&self) -> InternedString {
        self.package_id().name()
    }
    pub fn version(&self) -> &Version {
        self.package_id().version()
    }
    pub fn source_id(&self) -> &SourceId {
        self.package_id().source_id()
    }
    pub fn dependencies(&self) -> &[Dependency] {
        &self.inner.dependencies
    }
    pub fn features(&self) -> &FeatureMap {
        &self.inner.features
    }
    pub fn checksum(&self) -> Option<&str> {
        self.inner.checksum.as_ref().map(|s| &s[..])
    }
    pub fn links(&self) -> Option<InternedString> {
        self.inner.links
    }

    pub fn override_id(mut self, id: PackageId) -> Summary {
        Rc::make_mut(&mut self.inner).package_id = id;
        self
    }

    pub fn set_checksum(mut self, cksum: String) -> Summary {
        Rc::make_mut(&mut self.inner).checksum = Some(cksum);
        self
    }

    pub fn map_dependencies<F>(mut self, f: F) -> Summary
    where
        F: FnMut(Dependency) -> Dependency,
    {
        {
            let slot = &mut Rc::make_mut(&mut self.inner).dependencies;
            let deps = mem::replace(slot, Vec::new());
            *slot = deps.into_iter().map(f).collect();
        }
        self
    }

    pub fn map_source(self, to_replace: &SourceId, replace_with: &SourceId) -> Summary {
        let me = if self.package_id().source_id() == to_replace {
            let new_id = self.package_id().with_source_id(replace_with);
            self.override_id(new_id)
        } else {
            self
        };
        me.map_dependencies(|dep| dep.map_source(to_replace, replace_with))
    }
}

impl PartialEq for Summary {
    fn eq(&self, other: &Summary) -> bool {
        self.inner.package_id == other.inner.package_id
    }
}

// Checks features for errors, bailing out a CargoResult:Err if invalid,
// and creates FeatureValues for each feature.
fn build_feature_map(
    features: BTreeMap<String, Vec<String>>,
    dependencies: &[Dependency],
) -> CargoResult<FeatureMap> {
    use self::FeatureValue::*;
    let dep_map: HashMap<_, _> = dependencies
        .iter()
        .map(|d| (d.name().as_str(), d))
        .collect();

    let mut map = BTreeMap::new();
    for (feature, list) in features.iter() {
        let mut values = vec![];
        for dep in list {
            let val = FeatureValue::build(InternedString::new(dep), |fs| features.contains_key(fs));
            if let &Feature(_) = &val {
                values.push(val);
                continue;
            }

            // Find data for the referenced dependency...
            let dep_data = {
                match val {
                    Feature(_) => None,
                    Crate(ref dep_name) | CrateFeature(ref dep_name, _) => {
                        dep_map.get(dep_name.as_str())
                    }
                }
            };

            match (&val, dep_data) {
                (&Crate(ref dep), Some(d)) => {
                    if !d.is_optional() {
                        bail!(
                            "Feature `{}` depends on `{}` which is not an \
                             optional dependency.\nConsider adding \
                             `optional = true` to the dependency",
                            feature,
                            dep
                        )
                    }
                }
                (&CrateFeature(ref dep_name, _), None) => bail!(
                    "Feature `{}` requires a feature of `{}` which is not a \
                     dependency",
                    feature,
                    dep_name
                ),
                (&Crate(ref dep), None) => bail!(
                    "Feature `{}` includes `{}` which is neither \
                     a dependency nor another feature",
                    feature,
                    dep
                ),
                (&CrateFeature(_, _), Some(_)) | (&Feature(_), _) => {}
            }
            values.push(val);
        }
        map.insert(feature.clone(), values);
    }
    Ok(map)
}

/// FeatureValue represents the types of dependencies a feature can have:
///
/// * Another feature
/// * An optional dependency
/// * A feature in a depedency
///
/// The selection between these 3 things happens as part of the construction of the FeatureValue.
#[derive(Clone, Debug)]
pub enum FeatureValue {
    Feature(InternedString),
    Crate(InternedString),
    CrateFeature(InternedString, InternedString),
}

impl FeatureValue {
    fn build<T>(feature: InternedString, is_feature: T) -> FeatureValue
    where
        T: Fn(&str) -> bool,
    {
        match feature.find('/') {
            Some(pos) => {
                let (dep, dep_feat) = feature.split_at(pos);
                let dep_feat = &dep_feat[1..];
                FeatureValue::CrateFeature(InternedString::new(dep), InternedString::new(dep_feat))
            }
            None if is_feature(&feature) => FeatureValue::Feature(feature),
            None => FeatureValue::Crate(feature),
        }
    }

    pub fn new(feature: InternedString, s: &Summary) -> FeatureValue {
        Self::build(feature, |fs| s.features().contains_key(fs))
    }

    pub fn to_string(&self) -> String {
        use self::FeatureValue::*;
        match *self {
            Feature(ref f) => f.to_string(),
            Crate(ref c) => c.to_string(),
            CrateFeature(ref c, ref f) => [c.as_ref(), f.as_ref()].join("/"),
        }
    }
}

impl Serialize for FeatureValue {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        use self::FeatureValue::*;
        match *self {
            Feature(ref f) => serializer.serialize_str(f),
            Crate(ref c) => serializer.serialize_str(c),
            CrateFeature(ref c, ref f) => {
                serializer.serialize_str(&[c.as_ref(), f.as_ref()].join("/"))
            }
        }
    }
}

pub type FeatureMap = BTreeMap<String, Vec<FeatureValue>>;
<|endoftext|>


<commit_before>use nom::IResult;
use std::str;

use create::*;
use insert::*;
use select::*;

#[derive(Clone, Debug, Hash, PartialEq)]
pub enum SqlQuery {
    CreateTable(CreateTableStatement),
    Insert(InsertStatement),
    Select(SelectStatement),
}

/// Parse sequence of SQL statements, divided by semicolons or newlines
// named!(pub query_list<&[u8], Vec<SqlQuery> >,
//    many1!(map_res!(selection, |s| { SqlQuery::Select(s) }))
// );

pub fn parse_query(input: &str) -> Result<SqlQuery, &str> {
    // we process all queries in lowercase to avoid having to deal with capitalization in the
    // parser.
    let q_bytes = String::from(input.trim()).into_bytes();

    // TODO(malte): appropriately pass through errors from nom
    match creation(&q_bytes) {
        IResult::Done(_, o) => return Ok(SqlQuery::CreateTable(o)),
        _ => (),
    };

    match insertion(&q_bytes) {

        IResult::Done(_, o) => return Ok(SqlQuery::Insert(o)),
        _ => (),
    };

    match selection(&q_bytes) {
        IResult::Done(_, o) => return Ok(SqlQuery::Select(o)),
        _ => (),
    };

    Err("failed to parse query")
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::hash::{Hash, Hasher};
    use std::collections::hash_map::DefaultHasher;

    use column::Column;
    use table::Table;

    #[test]
    fn hash_query() {
        let qstring = "INSERT INTO users VALUES (42, test);";
        let res = parse_query(qstring);
        assert!(res.is_ok());

        let expected = SqlQuery::Insert(InsertStatement {
            table: Table::from("users"),
            fields: vec![(Column::from("0"), "42".into()), (Column::from("1"), "test".into())],
            ..Default::default()
        });
        let mut h0 = DefaultHasher::new();
        let mut h1 = DefaultHasher::new();
        res.unwrap().hash(&mut h0);
        expected.hash(&mut h1);
        assert_eq!(h0.finish(), h1.finish());
    }

    #[test]
    fn trim_query() {
        let qstring = "   INSERT INTO users VALUES (42, test);     ";
        let res = parse_query(qstring);
        assert!(res.is_ok());
    }
}
<commit_msg>Whitespace nit<commit_after>use nom::IResult;
use std::str;

use create::*;
use insert::*;
use select::*;

#[derive(Clone, Debug, Hash, PartialEq)]
pub enum SqlQuery {
    CreateTable(CreateTableStatement),
    Insert(InsertStatement),
    Select(SelectStatement),
}

/// Parse sequence of SQL statements, divided by semicolons or newlines
// named!(pub query_list<&[u8], Vec<SqlQuery> >,
//    many1!(map_res!(selection, |s| { SqlQuery::Select(s) }))
// );

pub fn parse_query(input: &str) -> Result<SqlQuery, &str> {
    // we process all queries in lowercase to avoid having to deal with capitalization in the
    // parser.
    let q_bytes = String::from(input.trim()).into_bytes();

    // TODO(malte): appropriately pass through errors from nom
    match creation(&q_bytes) {
        IResult::Done(_, o) => return Ok(SqlQuery::CreateTable(o)),
        _ => (),
    };

    match insertion(&q_bytes) {
        IResult::Done(_, o) => return Ok(SqlQuery::Insert(o)),
        _ => (),
    };

    match selection(&q_bytes) {
        IResult::Done(_, o) => return Ok(SqlQuery::Select(o)),
        _ => (),
    };

    Err("failed to parse query")
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::hash::{Hash, Hasher};
    use std::collections::hash_map::DefaultHasher;

    use column::Column;
    use table::Table;

    #[test]
    fn hash_query() {
        let qstring = "INSERT INTO users VALUES (42, test);";
        let res = parse_query(qstring);
        assert!(res.is_ok());

        let expected = SqlQuery::Insert(InsertStatement {
            table: Table::from("users"),
            fields: vec![(Column::from("0"), "42".into()), (Column::from("1"), "test".into())],
            ..Default::default()
        });
        let mut h0 = DefaultHasher::new();
        let mut h1 = DefaultHasher::new();
        res.unwrap().hash(&mut h0);
        expected.hash(&mut h1);
        assert_eq!(h0.finish(), h1.finish());
    }

    #[test]
    fn trim_query() {
        let qstring = "   INSERT INTO users VALUES (42, test);     ";
        let res = parse_query(qstring);
        assert!(res.is_ok());
    }
}
<|endoftext|>


<commit_before>use std::default::Default;
use error::Error;

#[derive(Debug, PartialEq, RustcDecodable, RustcEncodable)]
pub struct Header {
    pub typ: Option<HeaderType>,
    pub kid: Option<String>,
    pub alg: Algorithm,
}

#[derive(Debug, PartialEq, RustcDecodable, RustcEncodable)]
pub enum HeaderType {
    JWT,
}

#[derive(Debug, PartialEq, RustcDecodable, RustcEncodable)]
pub enum Algorithm {
    HS256, RS256,
}

impl Default for Header {
    fn default() -> Header {
        Header {
            typ: Some(HeaderType::JWT),
            kid: None,
            alg: Algorithm::HS256,
        }
    }
}

#[cfg(test)]
mod tests {
    use Component;
    use header::{
        Algorithm,
        Header,
        HeaderType,
    };

    #[test]
    fn from_base64() {
        let enc = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
        let header = Header::from_base64(enc).unwrap();

        assert_eq!(header.typ.unwrap(), HeaderType::JWT);
        assert_eq!(header.alg, Algorithm::HS256);

        let enc = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFLU0YzZyJ9";
        let header = Header::from_base64(enc).unwrap();

        assert_eq!(header.kid.unwrap(), "1KSF3g".to_string());
        assert_eq!(header.alg, Algorithm::RS256);
    }

    #[test]
    fn roundtrip() {
        let header: Header = Default::default();
        let enc = Component::to_base64(&header).unwrap();
        assert_eq!(header, Header::from_base64(&*enc).unwrap());
    }
}
<commit_msg>Removed an unused import.<commit_after>use std::default::Default;

#[derive(Debug, PartialEq, RustcDecodable, RustcEncodable)]
pub struct Header {
    pub typ: Option<HeaderType>,
    pub kid: Option<String>,
    pub alg: Algorithm,
}

#[derive(Debug, PartialEq, RustcDecodable, RustcEncodable)]
pub enum HeaderType {
    JWT,
}

#[derive(Debug, PartialEq, RustcDecodable, RustcEncodable)]
pub enum Algorithm {
    HS256, RS256,
}

impl Default for Header {
    fn default() -> Header {
        Header {
            typ: Some(HeaderType::JWT),
            kid: None,
            alg: Algorithm::HS256,
        }
    }
}

#[cfg(test)]
mod tests {
    use Component;
    use header::{
        Algorithm,
        Header,
        HeaderType,
    };

    #[test]
    fn from_base64() {
        let enc = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
        let header = Header::from_base64(enc).unwrap();

        assert_eq!(header.typ.unwrap(), HeaderType::JWT);
        assert_eq!(header.alg, Algorithm::HS256);

        let enc = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFLU0YzZyJ9";
        let header = Header::from_base64(enc).unwrap();

        assert_eq!(header.kid.unwrap(), "1KSF3g".to_string());
        assert_eq!(header.alg, Algorithm::RS256);
    }

    #[test]
    fn roundtrip() {
        let header: Header = Default::default();
        let enc = Component::to_base64(&header).unwrap();
        assert_eq!(header, Header::from_base64(&*enc).unwrap());
    }
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use sys::{TypeDesc, size_of};
use libc::{c_void, size_t, uintptr_t};
use c_malloc = libc::malloc;
use c_free = libc::free;
use managed::raw::{BoxHeaderRepr, BoxRepr};
use cast::transmute;
use ptr::{set_memory, null};
use intrinsic::TyDesc;

pub unsafe fn malloc(td: *TypeDesc, size: uint) -> *c_void {
    unsafe {
        assert td.is_not_null();

        let total_size = get_box_size(size, (*td).align);
        let p = c_malloc(total_size as size_t);
        assert p.is_not_null();

        // FIXME #4761: Would be very nice to not memset all allocations
        let p: *mut u8 = transmute(p);
        set_memory(p, 0, total_size);

        // FIXME #3475: Converting between our two different tydesc types
        let td: *TyDesc = transmute(td);

        let box: &mut BoxRepr = transmute(p);
        box.header.ref_count = -1; // Exchange values not ref counted
        box.header.type_desc = td;
        box.header.prev = null();
        box.header.next = null();

        let exchange_count = &mut *rust_get_exchange_count_ptr();
        rusti::atomic_xadd(exchange_count, 1);

        return transmute(box);
    }
}

pub unsafe fn free(ptr: *c_void) {
    let exchange_count = &mut *rust_get_exchange_count_ptr();
    rusti::atomic_xsub(exchange_count, 1);

    assert ptr.is_not_null();
    c_free(ptr);
}

fn get_box_size(body_size: uint, body_align: uint) -> uint {
    let header_size = size_of::<BoxHeaderRepr>();
    // FIXME (#2699): This alignment calculation is suspicious. Is it right?
    let total_size = align_to(header_size, body_align) + body_size;
    return total_size;
}

// Rounds |size| to the nearest |alignment|. Invariant: |alignment| is a power
// of two.
fn align_to(size: uint, align: uint) -> uint {
    assert align != 0;
    (size + align - 1) & !(align - 1)
}

extern {
    #[rust_stack]
    fn rust_get_exchange_count_ptr() -> *mut int;
}

#[abi = "rust-intrinsic"]
extern mod rusti {
    fn atomic_xadd(dst: &mut int, src: int) -> int;
    fn atomic_xsub(dst: &mut int, src: int) -> int;
}
<commit_msg>stop zeroing all exchange allocations<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use sys::{TypeDesc, size_of};
use libc::{c_void, size_t, uintptr_t};
use c_malloc = libc::malloc;
use c_free = libc::free;
use managed::raw::{BoxHeaderRepr, BoxRepr};
use cast::transmute;
use ptr::null;
use intrinsic::TyDesc;

pub unsafe fn malloc(td: *TypeDesc, size: uint) -> *c_void {
    unsafe {
        assert td.is_not_null();

        let total_size = get_box_size(size, (*td).align);
        let p = c_malloc(total_size as size_t);
        assert p.is_not_null();

        // FIXME #3475: Converting between our two different tydesc types
        let td: *TyDesc = transmute(td);

        let box: &mut BoxRepr = transmute(p);
        box.header.ref_count = -1; // Exchange values not ref counted
        box.header.type_desc = td;
        box.header.prev = null();
        box.header.next = null();

        let exchange_count = &mut *rust_get_exchange_count_ptr();
        rusti::atomic_xadd(exchange_count, 1);

        return transmute(box);
    }
}

pub unsafe fn free(ptr: *c_void) {
    let exchange_count = &mut *rust_get_exchange_count_ptr();
    rusti::atomic_xsub(exchange_count, 1);

    assert ptr.is_not_null();
    c_free(ptr);
}

fn get_box_size(body_size: uint, body_align: uint) -> uint {
    let header_size = size_of::<BoxHeaderRepr>();
    // FIXME (#2699): This alignment calculation is suspicious. Is it right?
    let total_size = align_to(header_size, body_align) + body_size;
    return total_size;
}

// Rounds |size| to the nearest |alignment|. Invariant: |alignment| is a power
// of two.
fn align_to(size: uint, align: uint) -> uint {
    assert align != 0;
    (size + align - 1) & !(align - 1)
}

extern {
    #[rust_stack]
    fn rust_get_exchange_count_ptr() -> *mut int;
}

#[abi = "rust-intrinsic"]
extern mod rusti {
    fn atomic_xadd(dst: &mut int, src: int) -> int;
    fn atomic_xsub(dst: &mut int, src: int) -> int;
}
<|endoftext|>


<commit_before>/*!
The ignore module is responsible for managing the state required to determine
whether a *single* file path should be searched or not.

In general, there are two ways to ignore a particular file:

1. Specify an ignore rule in some "global" configuration, such as a
   $HOME/.ignore or on the command line.
2. A specific ignore file (like .gitignore) found during directory traversal.

The `IgnoreDir` type handles ignore patterns for any one particular directory
(including "global" ignore patterns), while the `Ignore` type handles a stack
of `IgnoreDir`s for use during directory traversal.
*/

use std::error::Error as StdError;
use std::fmt;
use std::io;
use std::path::{Path, PathBuf};

use gitignore::{self, Gitignore, GitignoreBuilder, Match, Pattern};
use pathutil::is_hidden;
use types::Types;

const IGNORE_NAMES: &'static [&'static str] = &[
    ".gitignore",
    ".ignore",
    ".rgignore",
];

/// Represents an error that can occur when parsing a gitignore file.
#[derive(Debug)]
pub enum Error {
    Gitignore(gitignore::Error),
    Io {
        path: PathBuf,
        err: io::Error,
    },
}

impl Error {
    fn from_io<P: AsRef<Path>>(path: P, err: io::Error) -> Error {
        Error::Io { path: path.as_ref().to_path_buf(), err: err }
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Gitignore(ref err) => err.description(),
            Error::Io { ref err, .. } => err.description(),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Gitignore(ref err) => err.fmt(f),
            Error::Io { ref path, ref err } => {
                write!(f, "{}: {}", path.display(), err)
            }
        }
    }
}

impl From<gitignore::Error> for Error {
    fn from(err: gitignore::Error) -> Error {
        Error::Gitignore(err)
    }
}

/// Ignore represents a collection of ignore patterns organized by directory.
/// In particular, a stack is maintained, where the top of the stack
/// corresponds to the current directory being searched and the bottom of the
/// stack represents the root of a search. Ignore patterns at the top of the
/// stack take precedence over ignore patterns at the bottom of the stack.
pub struct Ignore {
    /// A stack of ignore patterns at each directory level of traversal.
    /// A directory that contributes no ignore patterns is `None`.
    stack: Vec<Option<IgnoreDir>>,
    /// A set of override globs that are always checked first. A match (whether
    /// it's whitelist or blacklist) trumps anything in stack.
    overrides: Overrides,
    /// A file type matcher.
    types: Types,
    /// Whether to ignore hidden files or not.
    ignore_hidden: bool,
    /// When true, don't look at .gitignore or .agignore files for ignore
    /// rules.
    no_ignore: bool,
}

impl Ignore {
    /// Create an empty set of ignore patterns.
    pub fn new() -> Ignore {
        Ignore {
            stack: vec![],
            overrides: Overrides::new(None),
            types: Types::empty(),
            ignore_hidden: true,
            no_ignore: false,
        }
    }

    /// Set whether hidden files/folders should be ignored (defaults to true).
    pub fn ignore_hidden(&mut self, yes: bool) -> &mut Ignore {
        self.ignore_hidden = yes;
        self
    }

    /// When set, ignore files are ignored.
    pub fn no_ignore(&mut self, yes: bool) -> &mut Ignore {
        self.no_ignore = yes;
        self
    }

    /// Add a set of globs that overrides all other match logic.
    pub fn add_override(&mut self, gi: Gitignore) -> &mut Ignore {
        self.overrides = Overrides::new(Some(gi));
        self
    }

    /// Add a file type matcher. The file type matcher has the lowest
    /// precedence.
    pub fn add_types(&mut self, types: Types) -> &mut Ignore {
        self.types = types;
        self
    }

    /// Push parent directories of `path` on to the stack.
    pub fn push_parents<P: AsRef<Path>>(
        &mut self,
        path: P,
    ) -> Result<(), Error> {
        let path = try!(path.as_ref().canonicalize().map_err(|err| {
            Error::from_io(path.as_ref(), err)
        }));
        let mut path = &*path;
        let mut saw_git = path.join(".git").is_dir();
        let mut ignore_names = IGNORE_NAMES.to_vec();
        let mut ignore_dir_results = vec![];
        while let Some(parent) = path.parent() {
            if self.no_ignore {
                ignore_dir_results.push(Ok(None));
            } else {
                if saw_git {
                    ignore_names.retain(|&name| name != ".gitignore");
                } else {
                    saw_git = parent.join(".git").is_dir();
                }
                let ignore_dir_result =
                    IgnoreDir::with_ignore_names(parent, ignore_names.iter());
                ignore_dir_results.push(ignore_dir_result);
            }
            path = parent;
        }

        for ignore_dir_result in ignore_dir_results.into_iter().rev() {
            try!(self.push_ignore_dir(ignore_dir_result));
        }
        Ok(())
    }

    /// Add a directory to the stack.
    ///
    /// Note that even if this returns an error, the directory is added to the
    /// stack (and therefore should be popped).
    pub fn push<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
        if self.no_ignore {
            self.stack.push(None);
            return Ok(());
        }
        self.push_ignore_dir(IgnoreDir::new(path))
    }

    /// Pushes the result of building a directory matcher on to the stack.
    ///
    /// If the result given contains an error, then it is returned.
    pub fn push_ignore_dir(
        &mut self,
        result: Result<Option<IgnoreDir>, Error>,
    ) -> Result<(), Error> {
        match result {
            Ok(id) => {
                self.stack.push(id);
                Ok(())
            }
            Err(err) => {
                // Don't leave the stack in an inconsistent state.
                self.stack.push(None);
                Err(err)
            }
        }
    }

    /// Pop a directory from the stack.
    ///
    /// This panics if the stack is empty.
    pub fn pop(&mut self) {
        self.stack.pop().expect("non-empty stack");
    }

    /// Returns true if and only if the given file path should be ignored.
    pub fn ignored<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> bool {
        let path = path.as_ref();
        let mat = self.overrides.matched(path, is_dir);
        if let Some(is_ignored) = self.ignore_match(path, mat) {
            return is_ignored;
        }
        if self.ignore_hidden && is_hidden(&path) {
            debug!("{} ignored because it is hidden", path.display());
            return true;
        }
        if !self.no_ignore {
            for id in self.stack.iter().rev().filter_map(|id| id.as_ref()) {
                let mat = id.matched(path, is_dir);
                if let Some(is_ignored) = self.ignore_match(path, mat) {
                    if is_ignored {
                        return true;
                    }
                    // If this path is whitelisted by an ignore, then
                    // fallthrough and let the file type matcher have a say.
                    break;
                }
            }
        }
        let mat = self.types.matched(path, is_dir);
        if let Some(is_ignored) = self.ignore_match(path, mat) {
            return is_ignored;
        }
        false
    }

    /// Returns true if the given match says the given pattern should be
    /// ignored or false if the given pattern should be explicitly whitelisted.
    /// Returns None otherwise.
    pub fn ignore_match<P: AsRef<Path>>(
        &self,
        path: P,
        mat: Match,
    ) -> Option<bool> {
        let path = path.as_ref();
        match mat {
            Match::Whitelist(ref pat) => {
                debug!("{} whitelisted by {:?}", path.display(), pat);
                Some(false)
            }
            Match::Ignored(ref pat) => {
                debug!("{} ignored by {:?}", path.display(), pat);
                Some(true)
            }
            Match::None => None,
        }
    }
}

/// IgnoreDir represents a set of ignore patterns retrieved from a single
/// directory.
pub struct IgnoreDir {
    /// The path to this directory as given.
    path: PathBuf,
    /// A single accumulation of glob patterns for this directory, matched
    /// using gitignore semantics.
    ///
    /// This will include patterns from rgignore as well. The patterns are
    /// ordered so that precedence applies automatically (e.g., rgignore
    /// patterns procede gitignore patterns).
    gi: Option<Gitignore>,
    // TODO(burntsushi): Matching other types of glob patterns that don't
    // conform to gitignore will probably require refactoring this approach.
}

impl IgnoreDir {
    /// Create a new matcher for the given directory.
    ///
    /// If no ignore glob patterns could be found in the directory then `None`
    /// is returned.
    pub fn new<P: AsRef<Path>>(path: P) -> Result<Option<IgnoreDir>, Error> {
        IgnoreDir::with_ignore_names(path, IGNORE_NAMES.iter())
    }

    /// Create a new matcher for the given directory using only the ignore
    /// patterns found in the file names given.
    ///
    /// If no ignore glob patterns could be found in the directory then `None`
    /// is returned.
    ///
    /// Note that the order of the names given is meaningful. Names appearing
    /// later in the list have precedence over names appearing earlier in the
    /// list.
    pub fn with_ignore_names<P: AsRef<Path>, S, I>(
        path: P,
        names: I,
    ) -> Result<Option<IgnoreDir>, Error>
    where P: AsRef<Path>, S: AsRef<str>, I: Iterator<Item=S> {
        let mut id = IgnoreDir {
            path: path.as_ref().to_path_buf(),
            gi: None,
        };
        let mut ok = false;
        let mut builder = GitignoreBuilder::new(&id.path);
        // The ordering here is important. Later globs have higher precedence.
        for name in names {
            ok = builder.add_path(id.path.join(name.as_ref())).is_ok() || ok;
        }
        if !ok {
            Ok(None)
        } else {
            id.gi = Some(try!(builder.build()));
            Ok(Some(id))
        }
    }

    /// Returns true if and only if the given file path should be ignored
    /// according to the globs in this directory. `is_dir` should be true if
    /// the path refers to a directory and false otherwise.
    ///
    /// Before matching path, its prefix (as determined by a common suffix
    /// of this directory) is stripped. If there is
    /// no common suffix/prefix overlap, then path is assumed to reside
    /// directly in this directory.
    ///
    /// If the given path has a `./` prefix then it is stripped before
    /// matching.
    pub fn matched<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> Match {
        self.gi.as_ref()
            .map(|gi| gi.matched(path, is_dir))
            .unwrap_or(Match::None)
    }
}

/// Manages a set of overrides provided explicitly by the end user.
struct Overrides {
    gi: Option<Gitignore>,
    unmatched_pat: Pattern,
}

impl Overrides {
    /// Creates a new set of overrides from the gitignore matcher provided.
    /// If no matcher is provided, then the resulting overrides have no effect.
    fn new(gi: Option<Gitignore>) -> Overrides {
        Overrides {
            gi: gi,
            unmatched_pat: Pattern {
                from: Path::new("<argv>").to_path_buf(),
                original: "<none>".to_string(),
                pat: "<none>".to_string(),
                whitelist: false,
                only_dir: false,
            },
        }
    }

    /// Returns a match for the given path against this set of overrides.
    ///
    /// If there are no overrides, then this always returns Match::None.
    ///
    /// If there is at least one positive override, then this never returns
    /// Match::None (and interpreting non-matches as ignored) unless is_dir
    /// is true.
    pub fn matched<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> Match {
        // File types don't apply to directories.
        if is_dir {
            return Match::None;
        }
        let path = path.as_ref();
        self.gi.as_ref()
            .map(|gi| {
                let mat = gi.matched_stripped(path, is_dir).invert();
                if mat.is_none() && !is_dir {
                    if gi.num_ignores() > 0 {
                        return Match::Ignored(&self.unmatched_pat);
                    }
                }
                mat
            })
            .unwrap_or(Match::None)
    }
}

#[cfg(test)]
mod tests {
    use std::path::Path;
    use gitignore::GitignoreBuilder;
    use super::IgnoreDir;

    macro_rules! ignored_dir {
        ($name:ident, $root:expr, $gi:expr, $xi:expr, $path:expr) => {
            #[test]
            fn $name() {
                let mut builder = GitignoreBuilder::new(&$root);
                builder.add_str($gi).unwrap();
                builder.add_str($xi).unwrap();
                let gi = builder.build().unwrap();
                let id = IgnoreDir {
                    path: Path::new($root).to_path_buf(),
                    gi: Some(gi),
                };
                assert!(id.matched($path, false).is_ignored());
            }
        };
    }

    macro_rules! not_ignored_dir {
        ($name:ident, $root:expr, $gi:expr, $xi:expr, $path:expr) => {
            #[test]
            fn $name() {
                let mut builder = GitignoreBuilder::new(&$root);
                builder.add_str($gi).unwrap();
                builder.add_str($xi).unwrap();
                let gi = builder.build().unwrap();
                let id = IgnoreDir {
                    path: Path::new($root).to_path_buf(),
                    gi: Some(gi),
                };
                assert!(!id.matched($path, false).is_ignored());
            }
        };
    }

    const ROOT: &'static str = "/home/foobar/rust/rg";

    ignored_dir!(id1, ROOT, "src/main.rs", "", "src/main.rs");
    ignored_dir!(id2, ROOT, "", "src/main.rs", "src/main.rs");
    ignored_dir!(id3, ROOT, "!src/main.rs", "*.rs", "src/main.rs");

    not_ignored_dir!(idnot1, ROOT, "*.rs", "!src/main.rs", "src/main.rs");
}
<commit_msg>Impl Debug for IgnoreDir.<commit_after>/*!
The ignore module is responsible for managing the state required to determine
whether a *single* file path should be searched or not.

In general, there are two ways to ignore a particular file:

1. Specify an ignore rule in some "global" configuration, such as a
   $HOME/.ignore or on the command line.
2. A specific ignore file (like .gitignore) found during directory traversal.

The `IgnoreDir` type handles ignore patterns for any one particular directory
(including "global" ignore patterns), while the `Ignore` type handles a stack
of `IgnoreDir`s for use during directory traversal.
*/

use std::error::Error as StdError;
use std::fmt;
use std::io;
use std::path::{Path, PathBuf};

use gitignore::{self, Gitignore, GitignoreBuilder, Match, Pattern};
use pathutil::is_hidden;
use types::Types;

const IGNORE_NAMES: &'static [&'static str] = &[
    ".gitignore",
    ".ignore",
    ".rgignore",
];

/// Represents an error that can occur when parsing a gitignore file.
#[derive(Debug)]
pub enum Error {
    Gitignore(gitignore::Error),
    Io {
        path: PathBuf,
        err: io::Error,
    },
}

impl Error {
    fn from_io<P: AsRef<Path>>(path: P, err: io::Error) -> Error {
        Error::Io { path: path.as_ref().to_path_buf(), err: err }
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Gitignore(ref err) => err.description(),
            Error::Io { ref err, .. } => err.description(),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Gitignore(ref err) => err.fmt(f),
            Error::Io { ref path, ref err } => {
                write!(f, "{}: {}", path.display(), err)
            }
        }
    }
}

impl From<gitignore::Error> for Error {
    fn from(err: gitignore::Error) -> Error {
        Error::Gitignore(err)
    }
}

/// Ignore represents a collection of ignore patterns organized by directory.
/// In particular, a stack is maintained, where the top of the stack
/// corresponds to the current directory being searched and the bottom of the
/// stack represents the root of a search. Ignore patterns at the top of the
/// stack take precedence over ignore patterns at the bottom of the stack.
pub struct Ignore {
    /// A stack of ignore patterns at each directory level of traversal.
    /// A directory that contributes no ignore patterns is `None`.
    stack: Vec<Option<IgnoreDir>>,
    /// A set of override globs that are always checked first. A match (whether
    /// it's whitelist or blacklist) trumps anything in stack.
    overrides: Overrides,
    /// A file type matcher.
    types: Types,
    /// Whether to ignore hidden files or not.
    ignore_hidden: bool,
    /// When true, don't look at .gitignore or .agignore files for ignore
    /// rules.
    no_ignore: bool,
}

impl Ignore {
    /// Create an empty set of ignore patterns.
    pub fn new() -> Ignore {
        Ignore {
            stack: vec![],
            overrides: Overrides::new(None),
            types: Types::empty(),
            ignore_hidden: true,
            no_ignore: false,
        }
    }

    /// Set whether hidden files/folders should be ignored (defaults to true).
    pub fn ignore_hidden(&mut self, yes: bool) -> &mut Ignore {
        self.ignore_hidden = yes;
        self
    }

    /// When set, ignore files are ignored.
    pub fn no_ignore(&mut self, yes: bool) -> &mut Ignore {
        self.no_ignore = yes;
        self
    }

    /// Add a set of globs that overrides all other match logic.
    pub fn add_override(&mut self, gi: Gitignore) -> &mut Ignore {
        self.overrides = Overrides::new(Some(gi));
        self
    }

    /// Add a file type matcher. The file type matcher has the lowest
    /// precedence.
    pub fn add_types(&mut self, types: Types) -> &mut Ignore {
        self.types = types;
        self
    }

    /// Push parent directories of `path` on to the stack.
    pub fn push_parents<P: AsRef<Path>>(
        &mut self,
        path: P,
    ) -> Result<(), Error> {
        let path = try!(path.as_ref().canonicalize().map_err(|err| {
            Error::from_io(path.as_ref(), err)
        }));
        let mut path = &*path;
        let mut saw_git = path.join(".git").is_dir();
        let mut ignore_names = IGNORE_NAMES.to_vec();
        let mut ignore_dir_results = vec![];
        while let Some(parent) = path.parent() {
            if self.no_ignore {
                ignore_dir_results.push(Ok(None));
            } else {
                if saw_git {
                    ignore_names.retain(|&name| name != ".gitignore");
                } else {
                    saw_git = parent.join(".git").is_dir();
                }
                let ignore_dir_result =
                    IgnoreDir::with_ignore_names(parent, ignore_names.iter());
                ignore_dir_results.push(ignore_dir_result);
            }
            path = parent;
        }

        for ignore_dir_result in ignore_dir_results.into_iter().rev() {
            try!(self.push_ignore_dir(ignore_dir_result));
        }
        Ok(())
    }

    /// Add a directory to the stack.
    ///
    /// Note that even if this returns an error, the directory is added to the
    /// stack (and therefore should be popped).
    pub fn push<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
        if self.no_ignore {
            self.stack.push(None);
            return Ok(());
        }
        self.push_ignore_dir(IgnoreDir::new(path))
    }

    /// Pushes the result of building a directory matcher on to the stack.
    ///
    /// If the result given contains an error, then it is returned.
    pub fn push_ignore_dir(
        &mut self,
        result: Result<Option<IgnoreDir>, Error>,
    ) -> Result<(), Error> {
        match result {
            Ok(id) => {
                self.stack.push(id);
                Ok(())
            }
            Err(err) => {
                // Don't leave the stack in an inconsistent state.
                self.stack.push(None);
                Err(err)
            }
        }
    }

    /// Pop a directory from the stack.
    ///
    /// This panics if the stack is empty.
    pub fn pop(&mut self) {
        self.stack.pop().expect("non-empty stack");
    }

    /// Returns true if and only if the given file path should be ignored.
    pub fn ignored<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> bool {
        let path = path.as_ref();
        let mat = self.overrides.matched(path, is_dir);
        if let Some(is_ignored) = self.ignore_match(path, mat) {
            return is_ignored;
        }
        if self.ignore_hidden && is_hidden(&path) {
            debug!("{} ignored because it is hidden", path.display());
            return true;
        }
        if !self.no_ignore {
            for id in self.stack.iter().rev().filter_map(|id| id.as_ref()) {
                let mat = id.matched(path, is_dir);
                // println!("{}, {:?}, {:?}, {:?}\n", path.display(), is_dir, mat, id);
                if let Some(is_ignored) = self.ignore_match(path, mat) {
                    if is_ignored {
                        return true;
                    }
                    // If this path is whitelisted by an ignore, then
                    // fallthrough and let the file type matcher have a say.
                    break;
                }
            }
        }
        let mat = self.types.matched(path, is_dir);
        if let Some(is_ignored) = self.ignore_match(path, mat) {
            return is_ignored;
        }
        false
    }

    /// Returns true if the given match says the given pattern should be
    /// ignored or false if the given pattern should be explicitly whitelisted.
    /// Returns None otherwise.
    pub fn ignore_match<P: AsRef<Path>>(
        &self,
        path: P,
        mat: Match,
    ) -> Option<bool> {
        let path = path.as_ref();
        match mat {
            Match::Whitelist(ref pat) => {
                debug!("{} whitelisted by {:?}", path.display(), pat);
                Some(false)
            }
            Match::Ignored(ref pat) => {
                debug!("{} ignored by {:?}", path.display(), pat);
                Some(true)
            }
            Match::None => None,
        }
    }
}

/// IgnoreDir represents a set of ignore patterns retrieved from a single
/// directory.
#[derive(Debug)]
pub struct IgnoreDir {
    /// The path to this directory as given.
    path: PathBuf,
    /// A single accumulation of glob patterns for this directory, matched
    /// using gitignore semantics.
    ///
    /// This will include patterns from rgignore as well. The patterns are
    /// ordered so that precedence applies automatically (e.g., rgignore
    /// patterns procede gitignore patterns).
    gi: Option<Gitignore>,
    // TODO(burntsushi): Matching other types of glob patterns that don't
    // conform to gitignore will probably require refactoring this approach.
}

impl IgnoreDir {
    /// Create a new matcher for the given directory.
    ///
    /// If no ignore glob patterns could be found in the directory then `None`
    /// is returned.
    pub fn new<P: AsRef<Path>>(path: P) -> Result<Option<IgnoreDir>, Error> {
        IgnoreDir::with_ignore_names(path, IGNORE_NAMES.iter())
    }

    /// Create a new matcher for the given directory using only the ignore
    /// patterns found in the file names given.
    ///
    /// If no ignore glob patterns could be found in the directory then `None`
    /// is returned.
    ///
    /// Note that the order of the names given is meaningful. Names appearing
    /// later in the list have precedence over names appearing earlier in the
    /// list.
    pub fn with_ignore_names<P: AsRef<Path>, S, I>(
        path: P,
        names: I,
    ) -> Result<Option<IgnoreDir>, Error>
    where P: AsRef<Path>, S: AsRef<str>, I: Iterator<Item=S> {
        let mut id = IgnoreDir {
            path: path.as_ref().to_path_buf(),
            gi: None,
        };
        let mut ok = false;
        let mut builder = GitignoreBuilder::new(&id.path);
        // The ordering here is important. Later globs have higher precedence.
        for name in names {
            ok = builder.add_path(id.path.join(name.as_ref())).is_ok() || ok;
        }
        if !ok {
            Ok(None)
        } else {
            id.gi = Some(try!(builder.build()));
            Ok(Some(id))
        }
    }

    /// Returns true if and only if the given file path should be ignored
    /// according to the globs in this directory. `is_dir` should be true if
    /// the path refers to a directory and false otherwise.
    ///
    /// Before matching path, its prefix (as determined by a common suffix
    /// of this directory) is stripped. If there is
    /// no common suffix/prefix overlap, then path is assumed to reside
    /// directly in this directory.
    ///
    /// If the given path has a `./` prefix then it is stripped before
    /// matching.
    pub fn matched<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> Match {
        self.gi.as_ref()
            .map(|gi| gi.matched(path, is_dir))
            .unwrap_or(Match::None)
    }
}

/// Manages a set of overrides provided explicitly by the end user.
struct Overrides {
    gi: Option<Gitignore>,
    unmatched_pat: Pattern,
}

impl Overrides {
    /// Creates a new set of overrides from the gitignore matcher provided.
    /// If no matcher is provided, then the resulting overrides have no effect.
    fn new(gi: Option<Gitignore>) -> Overrides {
        Overrides {
            gi: gi,
            unmatched_pat: Pattern {
                from: Path::new("<argv>").to_path_buf(),
                original: "<none>".to_string(),
                pat: "<none>".to_string(),
                whitelist: false,
                only_dir: false,
            },
        }
    }

    /// Returns a match for the given path against this set of overrides.
    ///
    /// If there are no overrides, then this always returns Match::None.
    ///
    /// If there is at least one positive override, then this never returns
    /// Match::None (and interpreting non-matches as ignored) unless is_dir
    /// is true.
    pub fn matched<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> Match {
        // File types don't apply to directories.
        if is_dir {
            return Match::None;
        }
        let path = path.as_ref();
        self.gi.as_ref()
            .map(|gi| {
                let mat = gi.matched_stripped(path, is_dir).invert();
                if mat.is_none() && !is_dir {
                    if gi.num_ignores() > 0 {
                        return Match::Ignored(&self.unmatched_pat);
                    }
                }
                mat
            })
            .unwrap_or(Match::None)
    }
}

#[cfg(test)]
mod tests {
    use std::path::Path;
    use gitignore::GitignoreBuilder;
    use super::IgnoreDir;

    macro_rules! ignored_dir {
        ($name:ident, $root:expr, $gi:expr, $xi:expr, $path:expr) => {
            #[test]
            fn $name() {
                let mut builder = GitignoreBuilder::new(&$root);
                builder.add_str($gi).unwrap();
                builder.add_str($xi).unwrap();
                let gi = builder.build().unwrap();
                let id = IgnoreDir {
                    path: Path::new($root).to_path_buf(),
                    gi: Some(gi),
                };
                assert!(id.matched($path, false).is_ignored());
            }
        };
    }

    macro_rules! not_ignored_dir {
        ($name:ident, $root:expr, $gi:expr, $xi:expr, $path:expr) => {
            #[test]
            fn $name() {
                let mut builder = GitignoreBuilder::new(&$root);
                builder.add_str($gi).unwrap();
                builder.add_str($xi).unwrap();
                let gi = builder.build().unwrap();
                let id = IgnoreDir {
                    path: Path::new($root).to_path_buf(),
                    gi: Some(gi),
                };
                assert!(!id.matched($path, false).is_ignored());
            }
        };
    }

    const ROOT: &'static str = "/home/foobar/rust/rg";

    ignored_dir!(id1, ROOT, "src/main.rs", "", "src/main.rs");
    ignored_dir!(id2, ROOT, "", "src/main.rs", "src/main.rs");
    ignored_dir!(id3, ROOT, "!src/main.rs", "*.rs", "src/main.rs");

    not_ignored_dir!(idnot1, ROOT, "*.rs", "!src/main.rs", "src/main.rs");
}
<|endoftext|>


<commit_before>// Copyright 2016 Pierre Marijon.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! GFF3 format reading and writing.
//!
//! GFF2 definition : http://gmod.org/wiki/GFF2#The_GFF2_File_Format (not yet support)
//! GTF2 definition : http://mblab.wustl.edu/GTF2.html (not yet support)
//! GFF3 definition : http://gmod.org/wiki/GFF3#GFF3_Format
//!
//! # Example
//!
//! ```
//! use std::io;
//! use bio::io::gff;
//! let reader = gff::Reader::new(io::stdin());
//! ```

use std::io;
use std::fs;
use std::path::Path;
use std::convert::AsRef;
use std::collections::HashMap;

use csv;

use io::Strand;

/// A GFF reader.
pub struct Reader<R: io::Read> {
    inner: csv::Reader<R>,
}

impl Reader<fs::File> {
    /// Read GFF from given file path.
    pub fn from_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
        fs::File::open(path).map(Reader::new)
    }
}

impl<R: io::Read> Reader<R> {
    /// Create a new GFF reader given an instance of `io::Read`.
    pub fn new(reader: R) -> Self {
        Reader {
            inner: csv::Reader::from_reader(reader).delimiter(b'\t').has_headers(false)
        }
    }

    /// Iterate over all records.
    pub fn records(&mut self) -> Records<R> {
        Records { inner: self.inner.decode() }
    }
}

/// A GFF record.
pub struct Records<'a, R: 'a + io::Read> {
    inner: csv::DecodedRecords<'a, R, (String, String, String, u64, u64, String, String, String, String)>,
}

impl<'a, R: io::Read> Iterator for Records<'a, R> {
    type Item = csv::Result<Record>;

    fn next(&mut self) -> Option<csv::Result<Record>> {
        self.inner.next().map(|res| {
            res.map(|(seqname, source, feature_type, start, end, score, strand, frame, attributes)| {
                Record {
                    seqname: seqname,
                    source: source,
                    feature_type: feature_type,
                    start: start,
                    end: end,
                    score: score,
                    strand: strand,
                    frame: frame,
                    attributes: csv::Reader::from_string(attributes)
                        .delimiter(b'=')
                        .record_terminator(csv::RecordTerminator::Any(b';'))
                        .has_headers(false)
                        .decode().collect::<csv::Result<HashMap<String, String>>>().unwrap(),
                }
            })
        })
    }
}

/// A GFF writer.
pub struct Writer<W: io::Write> {
    inner: csv::Writer<W>,
}

impl Writer<fs::File> {
    /// Write to a given file path.
    pub fn to_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
        fs::File::create(path).map(Writer::new)
    }
}

impl<W: io::Write> Writer<W> {
    /// Write to a given writer.
    pub fn new(writer: W) -> Self {
        Writer { inner: csv::Writer::from_writer(writer).delimiter(b'\t').flexible(true) }
    }

    /// Write a given GFF record.
    pub fn write(&mut self, record: Record) -> csv::Result<()> {
        let attributes;
        if !record.attributes.is_empty() {
            attributes = record.attributes.iter().map(|(a, b)| format!("{}={}", a, b)).collect::<Vec<_>>().join(";");
        } else {
            attributes = "".to_owned();
        }
        self.inner.encode((record.seqname, record.source, record.feature_type, record.start, record.end, record.score, record.strand, record.frame, attributes))
    }
}

/// A GFF record
#[derive(RustcEncodable)]
pub struct Record {
    seqname: String,
    source: String,
    feature_type: String,
    start: u64,
    end: u64,
    score: String,
    strand: String,
    frame: String,
    attributes: HashMap<String, String>,
}

impl Record {
    /// Create a new GFF record.
    pub fn new() -> Self {
        Record {
            seqname: "".to_owned(),
            source: "".to_owned(),
            feature_type: "".to_owned(),
            start: 0,
            end: 0,
            score: ".".to_owned(),
            strand: ".".to_owned(),
            frame: "".to_owned(),
            attributes: HashMap::<String, String>::new(),
        }
    }

    /// Sequence name of the feature.
    pub fn seqname(&self) -> &str {
        &self.seqname
    }

    /// Source of the feature.
    pub fn source(&self) -> &str {
        &self.source
    }

    /// Type of the feature.
    pub fn feature_type(&self) -> &str {
        &self.feature_type
    }

    /// Start position of feature (1-based).
    pub fn start(&self) -> &u64 {
        &self.start
    }

    /// End position of feature (1-based, not included).
    pub fn end(&self) -> &u64 {
        &self.end
    }

    /// Score of feature
    pub fn score(&self) -> Option<u64> {
        match self.score.as_ref() {
            "." => None,
            _ => self.score.parse::<u64>().ok(),
        }
    }

    /// Strand of the feature.
    pub fn strand(&self) -> Option<Strand> {
        match self.strand.as_ref() {
            "+" => Some(Strand::Forward),
            "-" => Some(Strand::Reverse),
            _ => None,
        }
    }

    /// Frame of the feature.
    pub fn frame(&self) -> &str {
        &self.frame
    }

    /// Attribute of feature
    pub fn attributes(&self) -> &HashMap<String, String> {
        &self.attributes
    }

    /// Get mutable reference on seqname of feature.
    pub fn seqname_mut(&mut self) -> &mut str {
        return &mut *self.seqname;
    }

    /// Get mutable reference on source of feature.
    pub fn source_mut(&mut self) -> &mut str {
        return &mut *self.source;
    }

    /// Get mutable reference on type of feature.
    pub fn feature_type_mut(&mut self) -> &mut str {
        return &mut *self.feature_type;
    }

    /// Get mutable reference on start of feature.
    pub fn start_mut(&mut self) -> &mut u64 {
        return &mut self.start;
    }

    /// Get mutable reference on end of feature.
    pub fn end_mut(&mut self) -> &mut u64 {
        return &mut self.end;
    }

    /// Get mutable reference on score of feature.
    pub fn score_mut(&mut self) -> &mut str {
        return &mut self.score;
    }

    /// Get mutable reference on strand of feature.
    pub fn strand_mut(&mut self) -> &mut str {
        return &mut self.strand;
    }

    /// Get mutable reference on attributes of feature.
    pub fn attributes_mut(&mut self) -> &mut HashMap<String, String> {
        return &mut self.attributes;
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use io::Strand;
    use std::collections::HashMap;

    const GFF_FILE: &'static [u8] = b"P0A7B8\tUniProtKB\tInitiator methionine\t1\t1\t.\t.\t.\tNote=Removed;ID=test
P0A7B8\tUniProtKB\tChain\t2\t176\t50\t+\t.\tNote=ATP-dependent protease subunit HslV;ID=PRO_0000148105
";
    //required because HashMap iter on element randomly
    const GFF_FILE_NO_ATTRIB: &'static [u8] = b"P0A7B8\tUniProtKB\tInitiator methionine\t1\t1\t.\t.\t.\t
P0A7B8\tUniProtKB\tChain\t2\t176\t50\t+\t.\t
";

    #[test]
    fn test_reader() {
        let seqname = ["P0A7B8", "P0A7B8"];
        let source = ["UniProtKB", "UniProtKB"];
        let feature_type = ["Initiator methionine", "Chain"];
        let starts = [1, 2];
        let ends = [1, 176];
        let scores = [None, Some(50)];
        let strand = [None, Some(Strand::Forward)];
        let frame = [".", "."];
        let mut attributes = [HashMap::new(), HashMap::new()];
        attributes[0].insert("ID".to_owned(), "test".to_owned());
        attributes[0].insert("Note".to_owned(), "Removed".to_owned());
        attributes[1].insert("ID".to_owned(), "PRO_0000148105".to_owned());
        attributes[1].insert("Note".to_owned(), "ATP-dependent protease subunit HslV".to_owned());

        let mut reader = Reader::new(GFF_FILE);
        for (i, r) in reader.records().enumerate() {
            let record = r.ok().expect("Error reading record");
            assert_eq!(record.seqname(), seqname[i]);
            assert_eq!(record.source(), source[i]);
            assert_eq!(record.feature_type(), feature_type[i]);
            assert_eq!(record.start(), starts[i]);
            assert_eq!(record.end(), ends[i]);
            assert_eq!(record.score(), scores[i]);
            assert_eq!(record.strand(), strand[i]);
            assert_eq!(record.frame(), frame[i]);
            assert_eq!(record.attributes(), &attributes[i]);
        }
    }

    #[test]
    fn test_writer() {
        let mut reader = Reader::new(GFF_FILE_NO_ATTRIB);
        let mut writer = Writer::new(vec![]);
        for r in reader.records() {
            writer.write(r.ok().expect("Error reading record")).ok().expect("Error writing record");
        }
        assert_eq!(writer.inner.as_string(), String::from_utf8_lossy(GFF_FILE_NO_ATTRIB))
    }
}
<commit_msg>Correct error in test<commit_after>// Copyright 2016 Pierre Marijon.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! GFF3 format reading and writing.
//!
//! GFF2 definition : http://gmod.org/wiki/GFF2#The_GFF2_File_Format (not yet support)
//! GTF2 definition : http://mblab.wustl.edu/GTF2.html (not yet support)
//! GFF3 definition : http://gmod.org/wiki/GFF3#GFF3_Format
//!
//! # Example
//!
//! ```
//! use std::io;
//! use bio::io::gff;
//! let reader = gff::Reader::new(io::stdin());
//! ```

use std::io;
use std::fs;
use std::path::Path;
use std::convert::AsRef;
use std::collections::HashMap;

use csv;

use io::Strand;

/// A GFF reader.
pub struct Reader<R: io::Read> {
    inner: csv::Reader<R>,
}

impl Reader<fs::File> {
    /// Read GFF from given file path.
    pub fn from_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
        fs::File::open(path).map(Reader::new)
    }
}

impl<R: io::Read> Reader<R> {
    /// Create a new GFF reader given an instance of `io::Read`.
    pub fn new(reader: R) -> Self {
        Reader {
            inner: csv::Reader::from_reader(reader).delimiter(b'\t').has_headers(false)
        }
    }

    /// Iterate over all records.
    pub fn records(&mut self) -> Records<R> {
        Records { inner: self.inner.decode() }
    }
}

/// A GFF record.
pub struct Records<'a, R: 'a + io::Read> {
    inner: csv::DecodedRecords<'a, R, (String, String, String, u64, u64, String, String, String, String)>,
}

impl<'a, R: io::Read> Iterator for Records<'a, R> {
    type Item = csv::Result<Record>;

    fn next(&mut self) -> Option<csv::Result<Record>> {
        self.inner.next().map(|res| {
            res.map(|(seqname, source, feature_type, start, end, score, strand, frame, attributes)| {
                Record {
                    seqname: seqname,
                    source: source,
                    feature_type: feature_type,
                    start: start,
                    end: end,
                    score: score,
                    strand: strand,
                    frame: frame,
                    attributes: csv::Reader::from_string(attributes)
                        .delimiter(b'=')
                        .record_terminator(csv::RecordTerminator::Any(b';'))
                        .has_headers(false)
                        .decode().collect::<csv::Result<HashMap<String, String>>>().unwrap(),
                }
            })
        })
    }
}

/// A GFF writer.
pub struct Writer<W: io::Write> {
    inner: csv::Writer<W>,
}

impl Writer<fs::File> {
    /// Write to a given file path.
    pub fn to_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
        fs::File::create(path).map(Writer::new)
    }
}

impl<W: io::Write> Writer<W> {
    /// Write to a given writer.
    pub fn new(writer: W) -> Self {
        Writer { inner: csv::Writer::from_writer(writer).delimiter(b'\t').flexible(true) }
    }

    /// Write a given GFF record.
    pub fn write(&mut self, record: Record) -> csv::Result<()> {
        let attributes;
        if !record.attributes.is_empty() {
            attributes = record.attributes.iter().map(|(a, b)| format!("{}={}", a, b)).collect::<Vec<_>>().join(";");
        } else {
            attributes = "".to_owned();
        }
        self.inner.encode((record.seqname, record.source, record.feature_type, record.start, record.end, record.score, record.strand, record.frame, attributes))
    }
}

/// A GFF record
#[derive(RustcEncodable)]
pub struct Record {
    seqname: String,
    source: String,
    feature_type: String,
    start: u64,
    end: u64,
    score: String,
    strand: String,
    frame: String,
    attributes: HashMap<String, String>,
}

impl Record {
    /// Create a new GFF record.
    pub fn new() -> Self {
        Record {
            seqname: "".to_owned(),
            source: "".to_owned(),
            feature_type: "".to_owned(),
            start: 0,
            end: 0,
            score: ".".to_owned(),
            strand: ".".to_owned(),
            frame: "".to_owned(),
            attributes: HashMap::<String, String>::new(),
        }
    }

    /// Sequence name of the feature.
    pub fn seqname(&self) -> &str {
        &self.seqname
    }

    /// Source of the feature.
    pub fn source(&self) -> &str {
        &self.source
    }

    /// Type of the feature.
    pub fn feature_type(&self) -> &str {
        &self.feature_type
    }

    /// Start position of feature (1-based).
    pub fn start(&self) -> &u64 {
        &self.start
    }

    /// End position of feature (1-based, not included).
    pub fn end(&self) -> &u64 {
        &self.end
    }

    /// Score of feature
    pub fn score(&self) -> Option<u64> {
        match self.score.as_ref() {
            "." => None,
            _ => self.score.parse::<u64>().ok(),
        }
    }

    /// Strand of the feature.
    pub fn strand(&self) -> Option<Strand> {
        match self.strand.as_ref() {
            "+" => Some(Strand::Forward),
            "-" => Some(Strand::Reverse),
            _ => None,
        }
    }

    /// Frame of the feature.
    pub fn frame(&self) -> &str {
        &self.frame
    }

    /// Attribute of feature
    pub fn attributes(&self) -> &HashMap<String, String> {
        &self.attributes
    }

    /// Get mutable reference on seqname of feature.
    pub fn seqname_mut(&mut self) -> &mut str {
        return &mut *self.seqname;
    }

    /// Get mutable reference on source of feature.
    pub fn source_mut(&mut self) -> &mut str {
        return &mut *self.source;
    }

    /// Get mutable reference on type of feature.
    pub fn feature_type_mut(&mut self) -> &mut str {
        return &mut *self.feature_type;
    }

    /// Get mutable reference on start of feature.
    pub fn start_mut(&mut self) -> &mut u64 {
        return &mut self.start;
    }

    /// Get mutable reference on end of feature.
    pub fn end_mut(&mut self) -> &mut u64 {
        return &mut self.end;
    }

    /// Get mutable reference on score of feature.
    pub fn score_mut(&mut self) -> &mut str {
        return &mut self.score;
    }

    /// Get mutable reference on strand of feature.
    pub fn strand_mut(&mut self) -> &mut str {
        return &mut self.strand;
    }

    /// Get mutable reference on attributes of feature.
    pub fn attributes_mut(&mut self) -> &mut HashMap<String, String> {
        return &mut self.attributes;
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use io::Strand;
    use std::collections::HashMap;

    const GFF_FILE: &'static [u8] = b"P0A7B8\tUniProtKB\tInitiator methionine\t1\t1\t.\t.\t.\tNote=Removed;ID=test
P0A7B8\tUniProtKB\tChain\t2\t176\t50\t+\t.\tNote=ATP-dependent protease subunit HslV;ID=PRO_0000148105
";
    //required because HashMap iter on element randomly
    const GFF_FILE_NO_ATTRIB: &'static [u8] = b"P0A7B8\tUniProtKB\tInitiator methionine\t1\t1\t.\t.\t.\t
P0A7B8\tUniProtKB\tChain\t2\t176\t50\t+\t.\t
";

    #[test]
    fn test_reader() {
        let seqname = ["P0A7B8", "P0A7B8"];
        let source = ["UniProtKB", "UniProtKB"];
        let feature_type = ["Initiator methionine", "Chain"];
        let starts = [1, 2];
        let ends = [1, 176];
        let scores = [None, Some(50)];
        let strand = [None, Some(Strand::Forward)];
        let frame = [".", "."];
        let mut attributes = [HashMap::new(), HashMap::new()];
        attributes[0].insert("ID".to_owned(), "test".to_owned());
        attributes[0].insert("Note".to_owned(), "Removed".to_owned());
        attributes[1].insert("ID".to_owned(), "PRO_0000148105".to_owned());
        attributes[1].insert("Note".to_owned(), "ATP-dependent protease subunit HslV".to_owned());

        let mut reader = Reader::new(GFF_FILE);
        for (i, r) in reader.records().enumerate() {
            let record = r.ok().expect("Error reading record");
            assert_eq!(record.seqname(), seqname[i]);
            assert_eq!(record.source(), source[i]);
            assert_eq!(record.feature_type(), feature_type[i]);
            assert_eq!(*record.start(), starts[i]);
            assert_eq!(*record.end(), ends[i]);
            assert_eq!(record.score(), scores[i]);
            assert_eq!(record.strand(), strand[i]);
            assert_eq!(record.frame(), frame[i]);
            assert_eq!(record.attributes(), &attributes[i]);
        }
    }

    #[test]
    fn test_writer() {
        let mut reader = Reader::new(GFF_FILE_NO_ATTRIB);
        let mut writer = Writer::new(vec![]);
        for r in reader.records() {
            writer.write(r.ok().expect("Error reading record")).ok().expect("Error writing record");
        }
        assert_eq!(writer.inner.as_string(), String::from_utf8_lossy(GFF_FILE_NO_ATTRIB))
    }
}
<|endoftext|>


<commit_before>mod pascal_str;
mod pascal_string;

pub use self::pascal_str::{Chars, CharsMut, Lines, PascalStr};
pub use self::pascal_string::PascalString;
<commit_msg>Improve utf8 mod.rs<commit_after>#![allow(missing_docs, unused_variables)]

mod pascal_str;
mod pascal_string;

pub use self::pascal_str::{Chars, CharsMut, Lines, PascalStr};
pub use self::pascal_string::PascalString;

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