<commit_before>// Copyright 2020 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 crate::{
    conversion::codegen_cpp::AdditionalNeed,
    conversion::{
        api::{TypeApiDetails, UnanalyzedApi},
        codegen_cpp::type_to_cpp::type_to_cpp,
        ConvertError,
    },
    known_types::KNOWN_TYPES,
    types::{make_ident, Namespace, TypeName},
};
use std::collections::{HashMap, HashSet};
use syn::{
    parse_quote, punctuated::Punctuated, GenericArgument, PathArguments, PathSegment, Type,
    TypePath, TypePtr,
};

/// Results of some type conversion, annotated with a list of every type encountered,
/// and optionally any extra APIs we need in order to use this type.
pub(crate) struct Annotated<T> {
    pub(crate) ty: T,
    pub(crate) types_encountered: HashSet<TypeName>,
    pub(crate) extra_apis: Vec<UnanalyzedApi>,
    pub(crate) requires_unsafe: bool,
}

impl<T> Annotated<T> {
    fn new(
        ty: T,
        types_encountered: HashSet<TypeName>,
        extra_apis: Vec<UnanalyzedApi>,
        requires_unsafe: bool,
    ) -> Self {
        Self {
            ty,
            types_encountered,
            extra_apis,
            requires_unsafe,
        }
    }

    fn map<T2, F: FnOnce(T) -> T2>(self, fun: F) -> Annotated<T2> {
        Annotated {
            ty: fun(self.ty),
            types_encountered: self.types_encountered,
            extra_apis: self.extra_apis,
            requires_unsafe: self.requires_unsafe,
        }
    }
}

/// A type which can convert from a type encountered in `bindgen`
/// output to the sort of type we should represeent to `cxx`.
/// As a simple example, `std::string` should be replaced
/// with [CxxString]. This also involves keeping track
/// of typedefs, and any instantiated concrete types.
///
/// This object is a bit of a pest. The information here
/// is compiled during the parsing phase (which is why it lives
/// in the parse mod) but is used during various other phases.
/// As such it contributes to both the parsing and analysis phases.
/// It's possible that the information here largely duplicates
/// information stored elsewhere in the list of `Api`s, or can
/// easily be moved into it, which would enable us to
/// distribute this logic elsewhere.
pub(crate) struct TypeConverter {
    types_found: Vec<TypeName>,
    typedefs: HashMap<TypeName, Type>,
    concrete_templates: HashMap<String, TypeName>,
}

impl TypeConverter {
    pub(crate) fn new() -> Self {
        Self {
            types_found: Vec::new(),
            typedefs: HashMap::new(),
            concrete_templates: HashMap::new(),
        }
    }

    pub(crate) fn push(&mut self, ty: TypeName) {
        self.types_found.push(ty);
    }

    pub(crate) fn insert_typedef(&mut self, id: TypeName, target: Type) {
        self.typedefs.insert(id, target);
    }

    pub(crate) fn convert_boxed_type(
        &mut self,
        ty: Box<Type>,
        ns: &Namespace,
        convert_ptrs_to_reference: bool,
    ) -> Result<Annotated<Box<Type>>, ConvertError> {
        Ok(self
            .convert_type(*ty, ns, convert_ptrs_to_reference)?
            .map(Box::new))
    }

    pub(crate) fn convert_type(
        &mut self,
        ty: Type,
        ns: &Namespace,
        convert_ptrs_to_reference: bool,
    ) -> Result<Annotated<Type>, ConvertError> {
        let result = match ty {
            Type::Path(p) => {
                let newp = self.convert_type_path(p, ns)?;
                if let Type::Path(newpp) = &newp.ty {
                    // Special handling because rust_Str (as emitted by bindgen)
                    // doesn't simply get renamed to a different type _identifier_.
                    // This plain type-by-value (as far as bindgen is concerned)
                    // is actually a &str.
                    if KNOWN_TYPES.should_dereference_in_cpp(newpp) {
                        Annotated::new(
                            Type::Reference(parse_quote! {
                                &str
                            }),
                            newp.types_encountered,
                            newp.extra_apis,
                            false,
                        )
                    } else {
                        newp
                    }
                } else {
                    newp
                }
            }
            Type::Reference(mut r) => {
                let innerty = self.convert_boxed_type(r.elem, ns, false)?;
                r.elem = innerty.ty;
                Annotated::new(
                    Type::Reference(r),
                    innerty.types_encountered,
                    innerty.extra_apis,
                    false,
                )
            }
            Type::Ptr(ptr) if convert_ptrs_to_reference => {
                self.convert_ptr_to_reference(ptr, ns)?
            }
            Type::Ptr(mut ptr) => {
                let innerty = self.convert_boxed_type(ptr.elem, ns, false)?;
                ptr.elem = innerty.ty;
                Annotated::new(
                    Type::Ptr(ptr),
                    innerty.types_encountered,
                    innerty.extra_apis,
                    true,
                )
            }
            _ => Annotated::new(ty, HashSet::new(), Vec::new(), false),
        };
        Ok(result)
    }

    fn convert_type_path(
        &mut self,
        mut typ: TypePath,
        ns: &Namespace,
    ) -> Result<Annotated<Type>, ConvertError> {
        // First, qualify any unqualified paths.
        if typ.path.segments.iter().next().unwrap().ident != "root" {
            let ty = TypeName::from_type_path(&typ);
            // If the type looks like it is unqualified, check we know it
            // already, and if not, qualify it according to the current
            // namespace. This is a bit of a shortcut compared to having a full
            // resolution pass which can search all known namespaces.
            if !KNOWN_TYPES.is_known_type(&ty) {
                let num_segments = typ.path.segments.len();
                if num_segments > 1 {
                    return Err(ConvertError::UnsupportedBuiltInType(ty));
                }
                if !self.types_found.contains(&ty) {
                    typ.path.segments = std::iter::once(&"root".to_string())
                        .chain(ns.iter())
                        .map(|s| {
                            let i = make_ident(s);
                            parse_quote! { #i }
                        })
                        .chain(typ.path.segments.into_iter())
                        .collect();
                }
            }
        }

        let original_tn = TypeName::from_type_path(&typ);
        let mut deps = HashSet::new();

        // Now convert this type itself.
        deps.insert(original_tn.clone());
        // First let's see if this is a typedef.
        let (typ, tn) = match self.resolve_typedef(&original_tn) {
            None => (typ, original_tn),
            Some(Type::Path(resolved_tp)) => {
                let resolved_tn = TypeName::from_type_path(&resolved_tp);
                deps.insert(resolved_tn.clone());
                (resolved_tp.clone(), resolved_tn)
            }
            Some(other) => {
                return Ok(Annotated::new(
                    other.clone(),
                    HashSet::new(),
                    Vec::new(),
                    false,
                ))
            }
        };

        // Now let's see if it's a known type.
        let mut typ = match KNOWN_TYPES.known_type_substitute_path(&typ) {
            Some(mut substitute_type) => {
                if let Some(last_seg_args) =
                    typ.path.segments.into_iter().last().map(|ps| ps.arguments)
                {
                    let last_seg = substitute_type.path.segments.last_mut().unwrap();
                    last_seg.arguments = last_seg_args;
                }
                substitute_type
            }
            None => typ,
        };

        let mut extra_apis = Vec::new();

        // Finally let's see if it's generic.
        if Self::is_generic(&typ) {
            if KNOWN_TYPES.is_cxx_acceptable_generic(&tn) {
                // this is a type of generic understood by cxx (e.g. CxxVector)
                // so let's convert any generic type arguments. This recurses.
                typ.path.segments = typ
                    .path
                    .segments
                    .into_iter()
                    .map(|s| -> Result<PathSegment, ConvertError> {
                        let ident = &s.ident;
                        let args = match s.arguments {
                            PathArguments::AngleBracketed(mut ab) => {
                                let mut innerty = self.convert_punctuated(ab.args, ns)?;
                                ab.args = innerty.ty;
                                deps.extend(innerty.types_encountered.drain());
                                PathArguments::AngleBracketed(ab)
                            }
                            _ => s.arguments,
                        };
                        Ok(parse_quote!( #ident #args ))
                    })
                    .collect::<Result<_, _>>()?;
            } else {
                // Oh poop. It's a generic type which cxx won't be able to handle.
                // We'll have to come up with a concrete type in both the cxx::bridge (in Rust)
                // and a corresponding typedef in C++.
                let (new_tn, api) = self.get_templated_typename(&Type::Path(typ))?;
                extra_apis.extend(api.into_iter());
                deps.remove(&tn);
                typ = new_tn.to_type_path();
                deps.insert(new_tn);
            }
        };
        Ok(Annotated::new(Type::Path(typ), deps, extra_apis, false))
    }

    fn is_generic(typ: &TypePath) -> bool {
        match typ.path.segments.last() {
            None => false,
            Some(s) => !s.arguments.is_empty(),
        }
    }

    fn convert_punctuated<P>(
        &mut self,
        pun: Punctuated<GenericArgument, P>,
        ns: &Namespace,
    ) -> Result<Annotated<Punctuated<GenericArgument, P>>, ConvertError>
    where
        P: Default,
    {
        let mut new_pun = Punctuated::new();
        let mut types_encountered = HashSet::new();
        let mut extra_apis = Vec::new();
        for arg in pun.into_iter() {
            new_pun.push(match arg {
                GenericArgument::Type(t) => {
                    let mut innerty = self.convert_type(t, ns, false)?;
                    types_encountered.extend(innerty.types_encountered.drain());
                    extra_apis.extend(innerty.extra_apis.drain(..));
                    GenericArgument::Type(innerty.ty)
                }
                _ => arg,
            });
        }
        Ok(Annotated::new(
            new_pun,
            types_encountered,
            extra_apis,
            false,
        ))
    }

    fn resolve_typedef<'b>(&'b self, tn: &TypeName) -> Option<&'b Type> {
        self.typedefs.get(&tn).map(|resolution| match resolution {
            Type::Path(typ) => {
                let tn = TypeName::from_type_path(typ);
                self.resolve_typedef(&tn).unwrap_or(resolution)
            }
            _ => resolution,
        })
    }

    fn convert_ptr_to_reference(
        &mut self,
        ptr: TypePtr,
        ns: &Namespace,
    ) -> Result<Annotated<Type>, ConvertError> {
        let mutability = ptr.mutability;
        let elem = self.convert_boxed_type(ptr.elem, ns, false)?;
        // TODO - in the future, we should check if this is a rust::Str and throw
        // a wobbler if not. rust::Str should only be seen _by value_ in C++
        // headers; it manifests as &str in Rust but on the C++ side it must
        // be a plain value. We should detect and abort.
        Ok(elem.map(|elem| match mutability {
            Some(_) => Type::Path(parse_quote! {
                std::pin::Pin < & #mutability #elem >
            }),
            None => Type::Reference(parse_quote! {
                & #elem
            }),
        }))
    }

    fn add_concrete_type(&self, tyname: &TypeName, rs_definition: &Type) -> UnanalyzedApi {
        let final_ident = make_ident(tyname.get_final_ident());
        let mut fulltypath: Vec<_> = ["bindgen", "root"].iter().map(make_ident).collect();
        fulltypath.push(final_ident.clone());
        let tynamestring = tyname.to_cpp_name();
        UnanalyzedApi {
            ns: tyname.get_namespace().clone(),
            id: final_ident.clone(),
            deps: HashSet::new(),
            detail: crate::conversion::api::ApiDetail::ConcreteType {
                ty_details: TypeApiDetails {
                    fulltypath,
                    final_ident,
                    tynamestring,
                },
                additional_cpp: AdditionalNeed::ConcreteTemplatedTypeTypedef(
                    tyname.clone(),
                    Box::new(rs_definition.clone()),
                ),
            },
        }
    }

    fn get_templated_typename(
        &mut self,
        rs_definition: &Type,
    ) -> Result<(TypeName, Option<UnanalyzedApi>), ConvertError> {
        let count = self.concrete_templates.len();
        // We just use this as a hash key, essentially.
        let cpp_definition = type_to_cpp(rs_definition)?;
        let e = self.concrete_templates.get(&cpp_definition);
        match e {
            Some(tn) => Ok((tn.clone(), None)),
            None => {
                let tn = TypeName::new(&Namespace::new(), &format!("AutocxxConcrete{}", count));
                self.concrete_templates
                    .insert(cpp_definition.clone(), tn.clone());
                let api = self.add_concrete_type(&tn, rs_definition);
                Ok((tn, Some(api)))
            }
        }
    }
}
<commit_msg>Simplify generic type conversion.<commit_after>// Copyright 2020 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 crate::{
    conversion::codegen_cpp::AdditionalNeed,
    conversion::{
        api::{TypeApiDetails, UnanalyzedApi},
        codegen_cpp::type_to_cpp::type_to_cpp,
        ConvertError,
    },
    known_types::KNOWN_TYPES,
    types::{make_ident, Namespace, TypeName},
};
use std::collections::{HashMap, HashSet};
use syn::{
    parse_quote, punctuated::Punctuated, GenericArgument, PathArguments, Type, TypePath, TypePtr,
};

/// Results of some type conversion, annotated with a list of every type encountered,
/// and optionally any extra APIs we need in order to use this type.
pub(crate) struct Annotated<T> {
    pub(crate) ty: T,
    pub(crate) types_encountered: HashSet<TypeName>,
    pub(crate) extra_apis: Vec<UnanalyzedApi>,
    pub(crate) requires_unsafe: bool,
}

impl<T> Annotated<T> {
    fn new(
        ty: T,
        types_encountered: HashSet<TypeName>,
        extra_apis: Vec<UnanalyzedApi>,
        requires_unsafe: bool,
    ) -> Self {
        Self {
            ty,
            types_encountered,
            extra_apis,
            requires_unsafe,
        }
    }

    fn map<T2, F: FnOnce(T) -> T2>(self, fun: F) -> Annotated<T2> {
        Annotated {
            ty: fun(self.ty),
            types_encountered: self.types_encountered,
            extra_apis: self.extra_apis,
            requires_unsafe: self.requires_unsafe,
        }
    }
}

/// A type which can convert from a type encountered in `bindgen`
/// output to the sort of type we should represeent to `cxx`.
/// As a simple example, `std::string` should be replaced
/// with [CxxString]. This also involves keeping track
/// of typedefs, and any instantiated concrete types.
///
/// This object is a bit of a pest. The information here
/// is compiled during the parsing phase (which is why it lives
/// in the parse mod) but is used during various other phases.
/// As such it contributes to both the parsing and analysis phases.
/// It's possible that the information here largely duplicates
/// information stored elsewhere in the list of `Api`s, or can
/// easily be moved into it, which would enable us to
/// distribute this logic elsewhere.
pub(crate) struct TypeConverter {
    types_found: Vec<TypeName>,
    typedefs: HashMap<TypeName, Type>,
    concrete_templates: HashMap<String, TypeName>,
}

impl TypeConverter {
    pub(crate) fn new() -> Self {
        Self {
            types_found: Vec::new(),
            typedefs: HashMap::new(),
            concrete_templates: HashMap::new(),
        }
    }

    pub(crate) fn push(&mut self, ty: TypeName) {
        self.types_found.push(ty);
    }

    pub(crate) fn insert_typedef(&mut self, id: TypeName, target: Type) {
        self.typedefs.insert(id, target);
    }

    pub(crate) fn convert_boxed_type(
        &mut self,
        ty: Box<Type>,
        ns: &Namespace,
        convert_ptrs_to_reference: bool,
    ) -> Result<Annotated<Box<Type>>, ConvertError> {
        Ok(self
            .convert_type(*ty, ns, convert_ptrs_to_reference)?
            .map(Box::new))
    }

    pub(crate) fn convert_type(
        &mut self,
        ty: Type,
        ns: &Namespace,
        convert_ptrs_to_reference: bool,
    ) -> Result<Annotated<Type>, ConvertError> {
        let result = match ty {
            Type::Path(p) => {
                let newp = self.convert_type_path(p, ns)?;
                if let Type::Path(newpp) = &newp.ty {
                    // Special handling because rust_Str (as emitted by bindgen)
                    // doesn't simply get renamed to a different type _identifier_.
                    // This plain type-by-value (as far as bindgen is concerned)
                    // is actually a &str.
                    if KNOWN_TYPES.should_dereference_in_cpp(newpp) {
                        Annotated::new(
                            Type::Reference(parse_quote! {
                                &str
                            }),
                            newp.types_encountered,
                            newp.extra_apis,
                            false,
                        )
                    } else {
                        newp
                    }
                } else {
                    newp
                }
            }
            Type::Reference(mut r) => {
                let innerty = self.convert_boxed_type(r.elem, ns, false)?;
                r.elem = innerty.ty;
                Annotated::new(
                    Type::Reference(r),
                    innerty.types_encountered,
                    innerty.extra_apis,
                    false,
                )
            }
            Type::Ptr(ptr) if convert_ptrs_to_reference => {
                self.convert_ptr_to_reference(ptr, ns)?
            }
            Type::Ptr(mut ptr) => {
                let innerty = self.convert_boxed_type(ptr.elem, ns, false)?;
                ptr.elem = innerty.ty;
                Annotated::new(
                    Type::Ptr(ptr),
                    innerty.types_encountered,
                    innerty.extra_apis,
                    true,
                )
            }
            _ => Annotated::new(ty, HashSet::new(), Vec::new(), false),
        };
        Ok(result)
    }

    fn convert_type_path(
        &mut self,
        mut typ: TypePath,
        ns: &Namespace,
    ) -> Result<Annotated<Type>, ConvertError> {
        // First, qualify any unqualified paths.
        if typ.path.segments.iter().next().unwrap().ident != "root" {
            let ty = TypeName::from_type_path(&typ);
            // If the type looks like it is unqualified, check we know it
            // already, and if not, qualify it according to the current
            // namespace. This is a bit of a shortcut compared to having a full
            // resolution pass which can search all known namespaces.
            if !KNOWN_TYPES.is_known_type(&ty) {
                let num_segments = typ.path.segments.len();
                if num_segments > 1 {
                    return Err(ConvertError::UnsupportedBuiltInType(ty));
                }
                if !self.types_found.contains(&ty) {
                    typ.path.segments = std::iter::once(&"root".to_string())
                        .chain(ns.iter())
                        .map(|s| {
                            let i = make_ident(s);
                            parse_quote! { #i }
                        })
                        .chain(typ.path.segments.into_iter())
                        .collect();
                }
            }
        }

        let original_tn = TypeName::from_type_path(&typ);
        let mut deps = HashSet::new();

        // Now convert this type itself.
        deps.insert(original_tn.clone());
        // First let's see if this is a typedef.
        let (typ, tn) = match self.resolve_typedef(&original_tn) {
            None => (typ, original_tn),
            Some(Type::Path(resolved_tp)) => {
                let resolved_tn = TypeName::from_type_path(&resolved_tp);
                deps.insert(resolved_tn.clone());
                (resolved_tp.clone(), resolved_tn)
            }
            Some(other) => {
                return Ok(Annotated::new(
                    other.clone(),
                    HashSet::new(),
                    Vec::new(),
                    false,
                ))
            }
        };

        // Now let's see if it's a known type.
        let mut typ = match KNOWN_TYPES.known_type_substitute_path(&typ) {
            Some(mut substitute_type) => {
                if let Some(last_seg_args) =
                    typ.path.segments.into_iter().last().map(|ps| ps.arguments)
                {
                    let last_seg = substitute_type.path.segments.last_mut().unwrap();
                    last_seg.arguments = last_seg_args;
                }
                substitute_type
            }
            None => typ,
        };

        let mut extra_apis = Vec::new();

        // Finally let's see if it's generic.
        if Self::is_generic(&typ) {
            if KNOWN_TYPES.is_cxx_acceptable_generic(&tn) {
                // this is a type of generic understood by cxx (e.g. CxxVector)
                // so let's convert any generic type arguments. This recurses.
                let s = typ.path.segments.last_mut().unwrap();
                if let PathArguments::AngleBracketed(ref mut ab) = s.arguments {
                    let mut innerty = self.convert_punctuated(ab.args.clone(), ns)?;
                    ab.args = innerty.ty;
                    deps.extend(innerty.types_encountered.drain());
                }
            } else {
                // Oh poop. It's a generic type which cxx won't be able to handle.
                // We'll have to come up with a concrete type in both the cxx::bridge (in Rust)
                // and a corresponding typedef in C++.
                let (new_tn, api) = self.get_templated_typename(&Type::Path(typ))?;
                extra_apis.extend(api.into_iter());
                deps.remove(&tn);
                typ = new_tn.to_type_path();
                deps.insert(new_tn);
            }
        };
        Ok(Annotated::new(Type::Path(typ), deps, extra_apis, false))
    }

    fn is_generic(typ: &TypePath) -> bool {
        match typ.path.segments.last() {
            None => false,
            Some(s) => !s.arguments.is_empty(),
        }
    }

    fn convert_punctuated<P>(
        &mut self,
        pun: Punctuated<GenericArgument, P>,
        ns: &Namespace,
    ) -> Result<Annotated<Punctuated<GenericArgument, P>>, ConvertError>
    where
        P: Default,
    {
        let mut new_pun = Punctuated::new();
        let mut types_encountered = HashSet::new();
        let mut extra_apis = Vec::new();
        for arg in pun.into_iter() {
            new_pun.push(match arg {
                GenericArgument::Type(t) => {
                    let mut innerty = self.convert_type(t, ns, false)?;
                    types_encountered.extend(innerty.types_encountered.drain());
                    extra_apis.extend(innerty.extra_apis.drain(..));
                    GenericArgument::Type(innerty.ty)
                }
                _ => arg,
            });
        }
        Ok(Annotated::new(
            new_pun,
            types_encountered,
            extra_apis,
            false,
        ))
    }

    fn resolve_typedef<'b>(&'b self, tn: &TypeName) -> Option<&'b Type> {
        self.typedefs.get(&tn).map(|resolution| match resolution {
            Type::Path(typ) => {
                let tn = TypeName::from_type_path(typ);
                self.resolve_typedef(&tn).unwrap_or(resolution)
            }
            _ => resolution,
        })
    }

    fn convert_ptr_to_reference(
        &mut self,
        ptr: TypePtr,
        ns: &Namespace,
    ) -> Result<Annotated<Type>, ConvertError> {
        let mutability = ptr.mutability;
        let elem = self.convert_boxed_type(ptr.elem, ns, false)?;
        // TODO - in the future, we should check if this is a rust::Str and throw
        // a wobbler if not. rust::Str should only be seen _by value_ in C++
        // headers; it manifests as &str in Rust but on the C++ side it must
        // be a plain value. We should detect and abort.
        Ok(elem.map(|elem| match mutability {
            Some(_) => Type::Path(parse_quote! {
                std::pin::Pin < & #mutability #elem >
            }),
            None => Type::Reference(parse_quote! {
                & #elem
            }),
        }))
    }

    fn add_concrete_type(&self, tyname: &TypeName, rs_definition: &Type) -> UnanalyzedApi {
        let final_ident = make_ident(tyname.get_final_ident());
        let mut fulltypath: Vec<_> = ["bindgen", "root"].iter().map(make_ident).collect();
        fulltypath.push(final_ident.clone());
        let tynamestring = tyname.to_cpp_name();
        UnanalyzedApi {
            ns: tyname.get_namespace().clone(),
            id: final_ident.clone(),
            deps: HashSet::new(),
            detail: crate::conversion::api::ApiDetail::ConcreteType {
                ty_details: TypeApiDetails {
                    fulltypath,
                    final_ident,
                    tynamestring,
                },
                additional_cpp: AdditionalNeed::ConcreteTemplatedTypeTypedef(
                    tyname.clone(),
                    Box::new(rs_definition.clone()),
                ),
            },
        }
    }

    fn get_templated_typename(
        &mut self,
        rs_definition: &Type,
    ) -> Result<(TypeName, Option<UnanalyzedApi>), ConvertError> {
        let count = self.concrete_templates.len();
        // We just use this as a hash key, essentially.
        let cpp_definition = type_to_cpp(rs_definition)?;
        let e = self.concrete_templates.get(&cpp_definition);
        match e {
            Some(tn) => Ok((tn.clone(), None)),
            None => {
                let tn = TypeName::new(&Namespace::new(), &format!("AutocxxConcrete{}", count));
                self.concrete_templates
                    .insert(cpp_definition.clone(), tn.clone());
                let api = self.add_concrete_type(&tn, rs_definition);
                Ok((tn, Some(api)))
            }
        }
    }
}
<|endoftext|>


<commit_before>#![feature(arc_counts)]

extern crate syscall;

use std::collections::{BTreeMap, VecDeque};
use std::fs::File;
use std::io::{Read, Write};
use std::{str, thread};
use std::sync::{Arc, Weak, Mutex};

use syscall::data::Packet;
use syscall::error::{Error, Result, EBADF, EINVAL, ENOENT, EPIPE, EWOULDBLOCK};
use syscall::flag::O_NONBLOCK;
use syscall::scheme::SchemeMut;

pub struct PtyScheme {
    next_id: usize,
    ptys: (BTreeMap<usize, PtyMaster>, BTreeMap<usize, PtySlave>)
}

impl PtyScheme {
    fn new() -> Self {
        PtyScheme {
            next_id: 0,
            ptys: (BTreeMap::new(), BTreeMap::new())
        }
    }
}

impl SchemeMut for PtyScheme {
    fn open(&mut self, path: &[u8], flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
        let path = str::from_utf8(path).or(Err(Error::new(EINVAL)))?.trim_matches('/');

        if path.is_empty() {
            let id = self.next_id;
            self.next_id += 1;

            self.ptys.0.insert(id, PtyMaster::new(id, flags));

            Ok(id)
        } else {
            let master_id = path.parse::<usize>().or(Err(Error::new(EINVAL)))?;
            let master = self.ptys.0.get(&master_id).map(|pipe| pipe.clone()).ok_or(Error::new(ENOENT))?;

            let id = self.next_id;
            self.next_id += 1;

            self.ptys.1.insert(id, PtySlave::new(&master, flags));

            Ok(id)
        }
    }

    fn dup(&mut self, id: usize, _buf: &[u8]) -> Result<usize> {
        let master_opt = self.ptys.0.get(&id).map(|pipe| pipe.clone());
        if let Some(pipe) = master_opt {
            let pipe_id = self.next_id;
            self.next_id += 1;
            self.ptys.0.insert(pipe_id, pipe);
            return Ok(pipe_id);
        }

        let slave_opt = self.ptys.1.get(&id).map(|pipe| pipe.clone());
        if let Some(pipe) = slave_opt {
            let pipe_id = self.next_id;
            self.next_id += 1;
            self.ptys.1.insert(pipe_id, pipe);
            return Ok(pipe_id);
        }

        Err(Error::new(EBADF))
    }

    fn read(&mut self, id: usize, buf: &mut [u8]) -> Result<usize> {
        let master_opt = self.ptys.0.get(&id).map(|pipe| pipe.clone());
        if let Some(pipe) = master_opt {
            return pipe.read(buf);
        }

        let slave_opt = self.ptys.1.get(&id).map(|pipe| pipe.clone());
        if let Some(pipe) = slave_opt {
            return pipe.read(buf);
        }

        Err(Error::new(EBADF))
    }

    fn write(&mut self, id: usize, buf: &[u8]) -> Result<usize> {
        let master_opt = self.ptys.0.get(&id).map(|pipe| pipe.clone());
        if let Some(pipe) = master_opt {
            return pipe.write(buf);
        }

        let slave_opt = self.ptys.1.get(&id).map(|pipe| pipe.clone());
        if let Some(pipe) = slave_opt {
            return pipe.write(buf);
        }

        Err(Error::new(EBADF))
    }

    fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<usize> {
        let master_opt = self.ptys.0.get(&id).map(|pipe| pipe.clone());
        if let Some(pipe) = master_opt {
            return pipe.path(buf);
        }

        let slave_opt = self.ptys.1.get(&id).map(|pipe| pipe.clone());
        if let Some(pipe) = slave_opt {
            return pipe.path(buf);
        }

        Err(Error::new(EBADF))
    }

    fn fsync(&mut self, id: usize) -> Result<usize> {
        let slave_opt = self.ptys.1.get(&id).map(|pipe| pipe.clone());
        if let Some(pipe) = slave_opt {
            return pipe.sync();
        }

        Ok(0)
    }

    fn close(&mut self, id: usize) -> Result<usize> {
        drop(self.ptys.0.remove(&id));
        drop(self.ptys.1.remove(&id));

        Ok(0)
    }
}

/// Read side of a pipe
#[derive(Clone)]
pub struct PtyMaster {
    id: usize,
    flags: usize,
    read: Arc<Mutex<VecDeque<Vec<u8>>>>,
    write: Arc<Mutex<VecDeque<u8>>>,
}

impl PtyMaster {
    pub fn new(id: usize, flags: usize) -> Self {
        PtyMaster {
            id: id,
            flags: flags,
            read: Arc::new(Mutex::new(VecDeque::new())),
            write: Arc::new(Mutex::new(VecDeque::new())),
        }
    }

    fn path(&self, buf: &mut [u8]) -> Result<usize> {
        let path_str = format!("pty:{}", self.id);
        let path = path_str.as_bytes();

        let mut i = 0;
        while i < buf.len() && i < path.len() {
            buf[i] = path[i];
            i += 1;
        }

        Ok(i)
    }

    fn read(&self, buf: &mut [u8]) -> Result<usize> {
        let mut read = self.read.lock().unwrap();

        if let Some(packet) = read.pop_front() {
            let mut i = 0;

            while i < buf.len() && i < packet.len() {
                buf[i] = packet[i];
                i += 1;
            }

            Ok(i)
        } else if self.flags & O_NONBLOCK == O_NONBLOCK || Arc::weak_count(&self.read) == 0 {
            Ok(0)
        } else {
            Err(Error::new(EWOULDBLOCK))
        }
    }

    fn write(&self, buf: &[u8]) -> Result<usize> {
        let mut write = self.write.lock().unwrap();

        let mut i = 0;
        while i < buf.len() {
            write.push_back(buf[i]);
            i += 1;
        }

        Ok(i)
    }
}

/// Read side of a pipe
#[derive(Clone)]
pub struct PtySlave {
    master_id: usize,
    flags: usize,
    read: Weak<Mutex<VecDeque<u8>>>,
    write: Weak<Mutex<VecDeque<Vec<u8>>>>,
}

impl PtySlave {
    pub fn new(master: &PtyMaster, flags: usize) -> Self {
        PtySlave {
            master_id: master.id,
            flags: flags,
            read: Arc::downgrade(&master.write),
            write: Arc::downgrade(&master.read),
        }
    }

    fn path(&self, buf: &mut [u8]) -> Result<usize> {
        let path_str = format!("pty:{}", self.master_id);
        let path = path_str.as_bytes();

        let mut i = 0;
        while i < buf.len() && i < path.len() {
            buf[i] = path[i];
            i += 1;
        }

        Ok(i)
    }

    fn read(&self, buf: &mut [u8]) -> Result<usize> {
        if let Some(read_lock) = self.read.upgrade() {
            let mut read = read_lock.lock().unwrap();

            let mut i = 0;

            while i < buf.len() && ! read.is_empty() {
                buf[i] = read.pop_front().unwrap();
                i += 1;
            }

            if i > 0 || self.flags & O_NONBLOCK == O_NONBLOCK {
                Ok(i)
            } else {
                Err(Error::new(EWOULDBLOCK))
            }
        } else {
            Ok(0)
        }
    }

    fn write(&self, buf: &[u8]) -> Result<usize> {
        if let Some(write_lock) = self.write.upgrade() {
            let mut vec = Vec::new();
            vec.push(0);
            vec.extend_from_slice(buf);

            let mut write = write_lock.lock().unwrap();
            write.push_back(vec);

            Ok(buf.len())
        } else {
            Err(Error::new(EPIPE))
        }
    }

    fn sync(&self) -> Result<usize> {
        if let Some(write_lock) = self.write.upgrade() {
            let mut vec = Vec::new();
            vec.push(1);

            let mut write = write_lock.lock().unwrap();
            write.push_back(vec);

            Ok(0)
        } else {
            Err(Error::new(EPIPE))
        }
    }
}

fn main(){
    thread::spawn(move || {
        let mut socket = File::create(":pty").expect("pty: failed to create pty scheme");
        let mut scheme = PtyScheme::new();
        let mut todo = Vec::new();
        loop {
            let mut packet = Packet::default();
            socket.read(&mut packet).expect("pty: failed to read events from pty scheme");

            let a = packet.a;
            scheme.handle(&mut packet);
            if packet.a == (-EWOULDBLOCK) as usize {
                packet.a = a;
                todo.push(packet);
            } else {
                socket.write(&packet).expect("pty: failed to write responses to pty scheme");
            }

            let mut i = 0;
            while i < todo.len() {
                let a = todo[i].a;
                scheme.handle(&mut todo[i]);
                if todo[i].a == (-EWOULDBLOCK) as usize {
                    todo[i].a = a;
                    i += 1;
                } else {
                    let packet = todo.remove(i);
                    socket.write(&packet).expect("pty: failed to write responses to pty scheme");
                }
            }
        }
    });
}
<commit_msg>Event on close<commit_after>#![feature(rc_counts)]

extern crate syscall;

use std::cell::RefCell;
use std::collections::{BTreeMap, VecDeque};
use std::fs::File;
use std::io::{Read, Write};
use std::rc::{Rc, Weak};
use std::{str, thread};

use syscall::data::Packet;
use syscall::error::{Error, Result, EBADF, EINVAL, ENOENT, EPIPE, EWOULDBLOCK};
use syscall::flag::O_NONBLOCK;
use syscall::scheme::SchemeMut;

pub struct PtyScheme {
    next_id: usize,
    ptys: (BTreeMap<usize, PtyMaster>, BTreeMap<usize, PtySlave>)
}

impl PtyScheme {
    fn new() -> Self {
        PtyScheme {
            next_id: 0,
            ptys: (BTreeMap::new(), BTreeMap::new())
        }
    }
}

impl SchemeMut for PtyScheme {
    fn open(&mut self, path: &[u8], flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
        let path = str::from_utf8(path).or(Err(Error::new(EINVAL)))?.trim_matches('/');

        if path.is_empty() {
            let id = self.next_id;
            self.next_id += 1;

            self.ptys.0.insert(id, PtyMaster::new(id, flags));

            Ok(id)
        } else {
            let master_id = path.parse::<usize>().or(Err(Error::new(EINVAL)))?;
            let master = self.ptys.0.get(&master_id).map(|pipe| pipe.clone()).ok_or(Error::new(ENOENT))?;

            let id = self.next_id;
            self.next_id += 1;

            self.ptys.1.insert(id, PtySlave::new(&master, flags));

            Ok(id)
        }
    }

    fn dup(&mut self, id: usize, _buf: &[u8]) -> Result<usize> {
        let master_opt = self.ptys.0.get(&id).map(|pipe| pipe.clone());
        if let Some(pipe) = master_opt {
            let pipe_id = self.next_id;
            self.next_id += 1;
            self.ptys.0.insert(pipe_id, pipe);
            return Ok(pipe_id);
        }

        let slave_opt = self.ptys.1.get(&id).map(|pipe| pipe.clone());
        if let Some(pipe) = slave_opt {
            let pipe_id = self.next_id;
            self.next_id += 1;
            self.ptys.1.insert(pipe_id, pipe);
            return Ok(pipe_id);
        }

        Err(Error::new(EBADF))
    }

    fn read(&mut self, id: usize, buf: &mut [u8]) -> Result<usize> {
        let master_opt = self.ptys.0.get(&id).map(|pipe| pipe.clone());
        if let Some(pipe) = master_opt {
            return pipe.read(buf);
        }

        let slave_opt = self.ptys.1.get(&id).map(|pipe| pipe.clone());
        if let Some(pipe) = slave_opt {
            return pipe.read(buf);
        }

        Err(Error::new(EBADF))
    }

    fn write(&mut self, id: usize, buf: &[u8]) -> Result<usize> {
        let master_opt = self.ptys.0.get(&id).map(|pipe| pipe.clone());
        if let Some(pipe) = master_opt {
            return pipe.write(buf);
        }

        let slave_opt = self.ptys.1.get(&id).map(|pipe| pipe.clone());
        if let Some(pipe) = slave_opt {
            return pipe.write(buf);
        }

        Err(Error::new(EBADF))
    }

    fn fevent(&mut self, id: usize, _flags: usize) -> Result<usize> {
        if self.ptys.0.contains_key(&id) || self.ptys.1.contains_key(&id) {
            Ok(id)
        } else {
            Err(Error::new(EBADF))
        }
    }

    fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<usize> {
        let master_opt = self.ptys.0.get(&id).map(|pipe| pipe.clone());
        if let Some(pipe) = master_opt {
            return pipe.path(buf);
        }

        let slave_opt = self.ptys.1.get(&id).map(|pipe| pipe.clone());
        if let Some(pipe) = slave_opt {
            return pipe.path(buf);
        }

        Err(Error::new(EBADF))
    }

    fn fsync(&mut self, id: usize) -> Result<usize> {
        let slave_opt = self.ptys.1.get(&id).map(|pipe| pipe.clone());
        if let Some(pipe) = slave_opt {
            return pipe.sync();
        }

        Ok(0)
    }

    fn close(&mut self, id: usize) -> Result<usize> {
        drop(self.ptys.0.remove(&id));
        drop(self.ptys.1.remove(&id));

        Ok(0)
    }
}

/// Read side of a pipe
#[derive(Clone)]
pub struct PtyMaster {
    id: usize,
    flags: usize,
    read: Rc<RefCell<VecDeque<Vec<u8>>>>,
    write: Rc<RefCell<VecDeque<u8>>>,
}

impl PtyMaster {
    pub fn new(id: usize, flags: usize) -> Self {
        PtyMaster {
            id: id,
            flags: flags,
            read: Rc::new(RefCell::new(VecDeque::new())),
            write: Rc::new(RefCell::new(VecDeque::new())),
        }
    }

    fn path(&self, buf: &mut [u8]) -> Result<usize> {
        let path_str = format!("pty:{}", self.id);
        let path = path_str.as_bytes();

        let mut i = 0;
        while i < buf.len() && i < path.len() {
            buf[i] = path[i];
            i += 1;
        }

        Ok(i)
    }

    fn read(&self, buf: &mut [u8]) -> Result<usize> {
        let mut read = self.read.borrow_mut();

        if let Some(packet) = read.pop_front() {
            let mut i = 0;

            while i < buf.len() && i < packet.len() {
                buf[i] = packet[i];
                i += 1;
            }

            Ok(i)
        } else if self.flags & O_NONBLOCK == O_NONBLOCK || Rc::weak_count(&self.read) == 0 {
            Ok(0)
        } else {
            Err(Error::new(EWOULDBLOCK))
        }
    }

    fn write(&self, buf: &[u8]) -> Result<usize> {
        let mut write = self.write.borrow_mut();

        let mut i = 0;
        while i < buf.len() {
            write.push_back(buf[i]);
            i += 1;
        }

        Ok(i)
    }
}

/// Read side of a pipe
#[derive(Clone)]
pub struct PtySlave {
    master_id: usize,
    flags: usize,
    read: Weak<RefCell<VecDeque<u8>>>,
    write: Weak<RefCell<VecDeque<Vec<u8>>>>,
}

impl PtySlave {
    pub fn new(master: &PtyMaster, flags: usize) -> Self {
        PtySlave {
            master_id: master.id,
            flags: flags,
            read: Rc::downgrade(&master.write),
            write: Rc::downgrade(&master.read),
        }
    }

    fn path(&self, buf: &mut [u8]) -> Result<usize> {
        let path_str = format!("pty:{}", self.master_id);
        let path = path_str.as_bytes();

        let mut i = 0;
        while i < buf.len() && i < path.len() {
            buf[i] = path[i];
            i += 1;
        }

        Ok(i)
    }

    fn read(&self, buf: &mut [u8]) -> Result<usize> {
        if let Some(read_lock) = self.read.upgrade() {
            let mut read = read_lock.borrow_mut();

            let mut i = 0;

            while i < buf.len() && ! read.is_empty() {
                buf[i] = read.pop_front().unwrap();
                i += 1;
            }

            if i > 0 || self.flags & O_NONBLOCK == O_NONBLOCK {
                Ok(i)
            } else {
                Err(Error::new(EWOULDBLOCK))
            }
        } else {
            Ok(0)
        }
    }

    fn write(&self, buf: &[u8]) -> Result<usize> {
        if let Some(write_lock) = self.write.upgrade() {
            let mut vec = Vec::new();
            vec.push(0);
            vec.extend_from_slice(buf);

            let mut write = write_lock.borrow_mut();
            write.push_back(vec);

            Ok(buf.len())
        } else {
            Err(Error::new(EPIPE))
        }
    }

    fn sync(&self) -> Result<usize> {
        if let Some(write_lock) = self.write.upgrade() {
            let mut vec = Vec::new();
            vec.push(1);

            let mut write = write_lock.borrow_mut();
            write.push_back(vec);

            Ok(0)
        } else {
            Err(Error::new(EPIPE))
        }
    }
}

fn main(){
    thread::spawn(move || {
        let mut socket = File::create(":pty").expect("pty: failed to create pty scheme");
        let mut scheme = PtyScheme::new();
        let mut todo = Vec::new();
        loop {
            let mut packet = Packet::default();
            socket.read(&mut packet).expect("pty: failed to read events from pty scheme");

            let a = packet.a;
            scheme.handle(&mut packet);
            if packet.a == (-EWOULDBLOCK) as usize {
                packet.a = a;
                todo.push(packet);
            } else {
                socket.write(&packet).expect("pty: failed to write responses to pty scheme");
            }

            let mut i = 0;
            while i < todo.len() {
                let a = todo[i].a;
                scheme.handle(&mut todo[i]);
                if todo[i].a == (-EWOULDBLOCK) as usize {
                    todo[i].a = a;
                    i += 1;
                } else {
                    let packet = todo.remove(i);
                    socket.write(&packet).expect("pty: failed to write responses to pty scheme");
                }
            }

            for (id, master) in scheme.ptys.0.iter() {
                let read = master.read.borrow();
                if let Some(data) = read.front() {
                    socket.write(&Packet {
                        id: 0,
                        pid: 0,
                        uid: 0,
                        gid: 0,
                        a: syscall::number::SYS_FEVENT,
                        b: *id,
                        c: syscall::flag::EVENT_READ,
                        d: data.len()
                    }).expect("pty: failed to write event");
                }
            }

            for (id, slave) in scheme.ptys.1.iter() {
                if let Some(read_lock) = slave.read.upgrade() {
                    let read = read_lock.borrow();
                    if ! read.is_empty() {
                        socket.write(&Packet {
                            id: 0,
                            pid: 0,
                            uid: 0,
                            gid: 0,
                            a: syscall::number::SYS_FEVENT,
                            b: *id,
                            c: syscall::flag::EVENT_READ,
                            d: read.len()
                        }).expect("pty: failed to write event");
                    }
                } else {
                    socket.write(&Packet {
                        id: 0,
                        pid: 0,
                        uid: 0,
                        gid: 0,
                        a: syscall::number::SYS_FEVENT,
                        b: *id,
                        c: syscall::flag::EVENT_READ,
                        d: 0
                    }).expect("pty: failed to write event");
                }
            }
        }
    });
}
<|endoftext|>


<commit_before>use clap::{App, AppSettings, Arg, SubCommand};
use std::env;
use std::fs;
use std::path::Path;
use std::process::exit;
use std::usize;
use tree_sitter_cli::{
    config, error, generate, highlight, loader, logger, parse, properties, test,
};

fn main() {
    if let Err(e) = run() {
        eprintln!("{}", e.0);
        exit(1);
    }
}

fn run() -> error::Result<()> {
    let matches = App::new("tree-sitter")
        .version(concat!(
            env!("CARGO_PKG_VERSION"),
            " (",
            env!("BUILD_SHA"),
            ")"
        ))
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .author("Max Brunsfeld <maxbrunsfeld@gmail.com>")
        .about("Generates and tests parsers")
        .subcommand(SubCommand::with_name("init-config").about("Generate a default config file"))
        .subcommand(
            SubCommand::with_name("generate")
                .about("Generate a parser")
                .arg(Arg::with_name("grammar-path").index(1))
                .arg(Arg::with_name("log").long("log"))
                .arg(Arg::with_name("properties-only").long("properties"))
                .arg(
                    Arg::with_name("state-ids-to-log")
                        .long("log-state")
                        .takes_value(true),
                )
                .arg(Arg::with_name("no-minimize").long("no-minimize")),
        )
        .subcommand(
            SubCommand::with_name("parse")
                .about("Parse a file")
                .arg(
                    Arg::with_name("path")
                        .index(1)
                        .multiple(true)
                        .required(true),
                )
                .arg(Arg::with_name("debug").long("debug").short("d"))
                .arg(Arg::with_name("debug-graph").long("debug-graph").short("D"))
                .arg(Arg::with_name("quiet").long("quiet").short("q"))
                .arg(Arg::with_name("time").long("time").short("t")),
        )
        .subcommand(
            SubCommand::with_name("test")
                .about("Run a parser's tests")
                .arg(
                    Arg::with_name("filter")
                        .long("filter")
                        .short("f")
                        .takes_value(true),
                )
                .arg(Arg::with_name("debug").long("debug").short("d"))
                .arg(Arg::with_name("debug-graph").long("debug-graph").short("D")),
        )
        .subcommand(
            SubCommand::with_name("highlight")
                .about("Highlight a file")
                .arg(
                    Arg::with_name("path")
                        .index(1)
                        .multiple(true)
                        .required(true),
                )
                .arg(Arg::with_name("scope").long("scope").takes_value(true))
                .arg(Arg::with_name("html").long("html").short("h")),
        )
        .get_matches();

    let home_dir = dirs::home_dir().expect("Failed to read home directory");
    let current_dir = env::current_dir().unwrap();
    let config = config::Config::load(&home_dir);
    let mut loader = loader::Loader::new(config.binary_directory.clone());

    if matches.subcommand_matches("init-config").is_some() {
        let config = config::Config::new(&home_dir);
        config.save(&home_dir)?;
    } else if let Some(matches) = matches.subcommand_matches("generate") {
        if matches.is_present("log") {
            logger::init();
        }

        let grammar_path = matches.value_of("grammar-path");
        let minimize = !matches.is_present("no-minimize");
        let properties_only = matches.is_present("properties-only");
        let state_ids_to_log = matches
            .values_of("state-ids-to-log")
            .map_or(Vec::new(), |ids| {
                ids.filter_map(|id| usize::from_str_radix(id, 10).ok())
                    .collect()
            });
        if !properties_only {
            generate::generate_parser_in_directory(
                &current_dir,
                grammar_path,
                minimize,
                state_ids_to_log,
            )?;
        }
        properties::generate_property_sheets_in_directory(&current_dir)?;
    } else if let Some(matches) = matches.subcommand_matches("test") {
        let debug = matches.is_present("debug");
        let debug_graph = matches.is_present("debug-graph");
        let filter = matches.value_of("filter");
        let corpus_path = current_dir.join("corpus");
        if let Some(language) = loader.language_at_path(&current_dir)? {
            test::run_tests_at_path(language, &corpus_path, debug, debug_graph, filter)?;
        } else {
            eprintln!("No language found");
        }
    } else if let Some(matches) = matches.subcommand_matches("parse") {
        let debug = matches.is_present("debug");
        let debug_graph = matches.is_present("debug-graph");
        let quiet = matches.is_present("quiet");
        let time = matches.is_present("time");
        loader.find_all_languages(&config.parser_directories)?;
        let paths = matches
            .values_of("path")
            .unwrap()
            .into_iter()
            .collect::<Vec<_>>();
        let max_path_length = paths.iter().map(|p| p.chars().count()).max().unwrap();
        let mut has_error = false;
        for path in paths {
            let path = Path::new(path);
            let language =
                if let Some((l, _)) = loader.language_configuration_for_file_name(path)? {
                    l
                } else if let Some(l) = loader.language_at_path(&current_dir)? {
                    l
                } else {
                    eprintln!("No language found");
                    return Ok(());
                };
            has_error |= parse::parse_file_at_path(
                language,
                path,
                max_path_length,
                quiet,
                time,
                debug,
                debug_graph,
            )?;
        }

        if has_error {
            return Err(error::Error(String::new()));
        }
    } else if let Some(matches) = matches.subcommand_matches("highlight") {
        let paths = matches.values_of("path").unwrap().into_iter();
        let html_mode = matches.is_present("html");
        loader.find_all_languages(&config.parser_directories)?;

        if html_mode {
            println!("{}", highlight::HTML_HEADER);
        }

        let language_config;
        if let Some(scope) = matches.value_of("scope") {
            language_config = loader.language_configuration_for_scope(scope)?;
            if language_config.is_none() {
                return Err(error::Error(format!("Unknown scope '{}'", scope)));
            }
        } else {
            language_config = None;
        }

        for path in paths {
            let path = Path::new(path);
            let (language, language_config) = match language_config {
                Some(v) => v,
                None => match loader.language_configuration_for_file_name(path)? {
                    Some(v) => v,
                    None => {
                        eprintln!("No language found for path {:?}", path);
                        continue;
                    }
                },
            };

            if let Some(sheet) = language_config.highlight_property_sheet(language)? {
                let source = fs::read(path)?;
                if html_mode {
                    highlight::html(&loader, &config.theme, &source, language, sheet)?;
                } else {
                    highlight::ansi(&loader, &config.theme, &source, language, sheet)?;
                }
            } else {
                return Err(error::Error(format!(
                    "No syntax highlighting property sheet specified"
                )));
            }
        }
    }

    Ok(())
}
<commit_msg>Don't generate prop sheets when a specific grammar path is passed<commit_after>use clap::{App, AppSettings, Arg, SubCommand};
use std::env;
use std::fs;
use std::path::Path;
use std::process::exit;
use std::usize;
use tree_sitter_cli::{
    config, error, generate, highlight, loader, logger, parse, properties, test,
};

fn main() {
    if let Err(e) = run() {
        eprintln!("{}", e.0);
        exit(1);
    }
}

fn run() -> error::Result<()> {
    let matches = App::new("tree-sitter")
        .version(concat!(
            env!("CARGO_PKG_VERSION"),
            " (",
            env!("BUILD_SHA"),
            ")"
        ))
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .author("Max Brunsfeld <maxbrunsfeld@gmail.com>")
        .about("Generates and tests parsers")
        .subcommand(SubCommand::with_name("init-config").about("Generate a default config file"))
        .subcommand(
            SubCommand::with_name("generate")
                .about("Generate a parser")
                .arg(Arg::with_name("grammar-path").index(1))
                .arg(Arg::with_name("log").long("log"))
                .arg(Arg::with_name("properties-only").long("properties"))
                .arg(
                    Arg::with_name("state-ids-to-log")
                        .long("log-state")
                        .takes_value(true),
                )
                .arg(Arg::with_name("no-minimize").long("no-minimize")),
        )
        .subcommand(
            SubCommand::with_name("parse")
                .about("Parse a file")
                .arg(
                    Arg::with_name("path")
                        .index(1)
                        .multiple(true)
                        .required(true),
                )
                .arg(Arg::with_name("debug").long("debug").short("d"))
                .arg(Arg::with_name("debug-graph").long("debug-graph").short("D"))
                .arg(Arg::with_name("quiet").long("quiet").short("q"))
                .arg(Arg::with_name("time").long("time").short("t")),
        )
        .subcommand(
            SubCommand::with_name("test")
                .about("Run a parser's tests")
                .arg(
                    Arg::with_name("filter")
                        .long("filter")
                        .short("f")
                        .takes_value(true),
                )
                .arg(Arg::with_name("debug").long("debug").short("d"))
                .arg(Arg::with_name("debug-graph").long("debug-graph").short("D")),
        )
        .subcommand(
            SubCommand::with_name("highlight")
                .about("Highlight a file")
                .arg(
                    Arg::with_name("path")
                        .index(1)
                        .multiple(true)
                        .required(true),
                )
                .arg(Arg::with_name("scope").long("scope").takes_value(true))
                .arg(Arg::with_name("html").long("html").short("h")),
        )
        .get_matches();

    let home_dir = dirs::home_dir().expect("Failed to read home directory");
    let current_dir = env::current_dir().unwrap();
    let config = config::Config::load(&home_dir);
    let mut loader = loader::Loader::new(config.binary_directory.clone());

    if matches.subcommand_matches("init-config").is_some() {
        let config = config::Config::new(&home_dir);
        config.save(&home_dir)?;
    } else if let Some(matches) = matches.subcommand_matches("generate") {
        if matches.is_present("log") {
            logger::init();
        }

        let grammar_path = matches.value_of("grammar-path");
        let minimize = !matches.is_present("no-minimize");
        let properties_only = matches.is_present("properties-only");
        let parser_only = grammar_path.is_some();
        let state_ids_to_log = matches
            .values_of("state-ids-to-log")
            .map_or(Vec::new(), |ids| {
                ids.filter_map(|id| usize::from_str_radix(id, 10).ok())
                    .collect()
            });

        if !properties_only {
            generate::generate_parser_in_directory(
                &current_dir,
                grammar_path,
                minimize,
                state_ids_to_log,
            )?;
        }

        if !parser_only {
            properties::generate_property_sheets_in_directory(&current_dir)?;
        }
    } else if let Some(matches) = matches.subcommand_matches("test") {
        let debug = matches.is_present("debug");
        let debug_graph = matches.is_present("debug-graph");
        let filter = matches.value_of("filter");
        let corpus_path = current_dir.join("corpus");
        if let Some(language) = loader.language_at_path(&current_dir)? {
            test::run_tests_at_path(language, &corpus_path, debug, debug_graph, filter)?;
        } else {
            eprintln!("No language found");
        }
    } else if let Some(matches) = matches.subcommand_matches("parse") {
        let debug = matches.is_present("debug");
        let debug_graph = matches.is_present("debug-graph");
        let quiet = matches.is_present("quiet");
        let time = matches.is_present("time");
        loader.find_all_languages(&config.parser_directories)?;
        let paths = matches
            .values_of("path")
            .unwrap()
            .into_iter()
            .collect::<Vec<_>>();
        let max_path_length = paths.iter().map(|p| p.chars().count()).max().unwrap();
        let mut has_error = false;
        for path in paths {
            let path = Path::new(path);
            let language =
                if let Some((l, _)) = loader.language_configuration_for_file_name(path)? {
                    l
                } else if let Some(l) = loader.language_at_path(&current_dir)? {
                    l
                } else {
                    eprintln!("No language found");
                    return Ok(());
                };
            has_error |= parse::parse_file_at_path(
                language,
                path,
                max_path_length,
                quiet,
                time,
                debug,
                debug_graph,
            )?;
        }

        if has_error {
            return Err(error::Error(String::new()));
        }
    } else if let Some(matches) = matches.subcommand_matches("highlight") {
        let paths = matches.values_of("path").unwrap().into_iter();
        let html_mode = matches.is_present("html");
        loader.find_all_languages(&config.parser_directories)?;

        if html_mode {
            println!("{}", highlight::HTML_HEADER);
        }

        let language_config;
        if let Some(scope) = matches.value_of("scope") {
            language_config = loader.language_configuration_for_scope(scope)?;
            if language_config.is_none() {
                return Err(error::Error(format!("Unknown scope '{}'", scope)));
            }
        } else {
            language_config = None;
        }

        for path in paths {
            let path = Path::new(path);
            let (language, language_config) = match language_config {
                Some(v) => v,
                None => match loader.language_configuration_for_file_name(path)? {
                    Some(v) => v,
                    None => {
                        eprintln!("No language found for path {:?}", path);
                        continue;
                    }
                },
            };

            if let Some(sheet) = language_config.highlight_property_sheet(language)? {
                let source = fs::read(path)?;
                if html_mode {
                    highlight::html(&loader, &config.theme, &source, language, sheet)?;
                } else {
                    highlight::ansi(&loader, &config.theme, &source, language, sheet)?;
                }
            } else {
                return Err(error::Error(format!(
                    "No syntax highlighting property sheet specified"
                )));
            }
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before>use cssparser::{self, Parser};

use bbox::BoundingBox;
use drawing_ctx::DrawingCtx;
use error::*;
use gradient;
use node::NodeType;
use parsers::Parse;
use pattern;
use unitinterval::UnitInterval;

#[derive(Debug, Clone, PartialEq)]
pub enum PaintServer {
    None,
    Iri {
        iri: String,
        alternate: Option<cssparser::Color>,
    },
    SolidColor(cssparser::Color),
}

impl Parse for PaintServer {
    type Data = ();
    type Err = ValueErrorKind;

    fn parse(parser: &mut Parser<'_, '_>, _: ()) -> Result<PaintServer, ValueErrorKind> {
        if parser.try(|i| i.expect_ident_matching("none")).is_ok() {
            Ok(PaintServer::None)
        } else if let Ok(url) = parser.try(|i| i.expect_url()) {
            let alternate = if !parser.is_exhausted() {
                if parser.try(|i| i.expect_ident_matching("none")).is_ok() {
                    None
                } else {
                    Some(parser.try(|i| cssparser::Color::parse(i))?)
                }
            } else {
                None
            };

            Ok(PaintServer::Iri {
                iri: String::from(url.as_ref()),
                alternate,
            })
        } else {
            cssparser::Color::parse(parser)
                .map(PaintServer::SolidColor)
                .map_err(ValueErrorKind::from)
        }
    }
}

fn set_color(
    draw_ctx: &mut DrawingCtx<'_>,
    color: &cssparser::Color,
    opacity: &UnitInterval,
    current_color: &cssparser::RGBA,
) {
    let rgba = match *color {
        cssparser::Color::RGBA(ref rgba) => rgba,
        cssparser::Color::CurrentColor => current_color,
    };

    let &UnitInterval(o) = opacity;
    draw_ctx.get_cairo_context().set_source_rgba(
        f64::from(rgba.red_f32()),
        f64::from(rgba.green_f32()),
        f64::from(rgba.blue_f32()),
        f64::from(rgba.alpha_f32()) * o,
    );
}

pub fn set_source_paint_server(
    draw_ctx: &mut DrawingCtx<'_>,
    ps: &PaintServer,
    opacity: &UnitInterval,
    bbox: &BoundingBox,
    current_color: &cssparser::RGBA,
) -> Result<bool, RenderingError> {
    let mut had_paint_server;

    match *ps {
        PaintServer::Iri {
            ref iri,
            ref alternate,
        } => {
            had_paint_server = false;

            if let Some(acquired) = draw_ctx.get_acquired_node(iri.as_str()) {
                let node = acquired.get();

                if node.get_type() == NodeType::LinearGradient
                    || node.get_type() == NodeType::RadialGradient
                {
                    had_paint_server = gradient::gradient_resolve_fallbacks_and_set_pattern(
                        &node, draw_ctx, opacity, bbox,
                    );
                } else if node.get_type() == NodeType::Pattern {
                    had_paint_server =
                        pattern::pattern_resolve_fallbacks_and_set_pattern(&node, draw_ctx, bbox)?;
                }
            }

            if !had_paint_server && alternate.is_some() {
                set_color(
                    draw_ctx,
                    alternate.as_ref().unwrap(),
                    opacity,
                    current_color,
                );
                had_paint_server = true;
            }
        }

        PaintServer::SolidColor(color) => {
            set_color(draw_ctx, &color, opacity, current_color);
            had_paint_server = true;
        }

        PaintServer::None => {
            had_paint_server = false;
        }
    };

    Ok(had_paint_server)
}

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

    #[test]
    fn catches_invalid_syntax() {
        assert!(PaintServer::parse_str("", ()).is_err());
        assert!(PaintServer::parse_str("42", ()).is_err());
        assert!(PaintServer::parse_str("invalid", ()).is_err());
    }

    #[test]
    fn parses_none() {
        assert_eq!(PaintServer::parse_str("none", ()), Ok(PaintServer::None));
    }

    #[test]
    fn parses_solid_color() {
        assert_eq!(
            PaintServer::parse_str("rgb(255, 128, 64, 0.5)", ()),
            Ok(PaintServer::SolidColor(cssparser::Color::RGBA(
                cssparser::RGBA::new(255, 128, 64, 128)
            )))
        );

        assert_eq!(
            PaintServer::parse_str("currentColor", ()),
            Ok(PaintServer::SolidColor(cssparser::Color::CurrentColor))
        );
    }

    #[test]
    fn parses_iri() {
        assert_eq!(
            PaintServer::parse_str("url(#link)", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: None,
            },)
        );

        assert_eq!(
            PaintServer::parse_str("url(#link) none", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: None,
            },)
        );

        assert_eq!(
            PaintServer::parse_str("url(#link) #ff8040", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: Some(cssparser::Color::RGBA(cssparser::RGBA::new(
                    255, 128, 64, 255
                ))),
            },)
        );

        assert_eq!(
            PaintServer::parse_str("url(#link) rgb(255, 128, 64, 0.5)", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: Some(cssparser::Color::RGBA(cssparser::RGBA::new(
                    255, 128, 64, 128
                ))),
            },)
        );

        assert_eq!(
            PaintServer::parse_str("url(#link) currentColor", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: Some(cssparser::Color::CurrentColor),
            },)
        );

        assert!(PaintServer::parse_str("url(#link) invalid", ()).is_err());
    }
}
<commit_msg>paint_server.rs: Log when there is a nonexistent pattern with no alternate<commit_after>use cssparser::{self, Parser};

use bbox::BoundingBox;
use drawing_ctx::DrawingCtx;
use error::*;
use gradient;
use node::NodeType;
use parsers::Parse;
use pattern;
use unitinterval::UnitInterval;

#[derive(Debug, Clone, PartialEq)]
pub enum PaintServer {
    None,
    Iri {
        iri: String,
        alternate: Option<cssparser::Color>,
    },
    SolidColor(cssparser::Color),
}

impl Parse for PaintServer {
    type Data = ();
    type Err = ValueErrorKind;

    fn parse(parser: &mut Parser<'_, '_>, _: ()) -> Result<PaintServer, ValueErrorKind> {
        if parser.try(|i| i.expect_ident_matching("none")).is_ok() {
            Ok(PaintServer::None)
        } else if let Ok(url) = parser.try(|i| i.expect_url()) {
            let alternate = if !parser.is_exhausted() {
                if parser.try(|i| i.expect_ident_matching("none")).is_ok() {
                    None
                } else {
                    Some(parser.try(|i| cssparser::Color::parse(i))?)
                }
            } else {
                None
            };

            Ok(PaintServer::Iri {
                iri: String::from(url.as_ref()),
                alternate,
            })
        } else {
            cssparser::Color::parse(parser)
                .map(PaintServer::SolidColor)
                .map_err(ValueErrorKind::from)
        }
    }
}

fn set_color(
    draw_ctx: &mut DrawingCtx<'_>,
    color: &cssparser::Color,
    opacity: &UnitInterval,
    current_color: &cssparser::RGBA,
) {
    let rgba = match *color {
        cssparser::Color::RGBA(ref rgba) => rgba,
        cssparser::Color::CurrentColor => current_color,
    };

    let &UnitInterval(o) = opacity;
    draw_ctx.get_cairo_context().set_source_rgba(
        f64::from(rgba.red_f32()),
        f64::from(rgba.green_f32()),
        f64::from(rgba.blue_f32()),
        f64::from(rgba.alpha_f32()) * o,
    );
}

pub fn set_source_paint_server(
    draw_ctx: &mut DrawingCtx<'_>,
    ps: &PaintServer,
    opacity: &UnitInterval,
    bbox: &BoundingBox,
    current_color: &cssparser::RGBA,
) -> Result<bool, RenderingError> {
    let mut had_paint_server;

    match *ps {
        PaintServer::Iri {
            ref iri,
            ref alternate,
        } => {
            had_paint_server = false;

            if let Some(acquired) = draw_ctx.get_acquired_node(iri.as_str()) {
                let node = acquired.get();

                if node.get_type() == NodeType::LinearGradient
                    || node.get_type() == NodeType::RadialGradient
                {
                    had_paint_server = gradient::gradient_resolve_fallbacks_and_set_pattern(
                        &node, draw_ctx, opacity, bbox,
                    );
                } else if node.get_type() == NodeType::Pattern {
                    had_paint_server =
                        pattern::pattern_resolve_fallbacks_and_set_pattern(&node, draw_ctx, bbox)?;
                }
            }

            if !had_paint_server && alternate.is_some() {
                set_color(
                    draw_ctx,
                    alternate.as_ref().unwrap(),
                    opacity,
                    current_color,
                );
                had_paint_server = true;
            } else {
                rsvg_log!(
                    "pattern \"{}\" was not found and there was no fallback alternate",
                    iri
                );
            }
        }

        PaintServer::SolidColor(color) => {
            set_color(draw_ctx, &color, opacity, current_color);
            had_paint_server = true;
        }

        PaintServer::None => {
            had_paint_server = false;
        }
    };

    Ok(had_paint_server)
}

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

    #[test]
    fn catches_invalid_syntax() {
        assert!(PaintServer::parse_str("", ()).is_err());
        assert!(PaintServer::parse_str("42", ()).is_err());
        assert!(PaintServer::parse_str("invalid", ()).is_err());
    }

    #[test]
    fn parses_none() {
        assert_eq!(PaintServer::parse_str("none", ()), Ok(PaintServer::None));
    }

    #[test]
    fn parses_solid_color() {
        assert_eq!(
            PaintServer::parse_str("rgb(255, 128, 64, 0.5)", ()),
            Ok(PaintServer::SolidColor(cssparser::Color::RGBA(
                cssparser::RGBA::new(255, 128, 64, 128)
            )))
        );

        assert_eq!(
            PaintServer::parse_str("currentColor", ()),
            Ok(PaintServer::SolidColor(cssparser::Color::CurrentColor))
        );
    }

    #[test]
    fn parses_iri() {
        assert_eq!(
            PaintServer::parse_str("url(#link)", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: None,
            },)
        );

        assert_eq!(
            PaintServer::parse_str("url(#link) none", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: None,
            },)
        );

        assert_eq!(
            PaintServer::parse_str("url(#link) #ff8040", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: Some(cssparser::Color::RGBA(cssparser::RGBA::new(
                    255, 128, 64, 255
                ))),
            },)
        );

        assert_eq!(
            PaintServer::parse_str("url(#link) rgb(255, 128, 64, 0.5)", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: Some(cssparser::Color::RGBA(cssparser::RGBA::new(
                    255, 128, 64, 128
                ))),
            },)
        );

        assert_eq!(
            PaintServer::parse_str("url(#link) currentColor", ()),
            Ok(PaintServer::Iri {
                iri: "#link".to_string(),
                alternate: Some(cssparser::Color::CurrentColor),
            },)
        );

        assert!(PaintServer::parse_str("url(#link) invalid", ()).is_err());
    }
}
<|endoftext|>


<commit_before>//! This module is UNSTABLE, the reason is - recently added.

use std::convert::From;
use std::io::{Cursor, Read};
use std::str::{from_utf8, Utf8Error};

use super::{read_marker};
use super::{
    ReadError,
    MarkerReadError,
};
use super::{BigEndianRead};

use super::super::init::Marker;
use super::super::value::{Float, Integer, ValueRef};

trait USizeCast {
    fn from(v: Self) -> Option<usize> where Self: Sized;
}

impl USizeCast for u8 {
    fn from(v: u8) -> Option<usize> {
        // Impossible to panic, since u8 always fits in usize.
        Some(v as usize)
    }
}

impl USizeCast for u16 {
    fn from(v: u16) -> Option<usize> {
        // TODO: This can overflow on 8-bit systems.
        Some(v as usize)
    }
}

impl USizeCast for u32 {
    fn from(v: u32) -> Option<usize> {
        // TODO: This can overflow on 8- and 16-bit systems.
        Some(v as usize)
    }
}

// TODO: Display trait.
#[derive(Debug)]
pub enum Error<'r> {
    /// Failed to read the type marker value.
    InvalidMarkerRead(ReadError),
    /// Failed to read string/array/map size.
    InvalidLengthRead(ReadError),
    /// Failed to read packed non-marker data.
    InvalidDataRead(ReadError),
    /// Failed to cast the length read to machine size.
    InvalidLengthSize,
    /// Failed to interpret a byte slice as a UTF-8 string.
    ///
    /// Contains untouched bytearray with the underlying decoding error.
    InvalidUtf8(&'r [u8], Utf8Error),
    /// Failed to read ext type.
    InvalidExtTypeRead(ReadError),
    /// Using Reserved type found.
    TypeMismatch,
}

impl<'r> From<MarkerReadError> for Error<'r> {
    fn from(err: MarkerReadError) -> Error<'r> {
        Error::InvalidMarkerRead(From::from(err))
    }
}

fn read_num<'a, R, D>(mut rd: &mut R) -> Result<D, Error<'a>>
    where R: BufRead<'a>,
          D: BigEndianRead
{
    D::read(&mut rd).map_err(|err| Error::InvalidDataRead(From::from(err)))
}

// TODO: Rename to `read_len`.
fn read_length<R, D>(rd: &mut R) -> Result<D, ReadError>
    where R: Read,
          D: BigEndianRead
{
    D::read(rd).map_err(From::from)
}

fn read_str<'a, R>(buf: &mut R, len: usize) -> Result<&'a str, Error<'a>>
    where R: BufRead<'a>
{
    let buf = try!(read_bin(buf, len));

    // Try to decode sliced buffer as UTF-8.
    let res = try!(from_utf8(buf).map_err(|err| Error::InvalidUtf8(buf, err)));

    Ok(res)
}

fn read_bin<'a, R>(rd: &mut R, len: usize) -> Result<&'a [u8], Error<'a>>
    where R: BufRead<'a>
{
    let buf = rd.fill_buf();

    if len > buf.len() {
        return Err(Error::InvalidDataRead(ReadError::UnexpectedEOF));
    }

    // Take a slice.
    let buf = &buf[..len];
    rd.consume(len);

    Ok(buf)
}

// Helper function that reads a single byte from the given `Read` and interpret it as an Ext type.
fn read_ext_type<R>(rd: &mut R) -> Result<i8, ReadError>
    where R: Read
{
    i8::read(rd).map_err(From::from)
}

fn read_ext<'a, R>(mut buf: &mut R, len: usize) -> Result<(i8, &'a [u8]), Error<'a>>
    where R: BufRead<'a>
{
    let ty  = try!(read_ext_type(&mut buf).map_err(|err| Error::InvalidExtTypeRead(err)));
    let buf = try!(read_bin(buf, len));

    Ok((ty, buf))
}

#[inline]
fn read_str_value<'a, R, U>(buf: &mut R, len: U) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>,
          U: USizeCast
{
    let len = try!(U::from(len).ok_or(Error::InvalidLengthSize));
    let res = try!(read_str(buf, len));

    Ok(ValueRef::String(res))
}

#[inline]
fn read_bin_value<'a, R, U>(buf: &mut R, len: U) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>,
          U: USizeCast
{
    let len = try!(U::from(len).ok_or(Error::InvalidLengthSize));
    let res = try!(read_bin(buf, len));

    Ok(ValueRef::Binary(res))
}

#[inline]
fn read_ext_value<'a, R, U>(mut buf: &mut R, len: U) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>,
          U: USizeCast
{
    let len = try!(U::from(len).ok_or(Error::InvalidLengthSize));
    let (ty, buf) = try!(read_ext(buf, len));

    Ok(ValueRef::Ext(ty, buf))
}

fn read_array<'a, R>(rd: &mut R, len: usize) -> Result<Vec<ValueRef<'a>>, Error<'a>>
    where R: BufRead<'a>
{
    let mut vec = Vec::with_capacity(len);

    for _ in 0..len {
        let val = try!(read_value_ref(rd));

        vec.push(val);
    }

    Ok(vec)
}

fn read_map<'a, R>(rd: &mut R, len: usize) -> Result<Vec<(ValueRef<'a>, ValueRef<'a>)>, Error<'a>>
    where R: BufRead<'a>
{
    let mut vec = Vec::with_capacity(len);

    for _ in 0..len {
        let key = try!(read_value_ref(rd));
        let val = try!(read_value_ref(rd));

        vec.push((key, val));
    }

    Ok(vec)
}

/// A BufRead is a type of Reader which has an internal buffer.
///
/// This magic trait acts like a standard BufRead but unlike the standard this has an explicit
/// internal buffer lifetime, which allows to borrow from underlying buffer while consuming bytes.
pub trait BufRead<'a>: Read {
    /// Returns the buffer contents.
    ///
    /// This function is a lower-level call. It needs to be paired with the consume method to
    /// function properly. When calling this method, none of the contents will be "read" in the
    /// sense that later calling read may return the same contents. As such, consume must be called
    /// with the number of bytes that are consumed from this buffer to ensure that the bytes are
    /// never returned twice.
    ///
    /// An empty buffer returned indicates that the stream has reached EOF.
    fn fill_buf(&self) -> &'a [u8];

    /// Tells this buffer that len bytes have been consumed from the buffer, so they should no
    /// longer be returned in calls to read.
    fn consume(&mut self, len: usize);
}

impl<'a> BufRead<'a> for &'a [u8] {
    fn fill_buf(&self) -> &'a [u8] {
        self
    }

    fn consume(&mut self, len: usize) {
        *self = &(*self)[len..];
    }
}

/// Useful when you want to know how much bytes has been consumed during ValueRef decoding.
impl<'a> BufRead<'a> for Cursor<&'a [u8]> {
    fn fill_buf(&self) -> &'a [u8] {
        use std::cmp;

        let len = cmp::min(self.position(), self.get_ref().len() as u64);
        &self.get_ref()[len as usize..]
    }

    fn consume(&mut self, len: usize) {
        let pos = self.position();
        self.set_position(pos + len as u64);
    }
}

// TODO: Doc, examples.
pub fn read_value_ref<'a, R>(rd: &mut R) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>
{
    let mut rd = rd;

    // Reading the marker involves either 1 byte read or nothing. On success consumes strictly
    // 1 byte from the `rd`.
    let marker = try!(read_marker(rd));

    let val = match marker {
        Marker::Null => ValueRef::Nil,
        Marker::True => ValueRef::Boolean(true),
        Marker::False => ValueRef::Boolean(false),
        Marker::PositiveFixnum(val) => {
            ValueRef::Integer(Integer::U64(val as u64))
        }
        Marker::U8 => {
            let val: u8 = try!(read_num(rd));
            ValueRef::Integer(Integer::U64(val as u64))
        }
        Marker::U16 => {
            let val: u16 = try!(read_num(rd));
            ValueRef::Integer(Integer::U64(val as u64))
        }
        Marker::U32 => {
            let val: u32 = try!(read_num(rd));
            ValueRef::Integer(Integer::U64(val as u64))
        }
        Marker::U64 => {
            let val: u64 = try!(read_num(rd));
            ValueRef::Integer(Integer::U64(val))
        }
        Marker::NegativeFixnum(val) => {
            ValueRef::Integer(Integer::I64(val as i64))
        }
        Marker::I8 => {
            let val: i8 = try!(read_num(rd));
            ValueRef::Integer(Integer::I64(val as i64))
        }
        Marker::I16 => {
            let val: i16 = try!(read_num(rd));
            ValueRef::Integer(Integer::I64(val as i64))
        }
        Marker::I32 => {
            let val: i32 = try!(read_num(rd));
            ValueRef::Integer(Integer::I64(val as i64))
        }
        Marker::I64 => {
            let val: i64 = try!(read_num(rd));
            ValueRef::Integer(Integer::I64(val))
        }
        Marker::F32 => {
            let val: f32 = try!(read_num(rd));
            ValueRef::Float(Float::F32(val))
        }
        Marker::F64 => {
            let val: f64 = try!(read_num(rd));
            ValueRef::Float(Float::F64(val))
        }
        Marker::FixedString(len) => {
            try!(read_str_value(rd, len))
        }
        Marker::Str8 => {
            let len: u8 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_str_value(rd, len))
        }
        Marker::Str16 => {
            let len: u16 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_str_value(rd, len))
        }
        Marker::Str32 => {
            let len: u32 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_str_value(rd, len))
        }
        Marker::Bin8 => {
            let len: u8 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_bin_value(rd, len))
        }
        Marker::Bin16 => {
            let len: u16 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_bin_value(rd, len))
        }
        Marker::Bin32 => {
            let len: u32 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_bin_value(rd, len))
        }
        Marker::FixedArray(len) => {
            let len = len as usize;
            let vec = try!(read_array(rd, len));
            ValueRef::Array(vec)
        }
        Marker::Array16 => {
            let len: u16 = try!(read_length(&mut rd).map_err(|err| Error::InvalidLengthRead(err)));
            let len = len as usize; // TODO: Possible overflow.
            let vec = try!(read_array(rd, len));
            ValueRef::Array(vec)
        }
        Marker::Array32 => {
            let len: u32 = try!(read_length(&mut rd).map_err(|err| Error::InvalidLengthRead(err)));
            let len = len as usize; // TODO: Possible overflow.
            let vec = try!(read_array(rd, len));
            ValueRef::Array(vec)
        }
        Marker::FixedMap(len) => {
            let len = len as usize;
            let map = try!(read_map(rd, len));
            ValueRef::Map(map)
        }
        Marker::Map16 => {
            let len: u16 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            let len = len as usize; // TODO: Possible overflow.
            let map = try!(read_map(rd, len));
            ValueRef::Map(map)
        }
        Marker::Map32 => {
            let len: u32 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            let len = len as usize; // TODO: Possible overflow.
            let map = try!(read_map(rd, len));
            ValueRef::Map(map)
        }
        Marker::FixExt1 => {
            let len: u8 = 1;
            try!(read_ext_value(rd, len))
        }
        Marker::FixExt2 => {
            let len: u8 = 2;
            try!(read_ext_value(rd, len))
        }
        Marker::FixExt4 => {
            let len: u8 = 4;
            try!(read_ext_value(rd, len))
        }
        Marker::FixExt8 => {
            let len: u8 = 8;
            try!(read_ext_value(rd, len))
        }
        Marker::FixExt16 => {
            let len: u8 = 16;
            try!(read_ext_value(rd, len))
        }
        Marker::Ext8 => {
            let len: u8 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_ext_value(rd, len))
        }
        Marker::Ext16 => {
            let len: u16 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_ext_value(rd, len))
        }
        Marker::Ext32 => {
            let len: u32 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_ext_value(rd, len))
        }
        Marker::Reserved => return Err(Error::TypeMismatch),
    };

    Ok(val)
}
<commit_msg>ValueRef: code decomposition.<commit_after>//! This module is UNSTABLE, the reason is - recently added.

use std::convert::From;
use std::io::{Cursor, Read};
use std::str::{from_utf8, Utf8Error};

use super::{read_marker};
use super::{
    ReadError,
    MarkerReadError,
};
use super::{BigEndianRead};

use super::super::init::Marker;
use super::super::value::{Float, Integer, ValueRef};

trait USizeCast {
    fn from(v: Self) -> Option<usize> where Self: Sized;
}

impl USizeCast for u8 {
    fn from(v: u8) -> Option<usize> {
        // Impossible to panic, since u8 always fits in usize.
        Some(v as usize)
    }
}

impl USizeCast for u16 {
    fn from(v: u16) -> Option<usize> {
        // TODO: This can overflow on 8-bit systems.
        Some(v as usize)
    }
}

impl USizeCast for u32 {
    fn from(v: u32) -> Option<usize> {
        // TODO: This can overflow on 8- and 16-bit systems.
        Some(v as usize)
    }
}

// TODO: Display trait.
#[derive(Debug)]
pub enum Error<'r> {
    /// Failed to read the type marker value.
    InvalidMarkerRead(ReadError),
    /// Failed to read string/array/map size.
    InvalidLengthRead(ReadError),
    /// Failed to read packed non-marker data.
    InvalidDataRead(ReadError),
    /// Failed to cast the length read to machine size.
    InvalidLengthSize,
    /// Failed to interpret a byte slice as a UTF-8 string.
    ///
    /// Contains untouched bytearray with the underlying decoding error.
    InvalidUtf8(&'r [u8], Utf8Error),
    /// Failed to read ext type.
    InvalidExtTypeRead(ReadError),
    /// Using Reserved type found.
    TypeMismatch,
}

impl<'r> From<MarkerReadError> for Error<'r> {
    fn from(err: MarkerReadError) -> Error<'r> {
        Error::InvalidMarkerRead(From::from(err))
    }
}

fn read_num<'a, R, D>(mut rd: &mut R) -> Result<D, Error<'a>>
    where R: BufRead<'a>,
          D: BigEndianRead
{
    D::read(&mut rd).map_err(|err| Error::InvalidDataRead(From::from(err)))
}

// TODO: Rename to `read_len`.
fn read_length<R, D>(rd: &mut R) -> Result<D, ReadError>
    where R: Read,
          D: BigEndianRead
{
    D::read(rd).map_err(From::from)
}

fn read_str<'a, R>(buf: &mut R, len: usize) -> Result<&'a str, Error<'a>>
    where R: BufRead<'a>
{
    let buf = try!(read_bin(buf, len));

    // Try to decode sliced buffer as UTF-8.
    let res = try!(from_utf8(buf).map_err(|err| Error::InvalidUtf8(buf, err)));

    Ok(res)
}

fn read_bin<'a, R>(rd: &mut R, len: usize) -> Result<&'a [u8], Error<'a>>
    where R: BufRead<'a>
{
    let buf = rd.fill_buf();

    if len > buf.len() {
        return Err(Error::InvalidDataRead(ReadError::UnexpectedEOF));
    }

    // Take a slice.
    let buf = &buf[..len];
    rd.consume(len);

    Ok(buf)
}

// Helper function that reads a single byte from the given `Read` and interpret it as an Ext type.
fn read_ext_type<R>(rd: &mut R) -> Result<i8, ReadError>
    where R: Read
{
    i8::read(rd).map_err(From::from)
}

fn read_ext<'a, R>(mut buf: &mut R, len: usize) -> Result<(i8, &'a [u8]), Error<'a>>
    where R: BufRead<'a>
{
    let ty  = try!(read_ext_type(&mut buf).map_err(|err| Error::InvalidExtTypeRead(err)));
    let buf = try!(read_bin(buf, len));

    Ok((ty, buf))
}

#[inline]
fn read_str_value<'a, R, U>(buf: &mut R, len: U) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>,
          U: USizeCast
{
    let len = try!(U::from(len).ok_or(Error::InvalidLengthSize));
    let res = try!(read_str(buf, len));

    Ok(ValueRef::String(res))
}

#[inline]
fn read_bin_value<'a, R, U>(buf: &mut R, len: U) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>,
          U: USizeCast
{
    let len = try!(U::from(len).ok_or(Error::InvalidLengthSize));
    let res = try!(read_bin(buf, len));

    Ok(ValueRef::Binary(res))
}

#[inline]
fn read_ext_value<'a, R, U>(mut buf: &mut R, len: U) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>,
          U: USizeCast
{
    let len = try!(U::from(len).ok_or(Error::InvalidLengthSize));
    let (ty, buf) = try!(read_ext(buf, len));

    Ok(ValueRef::Ext(ty, buf))
}

#[inline]
fn read_array_value<'a, R, U>(rd: &mut R, len: U) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>,
          U: USizeCast
{
    let len = try!(U::from(len).ok_or(Error::InvalidLengthSize));
    let vec = try!(read_array(rd, len));

    Ok(ValueRef::Array(vec))
}

#[inline]
fn read_map_value<'a, R, U>(rd: &mut R, len: U) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>,
          U: USizeCast
{
    let len = try!(U::from(len).ok_or(Error::InvalidLengthSize));
    let map = try!(read_map(rd, len));

    Ok(ValueRef::Map(map))
}

fn read_array<'a, R>(rd: &mut R, len: usize) -> Result<Vec<ValueRef<'a>>, Error<'a>>
    where R: BufRead<'a>
{
    let mut vec = Vec::with_capacity(len);

    for _ in 0..len {
        let val = try!(read_value_ref(rd));

        vec.push(val);
    }

    Ok(vec)
}

fn read_map<'a, R>(rd: &mut R, len: usize) -> Result<Vec<(ValueRef<'a>, ValueRef<'a>)>, Error<'a>>
    where R: BufRead<'a>
{
    let mut vec = Vec::with_capacity(len);

    for _ in 0..len {
        let key = try!(read_value_ref(rd));
        let val = try!(read_value_ref(rd));

        vec.push((key, val));
    }

    Ok(vec)
}

/// A BufRead is a type of Reader which has an internal buffer.
///
/// This magic trait acts like a standard BufRead but unlike the standard this has an explicit
/// internal buffer lifetime, which allows to borrow from underlying buffer while consuming bytes.
pub trait BufRead<'a>: Read {
    /// Returns the buffer contents.
    ///
    /// This function is a lower-level call. It needs to be paired with the consume method to
    /// function properly. When calling this method, none of the contents will be "read" in the
    /// sense that later calling read may return the same contents. As such, consume must be called
    /// with the number of bytes that are consumed from this buffer to ensure that the bytes are
    /// never returned twice.
    ///
    /// An empty buffer returned indicates that the stream has reached EOF.
    fn fill_buf(&self) -> &'a [u8];

    /// Tells this buffer that len bytes have been consumed from the buffer, so they should no
    /// longer be returned in calls to read.
    fn consume(&mut self, len: usize);
}

impl<'a> BufRead<'a> for &'a [u8] {
    fn fill_buf(&self) -> &'a [u8] {
        self
    }

    fn consume(&mut self, len: usize) {
        *self = &(*self)[len..];
    }
}

/// Useful when you want to know how much bytes has been consumed during ValueRef decoding.
impl<'a> BufRead<'a> for Cursor<&'a [u8]> {
    fn fill_buf(&self) -> &'a [u8] {
        use std::cmp;

        let len = cmp::min(self.position(), self.get_ref().len() as u64);
        &self.get_ref()[len as usize..]
    }

    fn consume(&mut self, len: usize) {
        let pos = self.position();
        self.set_position(pos + len as u64);
    }
}

// TODO: Doc, examples.
pub fn read_value_ref<'a, R>(rd: &mut R) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>
{
    let mut rd = rd;

    // Reading the marker involves either 1 byte read or nothing. On success consumes strictly
    // 1 byte from the `rd`.
    let marker = try!(read_marker(rd));

    let val = match marker {
        Marker::Null => ValueRef::Nil,
        Marker::True => ValueRef::Boolean(true),
        Marker::False => ValueRef::Boolean(false),
        Marker::PositiveFixnum(val) => {
            ValueRef::Integer(Integer::U64(val as u64))
        }
        Marker::U8 => {
            let val: u8 = try!(read_num(rd));
            ValueRef::Integer(Integer::U64(val as u64))
        }
        Marker::U16 => {
            let val: u16 = try!(read_num(rd));
            ValueRef::Integer(Integer::U64(val as u64))
        }
        Marker::U32 => {
            let val: u32 = try!(read_num(rd));
            ValueRef::Integer(Integer::U64(val as u64))
        }
        Marker::U64 => {
            let val: u64 = try!(read_num(rd));
            ValueRef::Integer(Integer::U64(val))
        }
        Marker::NegativeFixnum(val) => {
            ValueRef::Integer(Integer::I64(val as i64))
        }
        Marker::I8 => {
            let val: i8 = try!(read_num(rd));
            ValueRef::Integer(Integer::I64(val as i64))
        }
        Marker::I16 => {
            let val: i16 = try!(read_num(rd));
            ValueRef::Integer(Integer::I64(val as i64))
        }
        Marker::I32 => {
            let val: i32 = try!(read_num(rd));
            ValueRef::Integer(Integer::I64(val as i64))
        }
        Marker::I64 => {
            let val: i64 = try!(read_num(rd));
            ValueRef::Integer(Integer::I64(val))
        }
        Marker::F32 => {
            let val: f32 = try!(read_num(rd));
            ValueRef::Float(Float::F32(val))
        }
        Marker::F64 => {
            let val: f64 = try!(read_num(rd));
            ValueRef::Float(Float::F64(val))
        }
        Marker::FixedString(len) => {
            try!(read_str_value(rd, len))
        }
        Marker::Str8 => {
            let len: u8 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_str_value(rd, len))
        }
        Marker::Str16 => {
            let len: u16 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_str_value(rd, len))
        }
        Marker::Str32 => {
            let len: u32 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_str_value(rd, len))
        }
        Marker::Bin8 => {
            let len: u8 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_bin_value(rd, len))
        }
        Marker::Bin16 => {
            let len: u16 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_bin_value(rd, len))
        }
        Marker::Bin32 => {
            let len: u32 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_bin_value(rd, len))
        }
        Marker::FixedArray(len) => {
            try!(read_array_value(rd, len))
        }
        Marker::Array16 => {
            let len: u16 = try!(read_length(&mut rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_array_value(rd, len))
        }
        Marker::Array32 => {
            let len: u32 = try!(read_length(&mut rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_array_value(rd, len))
        }
        Marker::FixedMap(len) => {
            try!(read_map_value(rd, len))
        }
        Marker::Map16 => {
            let len: u16 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_map_value(rd, len))
        }
        Marker::Map32 => {
            let len: u32 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_map_value(rd, len))
        }
        Marker::FixExt1 => {
            try!(read_ext_value(rd, 1u8))
        }
        Marker::FixExt2 => {
            try!(read_ext_value(rd, 2u8))
        }
        Marker::FixExt4 => {
            try!(read_ext_value(rd, 4u8))
        }
        Marker::FixExt8 => {
            try!(read_ext_value(rd, 8u8))
        }
        Marker::FixExt16 => {
            try!(read_ext_value(rd, 16u8))
        }
        Marker::Ext8 => {
            let len: u8 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_ext_value(rd, len))
        }
        Marker::Ext16 => {
            let len: u16 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_ext_value(rd, len))
        }
        Marker::Ext32 => {
            let len: u32 = try!(read_length(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_ext_value(rd, len))
        }
        Marker::Reserved => return Err(Error::TypeMismatch),
    };

    Ok(val)
}
<|endoftext|>