<commit_before>use crate::syntax::atom::Atom::{self, *};
use crate::syntax::{
    error, ident, Api, ExternFn, ExternType, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, Types,
};
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
use quote::{quote, ToTokens};
use std::fmt::Display;
use syn::{Error, Result};

struct Check<'a> {
    apis: &'a [Api],
    types: &'a Types<'a>,
    errors: &'a mut Vec<Error>,
}

pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> {
    let mut errors = Vec::new();
    let mut cx = Check {
        apis,
        types,
        errors: &mut errors,
    };
    do_typecheck(&mut cx);
    combine_errors(errors)
}

fn do_typecheck(cx: &mut Check) {
    for ty in cx.types {
        match ty {
            Type::Ident(ident) => check_type_ident(cx, ident),
            Type::RustBox(ptr) => check_type_box(cx, ptr),
            Type::RustVec(ptr) => check_type_rust_vec(cx, ptr),
            Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr),
            Type::CxxVector(ptr) => check_type_cxx_vector(cx, ptr),
            Type::Ref(ty) => check_type_ref(cx, ty),
            Type::Slice(ty) => check_type_slice(cx, ty),
            _ => {}
        }
    }

    for api in cx.apis {
        match api {
            Api::Struct(strct) => check_api_struct(cx, strct),
            Api::CxxType(ty) | Api::RustType(ty) => check_api_type(cx, ty),
            Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn),
            _ => {}
        }
    }

    for api in cx.apis {
        if let Api::CxxFunction(efn) = api {
            check_mut_return_restriction(cx, efn);
        }
        if let Api::CxxFunction(efn) | Api::RustFunction(efn) = api {
            check_multiple_arg_lifetimes(cx, efn);
        }
    }

    ident::check_all(cx.apis, cx.errors);
}

impl Check<'_> {
    fn error(&mut self, sp: impl ToTokens, msg: impl Display) {
        self.errors.push(Error::new_spanned(sp, msg));
    }
}

fn check_type_ident(cx: &mut Check, ident: &Ident) {
    if Atom::from(ident).is_none()
        && !cx.types.structs.contains_key(ident)
        && !cx.types.cxx.contains(ident)
        && !cx.types.rust.contains(ident)
    {
        cx.error(ident, "unsupported type");
    }
}

fn check_type_box(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.cxx.contains(ident) {
            cx.error(ptr, error::BOX_CXX_TYPE.msg);
        }

        if Atom::from(ident).is_none() {
            return;
        }
    }

    cx.error(ptr, "unsupported target type of Box");
}

fn check_type_rust_vec(cx: &mut Check, ptr: &Ty1) {
    // Vec can contain either user-defined type or u8
    if let Type::Ident(ident) = &ptr.inner {
        if Atom::from(ident).map(is_valid_vector_element) == Some(true) {
            return;
        } else if cx.types.cxx.contains(ident) {
            cx.error(ptr, error::VEC_CXX_TYPE.msg);
        } else {
            return;
        }
    }

    cx.error(ptr, "unsupported target type of Vec");
}

fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.rust.contains(ident) {
            cx.error(ptr, "unique_ptr of a Rust type is not supported yet");
        }

        match Atom::from(ident) {
            None | Some(CxxString) => return,
            _ => {}
        }
    } else if let Type::CxxVector(_) = &ptr.inner {
        return;
    }

    cx.error(ptr, "unsupported unique_ptr target type");
}

fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.rust.contains(ident) {
            cx.error(ptr, "vector of a Rust type is not supported yet");
        }

        match Atom::from(ident) {
            None => return,
            Some(atom) => {
                if is_valid_vector_element(atom) {
                    return;
                }
            }
        }
    }
    cx.error(ptr, "unsupported vector target type");
}

fn check_type_ref(cx: &mut Check, ty: &Ref) {
    if ty.lifetime.is_some() {
        cx.error(ty, "references with explicit lifetimes are not supported");
    }

    match ty.inner {
        Type::Fn(_) | Type::Void(_) => {}
        _ => return,
    }

    cx.error(ty, "unsupported reference type");
}

fn check_type_slice(cx: &mut Check, ty: &Slice) {
    cx.error(ty, "only &[u8] is supported so far, not other slice types");
}

fn check_api_struct(cx: &mut Check, strct: &Struct) {
    check_reserved_name(cx, &strct.ident);

    if strct.fields.is_empty() {
        let span = span_for_struct_error(strct);
        cx.error(span, "structs without any fields are not supported");
    }

    for field in &strct.fields {
        if is_unsized(cx, &field.ty) {
            let desc = describe(cx, &field.ty);
            let msg = format!("using {} by value is not supported", desc);
            cx.error(field, msg);
        }
        if let Type::Fn(_) = field.ty {
            cx.error(
                field,
                "function pointers in a struct field are not implemented yet",
            );
        }
    }
}

fn check_api_type(cx: &mut Check, ty: &ExternType) {
    check_reserved_name(cx, &ty.ident);
}

fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
    if let Some(receiver) = &efn.receiver {
        let ref span = span_for_receiver_error(receiver);

        if receiver.ty == "Self" {
            let mutability = match receiver.mutability {
                Some(_) => "mut ",
                None => "",
            };
            let msg = format!(
                "unnamed receiver type is only allowed if the surrounding \
                 extern block contains exactly one extern type; \
                 use `self: &{mutability}TheType`",
                mutability = mutability,
            );
            cx.error(span, msg);
        } else if !cx.types.structs.contains_key(&receiver.ty)
            && !cx.types.cxx.contains(&receiver.ty)
            && !cx.types.rust.contains(&receiver.ty)
        {
            cx.error(span, "unrecognized receiver type");
        }

        if receiver.lifetime.is_some() {
            cx.error(span, "references with explicit lifetimes are not supported");
        }
    }

    for arg in &efn.args {
        if is_unsized(cx, &arg.ty) {
            let desc = describe(cx, &arg.ty);
            let msg = format!("passing {} by value is not supported", desc);
            cx.error(arg, msg);
        }
        if let Type::Fn(_) = arg.ty {
            if efn.lang == Lang::Rust {
                cx.error(
                    arg,
                    "passing a function pointer from C++ to Rust is not implemented yet",
                );
            }
        }
    }

    if let Some(ty) = &efn.ret {
        if is_unsized(cx, ty) {
            let desc = describe(cx, ty);
            let msg = format!("returning {} by value is not supported", desc);
            cx.error(ty, msg);
        }
        if let Type::Fn(_) = ty {
            cx.error(ty, "returning a function pointer is not implemented yet");
        }
    }
}

fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) {
    match &efn.ret {
        Some(Type::Ref(ty)) if ty.mutability.is_some() => {}
        _ => return,
    }

    for arg in &efn.args {
        if let Type::Ref(ty) = &arg.ty {
            if ty.mutability.is_some() {
                return;
            }
        }
    }

    cx.error(
        efn,
        "&mut return type is not allowed unless there is a &mut argument",
    );
}

fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) {
    match &efn.ret {
        Some(Type::Ref(_)) => {}
        _ => return,
    }

    let mut reference_args = 0;
    for arg in &efn.args {
        if let Type::Ref(_) = &arg.ty {
            reference_args += 1;
        }
    }

    if efn.receiver.is_some() {
        reference_args += 1;
    }

    if reference_args != 1 {
        cx.error(
            efn,
            "functions that return a reference must take exactly one input reference",
        );
    }
}

fn check_reserved_name(cx: &mut Check, ident: &Ident) {
    if ident == "Box" || ident == "UniquePtr" || Atom::from(ident).is_some() {
        cx.error(ident, "reserved name");
    }
}

fn is_unsized(cx: &mut Check, ty: &Type) -> bool {
    let ident = match ty {
        Type::Ident(ident) => ident,
        Type::Slice(_) | Type::Void(_) => return true,
        _ => return false,
    };
    ident == CxxString || cx.types.cxx.contains(ident) || cx.types.rust.contains(ident)
}

fn is_valid_vector_element(atom: Atom) -> bool {
    atom == U8
        || atom == U16
        || atom == U32
        || atom == U64
        || atom == Usize
        || atom == I8
        || atom == I16
        || atom == I32
        || atom == I64
        || atom == Isize
        || atom == F32
        || atom == F64
}

fn span_for_struct_error(strct: &Struct) -> TokenStream {
    let struct_token = strct.struct_token;
    let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
    brace_token.set_span(strct.brace_token.span);
    quote!(#struct_token #brace_token)
}

fn span_for_receiver_error(receiver: &Receiver) -> TokenStream {
    let ampersand = receiver.ampersand;
    let lifetime = &receiver.lifetime;
    let mutability = receiver.mutability;
    if receiver.shorthand {
        let var = receiver.var;
        quote!(#ampersand #lifetime #mutability #var)
    } else {
        let ty = &receiver.ty;
        quote!(#ampersand #lifetime #mutability #ty)
    }
}

fn combine_errors(errors: Vec<Error>) -> Result<()> {
    let mut iter = errors.into_iter();
    let mut all_errors = match iter.next() {
        Some(err) => err,
        None => return Ok(()),
    };
    for err in iter {
        all_errors.combine(err);
    }
    Err(all_errors)
}

fn describe(cx: &mut Check, ty: &Type) -> String {
    match ty {
        Type::Ident(ident) => {
            if cx.types.structs.contains_key(ident) {
                "struct".to_owned()
            } else if cx.types.cxx.contains(ident) {
                "C++ type".to_owned()
            } else if cx.types.rust.contains(ident) {
                "opaque Rust type".to_owned()
            } else if Atom::from(ident) == Some(CxxString) {
                "C++ string".to_owned()
            } else {
                ident.to_string()
            }
        }
        Type::RustBox(_) => "Box".to_owned(),
        Type::RustVec(_) => "Vec".to_owned(),
        Type::UniquePtr(_) => "unique_ptr".to_owned(),
        Type::Ref(_) => "reference".to_owned(),
        Type::Str(_) => "&str".to_owned(),
        Type::CxxVector(_) => "vector".to_owned(),
        Type::Slice(_) => "slice".to_owned(),
        Type::SliceRefU8(_) => "&[u8]".to_owned(),
        Type::Fn(_) => "function pointer".to_owned(),
        Type::Void(_) => "()".to_owned(),
    }
}
<commit_msg>Express is_valid_vector_element more compactly<commit_after>use crate::syntax::atom::Atom::{self, *};
use crate::syntax::{
    error, ident, Api, ExternFn, ExternType, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, Types,
};
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
use quote::{quote, ToTokens};
use std::fmt::Display;
use syn::{Error, Result};

struct Check<'a> {
    apis: &'a [Api],
    types: &'a Types<'a>,
    errors: &'a mut Vec<Error>,
}

pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> {
    let mut errors = Vec::new();
    let mut cx = Check {
        apis,
        types,
        errors: &mut errors,
    };
    do_typecheck(&mut cx);
    combine_errors(errors)
}

fn do_typecheck(cx: &mut Check) {
    for ty in cx.types {
        match ty {
            Type::Ident(ident) => check_type_ident(cx, ident),
            Type::RustBox(ptr) => check_type_box(cx, ptr),
            Type::RustVec(ptr) => check_type_rust_vec(cx, ptr),
            Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr),
            Type::CxxVector(ptr) => check_type_cxx_vector(cx, ptr),
            Type::Ref(ty) => check_type_ref(cx, ty),
            Type::Slice(ty) => check_type_slice(cx, ty),
            _ => {}
        }
    }

    for api in cx.apis {
        match api {
            Api::Struct(strct) => check_api_struct(cx, strct),
            Api::CxxType(ty) | Api::RustType(ty) => check_api_type(cx, ty),
            Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn),
            _ => {}
        }
    }

    for api in cx.apis {
        if let Api::CxxFunction(efn) = api {
            check_mut_return_restriction(cx, efn);
        }
        if let Api::CxxFunction(efn) | Api::RustFunction(efn) = api {
            check_multiple_arg_lifetimes(cx, efn);
        }
    }

    ident::check_all(cx.apis, cx.errors);
}

impl Check<'_> {
    fn error(&mut self, sp: impl ToTokens, msg: impl Display) {
        self.errors.push(Error::new_spanned(sp, msg));
    }
}

fn check_type_ident(cx: &mut Check, ident: &Ident) {
    if Atom::from(ident).is_none()
        && !cx.types.structs.contains_key(ident)
        && !cx.types.cxx.contains(ident)
        && !cx.types.rust.contains(ident)
    {
        cx.error(ident, "unsupported type");
    }
}

fn check_type_box(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.cxx.contains(ident) {
            cx.error(ptr, error::BOX_CXX_TYPE.msg);
        }

        if Atom::from(ident).is_none() {
            return;
        }
    }

    cx.error(ptr, "unsupported target type of Box");
}

fn check_type_rust_vec(cx: &mut Check, ptr: &Ty1) {
    // Vec can contain either user-defined type or u8
    if let Type::Ident(ident) = &ptr.inner {
        if Atom::from(ident).map(is_valid_vector_element) == Some(true) {
            return;
        } else if cx.types.cxx.contains(ident) {
            cx.error(ptr, error::VEC_CXX_TYPE.msg);
        } else {
            return;
        }
    }

    cx.error(ptr, "unsupported target type of Vec");
}

fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.rust.contains(ident) {
            cx.error(ptr, "unique_ptr of a Rust type is not supported yet");
        }

        match Atom::from(ident) {
            None | Some(CxxString) => return,
            _ => {}
        }
    } else if let Type::CxxVector(_) = &ptr.inner {
        return;
    }

    cx.error(ptr, "unsupported unique_ptr target type");
}

fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.rust.contains(ident) {
            cx.error(ptr, "vector of a Rust type is not supported yet");
        }

        match Atom::from(ident) {
            None => return,
            Some(atom) => {
                if is_valid_vector_element(atom) {
                    return;
                }
            }
        }
    }
    cx.error(ptr, "unsupported vector target type");
}

fn check_type_ref(cx: &mut Check, ty: &Ref) {
    if ty.lifetime.is_some() {
        cx.error(ty, "references with explicit lifetimes are not supported");
    }

    match ty.inner {
        Type::Fn(_) | Type::Void(_) => {}
        _ => return,
    }

    cx.error(ty, "unsupported reference type");
}

fn check_type_slice(cx: &mut Check, ty: &Slice) {
    cx.error(ty, "only &[u8] is supported so far, not other slice types");
}

fn check_api_struct(cx: &mut Check, strct: &Struct) {
    check_reserved_name(cx, &strct.ident);

    if strct.fields.is_empty() {
        let span = span_for_struct_error(strct);
        cx.error(span, "structs without any fields are not supported");
    }

    for field in &strct.fields {
        if is_unsized(cx, &field.ty) {
            let desc = describe(cx, &field.ty);
            let msg = format!("using {} by value is not supported", desc);
            cx.error(field, msg);
        }
        if let Type::Fn(_) = field.ty {
            cx.error(
                field,
                "function pointers in a struct field are not implemented yet",
            );
        }
    }
}

fn check_api_type(cx: &mut Check, ty: &ExternType) {
    check_reserved_name(cx, &ty.ident);
}

fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
    if let Some(receiver) = &efn.receiver {
        let ref span = span_for_receiver_error(receiver);

        if receiver.ty == "Self" {
            let mutability = match receiver.mutability {
                Some(_) => "mut ",
                None => "",
            };
            let msg = format!(
                "unnamed receiver type is only allowed if the surrounding \
                 extern block contains exactly one extern type; \
                 use `self: &{mutability}TheType`",
                mutability = mutability,
            );
            cx.error(span, msg);
        } else if !cx.types.structs.contains_key(&receiver.ty)
            && !cx.types.cxx.contains(&receiver.ty)
            && !cx.types.rust.contains(&receiver.ty)
        {
            cx.error(span, "unrecognized receiver type");
        }

        if receiver.lifetime.is_some() {
            cx.error(span, "references with explicit lifetimes are not supported");
        }
    }

    for arg in &efn.args {
        if is_unsized(cx, &arg.ty) {
            let desc = describe(cx, &arg.ty);
            let msg = format!("passing {} by value is not supported", desc);
            cx.error(arg, msg);
        }
        if let Type::Fn(_) = arg.ty {
            if efn.lang == Lang::Rust {
                cx.error(
                    arg,
                    "passing a function pointer from C++ to Rust is not implemented yet",
                );
            }
        }
    }

    if let Some(ty) = &efn.ret {
        if is_unsized(cx, ty) {
            let desc = describe(cx, ty);
            let msg = format!("returning {} by value is not supported", desc);
            cx.error(ty, msg);
        }
        if let Type::Fn(_) = ty {
            cx.error(ty, "returning a function pointer is not implemented yet");
        }
    }
}

fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) {
    match &efn.ret {
        Some(Type::Ref(ty)) if ty.mutability.is_some() => {}
        _ => return,
    }

    for arg in &efn.args {
        if let Type::Ref(ty) = &arg.ty {
            if ty.mutability.is_some() {
                return;
            }
        }
    }

    cx.error(
        efn,
        "&mut return type is not allowed unless there is a &mut argument",
    );
}

fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) {
    match &efn.ret {
        Some(Type::Ref(_)) => {}
        _ => return,
    }

    let mut reference_args = 0;
    for arg in &efn.args {
        if let Type::Ref(_) = &arg.ty {
            reference_args += 1;
        }
    }

    if efn.receiver.is_some() {
        reference_args += 1;
    }

    if reference_args != 1 {
        cx.error(
            efn,
            "functions that return a reference must take exactly one input reference",
        );
    }
}

fn check_reserved_name(cx: &mut Check, ident: &Ident) {
    if ident == "Box" || ident == "UniquePtr" || Atom::from(ident).is_some() {
        cx.error(ident, "reserved name");
    }
}

fn is_unsized(cx: &mut Check, ty: &Type) -> bool {
    let ident = match ty {
        Type::Ident(ident) => ident,
        Type::Slice(_) | Type::Void(_) => return true,
        _ => return false,
    };
    ident == CxxString || cx.types.cxx.contains(ident) || cx.types.rust.contains(ident)
}

fn is_valid_vector_element(atom: Atom) -> bool {
    match atom {
        U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize | F32 | F64 => true,
        Bool | CxxString | RustString => false,
    }
}

fn span_for_struct_error(strct: &Struct) -> TokenStream {
    let struct_token = strct.struct_token;
    let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
    brace_token.set_span(strct.brace_token.span);
    quote!(#struct_token #brace_token)
}

fn span_for_receiver_error(receiver: &Receiver) -> TokenStream {
    let ampersand = receiver.ampersand;
    let lifetime = &receiver.lifetime;
    let mutability = receiver.mutability;
    if receiver.shorthand {
        let var = receiver.var;
        quote!(#ampersand #lifetime #mutability #var)
    } else {
        let ty = &receiver.ty;
        quote!(#ampersand #lifetime #mutability #ty)
    }
}

fn combine_errors(errors: Vec<Error>) -> Result<()> {
    let mut iter = errors.into_iter();
    let mut all_errors = match iter.next() {
        Some(err) => err,
        None => return Ok(()),
    };
    for err in iter {
        all_errors.combine(err);
    }
    Err(all_errors)
}

fn describe(cx: &mut Check, ty: &Type) -> String {
    match ty {
        Type::Ident(ident) => {
            if cx.types.structs.contains_key(ident) {
                "struct".to_owned()
            } else if cx.types.cxx.contains(ident) {
                "C++ type".to_owned()
            } else if cx.types.rust.contains(ident) {
                "opaque Rust type".to_owned()
            } else if Atom::from(ident) == Some(CxxString) {
                "C++ string".to_owned()
            } else {
                ident.to_string()
            }
        }
        Type::RustBox(_) => "Box".to_owned(),
        Type::RustVec(_) => "Vec".to_owned(),
        Type::UniquePtr(_) => "unique_ptr".to_owned(),
        Type::Ref(_) => "reference".to_owned(),
        Type::Str(_) => "&str".to_owned(),
        Type::CxxVector(_) => "vector".to_owned(),
        Type::Slice(_) => "slice".to_owned(),
        Type::SliceRefU8(_) => "&[u8]".to_owned(),
        Type::Fn(_) => "function pointer".to_owned(),
        Type::Void(_) => "()".to_owned(),
    }
}
<|endoftext|>


<commit_before>use crate::syntax::atom::Atom::{self, *};
use crate::syntax::set::OrderedSet as Set;
use crate::syntax::{Api, Derive, Enum, ExternFn, ExternType, Struct, Type};
use proc_macro2::Ident;
use std::collections::{BTreeMap as Map, HashSet as UnorderedSet};
use syn::{Error, Result};

pub struct Types<'a> {
    pub all: Set<'a, Type>,
    pub structs: Map<Ident, &'a Struct>,
    pub enums: Map<Ident, &'a Enum>,
    pub cxx: Set<'a, Ident>,
    pub rust: Set<'a, Ident>,
}

impl<'a> Types<'a> {
    pub fn collect(apis: &'a [Api]) -> Result<Self> {
        let mut all = Set::new();
        let mut structs = Map::new();
        let mut enums = Map::new();
        let mut cxx = Set::new();
        let mut rust = Set::new();

        fn visit<'a>(all: &mut Set<'a, Type>, ty: &'a Type) {
            all.insert(ty);
            match ty {
                Type::Ident(_) | Type::Str(_) | Type::Void(_) | Type::SliceRefU8(_) => {}
                Type::RustBox(ty)
                | Type::UniquePtr(ty)
                | Type::CxxVector(ty)
                | Type::RustVec(ty) => visit(all, &ty.inner),
                Type::Ref(r) => visit(all, &r.inner),
                Type::Slice(s) => visit(all, &s.inner),
                Type::Fn(f) => {
                    if let Some(ret) = &f.ret {
                        visit(all, ret);
                    }
                    for arg in &f.args {
                        visit(all, &arg.ty);
                    }
                }
            }
        }

        let mut type_names = UnorderedSet::new();
        let mut function_names = UnorderedSet::new();
        for api in apis {
            match api {
                Api::Include(_) => {}
                Api::Struct(strct) => {
                    if !type_names.insert(&strct.ident) {
                        return Err(duplicate_struct(strct));
                    }
                    structs.insert(strct.ident.clone(), strct);
                    for field in &strct.fields {
                        visit(&mut all, &field.ty);
                    }
                }
                Api::Enum(enm) => {
                    if !type_names.insert(&enm.ident) {
                        return Err(duplicate_enum(enm));
                    }
                    enums.insert(enm.ident.clone(), enm);
                }
                Api::CxxType(ety) => {
                    if !type_names.insert(&ety.ident) {
                        return Err(duplicate_type(ety));
                    }
                    cxx.insert(&ety.ident);
                }
                Api::RustType(ety) => {
                    if !type_names.insert(&ety.ident) {
                        return Err(duplicate_type(ety));
                    }
                    rust.insert(&ety.ident);
                }
                Api::CxxFunction(efn) | Api::RustFunction(efn) => {
                    if !function_names.insert((&efn.receiver, &efn.ident)) {
                        return Err(duplicate_function(efn));
                    }
                    for arg in &efn.args {
                        visit(&mut all, &arg.ty);
                    }
                    if let Some(ret) = &efn.ret {
                        visit(&mut all, ret);
                    }
                }
            }
        }

        Ok(Types {
            all,
            structs,
            enums,
            cxx,
            rust,
        })
    }

    pub fn needs_indirect_abi(&self, ty: &Type) -> bool {
        match ty {
            Type::Ident(ident) => {
                if let Some(strct) = self.structs.get(ident) {
                    !self.is_pod(strct)
                } else {
                    Atom::from(ident) == Some(RustString)
                }
            }
            Type::RustVec(_) => true,
            _ => false,
        }
    }

    pub fn is_pod(&self, strct: &Struct) -> bool {
        for derive in &strct.derives {
            if *derive == Derive::Copy {
                return true;
            }
        }
        false
    }
}

impl<'t, 'a> IntoIterator for &'t Types<'a> {
    type Item = &'a Type;
    type IntoIter = crate::syntax::set::Iter<'t, 'a, Type>;
    fn into_iter(self) -> Self::IntoIter {
        self.all.into_iter()
    }
}

fn duplicate_struct(strct: &Struct) -> Error {
    Error::new_spanned(strct, "duplicate type")
}

fn duplicate_enum(enm: &Enum) -> Error {
    Error::new_spanned(enm, "duplicate type")
}

fn duplicate_type(ety: &ExternType) -> Error {
    Error::new_spanned(ety, "duplicate type")
}

fn duplicate_function(efn: &ExternFn) -> Error {
    Error::new_spanned(efn, "duplicate function")
}
<commit_msg>Include conflicting name in duplicate name error message<commit_after>use crate::syntax::atom::Atom::{self, *};
use crate::syntax::set::OrderedSet as Set;
use crate::syntax::{Api, Derive, Enum, Struct, Type};
use proc_macro2::Ident;
use quote::ToTokens;
use std::collections::{BTreeMap as Map, HashSet as UnorderedSet};
use syn::{Error, Result};

pub struct Types<'a> {
    pub all: Set<'a, Type>,
    pub structs: Map<Ident, &'a Struct>,
    pub enums: Map<Ident, &'a Enum>,
    pub cxx: Set<'a, Ident>,
    pub rust: Set<'a, Ident>,
}

impl<'a> Types<'a> {
    pub fn collect(apis: &'a [Api]) -> Result<Self> {
        let mut all = Set::new();
        let mut structs = Map::new();
        let mut enums = Map::new();
        let mut cxx = Set::new();
        let mut rust = Set::new();

        fn visit<'a>(all: &mut Set<'a, Type>, ty: &'a Type) {
            all.insert(ty);
            match ty {
                Type::Ident(_) | Type::Str(_) | Type::Void(_) | Type::SliceRefU8(_) => {}
                Type::RustBox(ty)
                | Type::UniquePtr(ty)
                | Type::CxxVector(ty)
                | Type::RustVec(ty) => visit(all, &ty.inner),
                Type::Ref(r) => visit(all, &r.inner),
                Type::Slice(s) => visit(all, &s.inner),
                Type::Fn(f) => {
                    if let Some(ret) = &f.ret {
                        visit(all, ret);
                    }
                    for arg in &f.args {
                        visit(all, &arg.ty);
                    }
                }
            }
        }

        let mut type_names = UnorderedSet::new();
        let mut function_names = UnorderedSet::new();
        for api in apis {
            match api {
                Api::Include(_) => {}
                Api::Struct(strct) => {
                    let ident = &strct.ident;
                    if !type_names.insert(ident) {
                        return Err(duplicate_name(strct, ident));
                    }
                    structs.insert(ident.clone(), strct);
                    for field in &strct.fields {
                        visit(&mut all, &field.ty);
                    }
                }
                Api::Enum(enm) => {
                    let ident = &enm.ident;
                    if !type_names.insert(ident) {
                        return Err(duplicate_name(enm, ident));
                    }
                    enums.insert(ident.clone(), enm);
                }
                Api::CxxType(ety) => {
                    let ident = &ety.ident;
                    if !type_names.insert(ident) {
                        return Err(duplicate_name(ety, ident));
                    }
                    cxx.insert(ident);
                }
                Api::RustType(ety) => {
                    let ident = &ety.ident;
                    if !type_names.insert(ident) {
                        return Err(duplicate_name(ety, ident));
                    }
                    rust.insert(ident);
                }
                Api::CxxFunction(efn) | Api::RustFunction(efn) => {
                    let ident = &efn.ident;
                    if !function_names.insert((&efn.receiver, ident)) {
                        return Err(duplicate_name(efn, ident));
                    }
                    for arg in &efn.args {
                        visit(&mut all, &arg.ty);
                    }
                    if let Some(ret) = &efn.ret {
                        visit(&mut all, ret);
                    }
                }
            }
        }

        Ok(Types {
            all,
            structs,
            enums,
            cxx,
            rust,
        })
    }

    pub fn needs_indirect_abi(&self, ty: &Type) -> bool {
        match ty {
            Type::Ident(ident) => {
                if let Some(strct) = self.structs.get(ident) {
                    !self.is_pod(strct)
                } else {
                    Atom::from(ident) == Some(RustString)
                }
            }
            Type::RustVec(_) => true,
            _ => false,
        }
    }

    pub fn is_pod(&self, strct: &Struct) -> bool {
        for derive in &strct.derives {
            if *derive == Derive::Copy {
                return true;
            }
        }
        false
    }
}

impl<'t, 'a> IntoIterator for &'t Types<'a> {
    type Item = &'a Type;
    type IntoIter = crate::syntax::set::Iter<'t, 'a, Type>;
    fn into_iter(self) -> Self::IntoIter {
        self.all.into_iter()
    }
}

fn duplicate_name(sp: impl ToTokens, ident: &Ident) -> Error {
    let msg = format!("the name `{}` is defined multiple times", ident);
    Error::new_spanned(sp, msg)
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::iter;
use std::iter::Empty as EmptyIter;
use std::option::IntoIter as OptionIntoIter;
use std::sync::Arc;
//use std::vec::IntoIter as VecIntoIter;

use descriptor::descriptor_set::DescriptorSet;

/// A collection of descriptor set objects.
pub unsafe trait DescriptorSetsCollection {
    /// An iterator that produces the list of descriptor set objects contained in this collection.
    type Iter: ExactSizeIterator<Item = Arc<DescriptorSet>>;

    /// Returns the list of descriptor set objects of this collection.
    fn list(&self) -> Self::Iter;
}

unsafe impl DescriptorSetsCollection for () {
    type Iter = EmptyIter<Arc<DescriptorSet>>;

    #[inline]
    fn list(&self) -> Self::Iter {
        iter::empty()
    }
}

unsafe impl<'a, T> DescriptorSetsCollection for Arc<T>
    where T: DescriptorSet
{
    type Iter = OptionIntoIter<Arc<DescriptorSet>>;

    #[inline]
    fn list(&self) -> Self::Iter {
        Some(self.clone() as Arc<_>).into_iter()
    }
}

unsafe impl<'a, T> DescriptorSetsCollection for &'a Arc<T>
    where T: DescriptorSet
{
    type Iter = OptionIntoIter<Arc<DescriptorSet>>;

    #[inline]
    fn list(&self) -> Self::Iter {
        Some((*self).clone() as Arc<_>).into_iter()
    }
}

macro_rules! impl_collection {
    ($first:ident $(, $others:ident)*) => (
        /*unsafe impl<'a, $first$(, $others)*> DescriptorSetsCollection for
                                                        (&'a Arc<$first>, $(&'a Arc<$others>),*)
        {
            type Iter = VecIntoIter<Arc<DescriptorSet>>;

            #[inline]
            fn list(&self) -> Self::Iter {
                let list = vec![
                    $first,
                    $($others),*
                ];

                list.into_iter()
            }
        }*/

        impl_collection!($($others),*);
    );

    () => ();
}

impl_collection!(Z, Y, X, W, V, U, T, S, R, Q, P, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A);
<commit_msg>Restore DescriptorSetsCollection impl for tuples<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::iter;
use std::iter::Empty as EmptyIter;
use std::option::IntoIter as OptionIntoIter;
use std::sync::Arc;
use std::vec::IntoIter as VecIntoIter;

use descriptor::descriptor_set::DescriptorSet;

/// A collection of descriptor set objects.
pub unsafe trait DescriptorSetsCollection {
    /// An iterator that produces the list of descriptor set objects contained in this collection.
    type Iter: ExactSizeIterator<Item = Arc<DescriptorSet>>;

    /// Returns the list of descriptor set objects of this collection.
    fn list(&self) -> Self::Iter;
}

unsafe impl DescriptorSetsCollection for () {
    type Iter = EmptyIter<Arc<DescriptorSet>>;

    #[inline]
    fn list(&self) -> Self::Iter {
        iter::empty()
    }
}

unsafe impl<'a, T> DescriptorSetsCollection for Arc<T>
    where T: DescriptorSet
{
    type Iter = OptionIntoIter<Arc<DescriptorSet>>;

    #[inline]
    fn list(&self) -> Self::Iter {
        Some(self.clone() as Arc<_>).into_iter()
    }
}

unsafe impl<'a, T> DescriptorSetsCollection for &'a Arc<T>
    where T: DescriptorSet
{
    type Iter = OptionIntoIter<Arc<DescriptorSet>>;

    #[inline]
    fn list(&self) -> Self::Iter {
        Some((*self).clone() as Arc<_>).into_iter()
    }
}

macro_rules! impl_collection {
    ($first:ident $(, $others:ident)*) => (
        unsafe impl<'a, $first$(, $others)*> DescriptorSetsCollection for
                                                        (&'a Arc<$first>, $(&'a Arc<$others>),*)
            where $first: DescriptorSet + 'static $(, $others: DescriptorSet + 'static)*
        {
            type Iter = VecIntoIter<Arc<DescriptorSet>>;

            #[inline]
            fn list(&self) -> Self::Iter {
                #![allow(non_snake_case)]
                let ($first, $($others),*) = *self;

                let list = vec![
                    $first.clone() as Arc<_>,
                    $($others.clone() as Arc<_>),*
                ];

                list.into_iter()
            }
        }

        impl_collection!($($others),*);
    );

    () => ();
}

impl_collection!(Z, Y, X, W, V, U, T, S, R, Q, P, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A);
<|endoftext|>


<commit_before>extern crate hex;
extern crate serde_json;

use errors::prelude::*;
use self::hex::ToHex;
use self::serde_json::Value;
use utils::crypto::hash::Hash;
use domain::ledger::constants::{ATTRIB, GET_ATTR};

pub fn serialize_signature(v: Value) -> Result<String, IndyError> {
    let _type = v["operation"]["type"].clone();
    _serialize_signature(v, true, _type.as_str())
}

fn _serialize_signature(v: Value, is_top_level: bool, _type: Option<&str>) -> Result<String, IndyError> {
    match v {
        Value::Bool(value) => Ok(if value { "True".to_string() } else { "False".to_string() }),
        Value::Number(value) => Ok(value.to_string()),
        Value::String(value) => Ok(value),
        Value::Array(array) => {
            let mut result = "".to_string();
            let length = array.len();
            for (index, element) in array.iter().enumerate() {
                result += &_serialize_signature(element.clone(), false, _type)?;
                if index < length - 1 {
                    result += ",";
                }
            }
            Ok(result)
        }
        Value::Object(map) => {
            let mut result = "".to_string();
            let mut in_middle = false;
            for key in map.keys() {
                // Skip signature field at top level as in python code
                if is_top_level && (key == "signature" || key == "fees" || key == "signatures") { continue; }

                if in_middle {
                    result += "|";
                }

                let mut value = map[key].clone();
                if (_type == Some(ATTRIB) || _type == Some(GET_ATTR)) && (key == "raw" || key == "hash" || key == "enc") {
                    // do it only for attribute related request
                    let mut ctx = Hash::new_context()?;

                    ctx.update(&value
                        .as_str()
                        .ok_or(IndyError::from_msg(IndyErrorKind::InvalidState, "Cannot update hash context"))?
                        .as_bytes())?;

                    value = Value::String(ctx.finish()?.as_ref().to_hex());
                }
                result = result + key + ":" + &_serialize_signature(value, false, _type)?;
                in_middle = true;
            }
            Ok(result)
        }
        _ => Ok("".to_string())
    }
}

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

    #[test]
    fn signature_serialize_works() {
        let data = r#"{
                        "name": "John Doe",
                        "age": 43,
                        "operation": {
                            "dest": 54
                        },
                        "phones": [
                          "1234567",
                          "2345678",
                          {"rust": 5, "age": 1},
                          3
                        ]
                    }"#;
        let msg: Value = serde_json::from_str(data).unwrap();

        let result = "age:43|name:John Doe|operation:dest:54|phones:1234567,2345678,age:1|rust:5,3";

        assert_eq!(serialize_signature(msg).unwrap(), result)
    }

    #[test]
    fn signature_serialize_works_for_skipped_fields() {
        let data = r#"{
                        "name": "John Doe",
                        "age": 43,
                        "operation": {
                            "type": "100",
                            "hash": "cool hash",
                            "dest": 54
                        },
			"fees": "fees1",
			"signature": "sign1",
			"signatures": "sign-m",
                        "phones": [
                          "1234567",
                          "2345678",
                          {"rust": 5, "age": 1},
                          3
                        ]
                    }"#;
        let msg: Value = serde_json::from_str(data).unwrap();

        let result = "age:43|name:John Doe|operation:dest:54|hash:46aa0c92129b33ee72ee1478d2ae62fa6e756869dedc6c858af3214a6fcf1904|type:100|phones:1234567,2345678,age:1|rust:5,3";

        assert_eq!(serialize_signature(msg).unwrap(), result)
    }

    #[test]
    fn signature_serialize_works_with_raw_hash_for_attrib_related_type() {
        let data = r#"{
                        "name": "John Doe",
                        "age": 43,
                        "operation": {
                            "type": "100",
                            "hash": "cool hash",
                            "dest": 54,
                            "raw": "string for hash"
                        },
                        "phones": [
                          "1234567",
                          "2345678",
                          {"rust": 5, "age": 1},
                          3
                        ]
                    }"#;
        let msg: Value = serde_json::from_str(data).unwrap();

        let result = "age:43|name:John Doe|operation:dest:54|hash:46aa0c92129b33ee72ee1478d2ae62fa6e756869dedc6c858af3214a6fcf1904|raw:1dcd0759ce38f57049344a6b3c5fc18144fca1724713090c2ceeffa788c02711|type:100|phones:1234567,2345678,age:1|rust:5,3";

        assert_eq!(serialize_signature(msg).unwrap(), result)
    }

    #[test]
    fn signature_serialize_works_with_raw_hash_for_not_attrib_related_type() {
        let data = r#"{
                        "name": "John Doe",
                        "age": 43,
                        "operation": {
                            "type": "101",
                            "hash": "cool hash",
                            "dest": 54,
                            "raw": "string for hash"
                        },
                        "phones": [
                          "1234567",
                          "2345678",
                          {"rust": 5, "age": 1},
                          3
                        ]
                    }"#;
        let msg: Value = serde_json::from_str(data).unwrap();

        let result = "age:43|name:John Doe|operation:dest:54|hash:cool hash|raw:string for hash|type:101|phones:1234567,2345678,age:1|rust:5,3";

        assert_eq!(serialize_signature(msg).unwrap(), result)
    }

    #[test]
    fn signature_serialize_works_with_null() {
        let data = r#"{"signature": null}"#;
        let v: serde_json::Value = serde_json::from_str(data).unwrap();
        let serialized = serialize_signature(v).unwrap();
        assert_eq!(serialized, "");
    }
}
<commit_msg>little refactoring<commit_after>extern crate hex;
extern crate serde_json;

use errors::prelude::*;
use self::hex::ToHex;
use self::serde_json::Value;
use utils::crypto::hash::Hash;
use domain::ledger::constants::{ATTRIB, GET_ATTR};

pub fn serialize_signature(v: Value) -> Result<String, IndyError> {
    let _type = v["operation"]["type"].clone();
    _serialize_signature(v, true, _type.as_str())
}

fn _serialize_signature(v: Value, is_top_level: bool, _type: Option<&str>) -> Result<String, IndyError> {
    match v {
        Value::Bool(value) => Ok(if value { "True".to_string() } else { "False".to_string() }),
        Value::Number(value) => Ok(value.to_string()),
        Value::String(value) => Ok(value),
        Value::Array(array) => {
            array
                .into_iter()
                .map(|element| _serialize_signature(element, false, _type))
                .collect::<Result<Vec<String>, IndyError>>()
                .map(|res| res.join(","))
        }
        Value::Object(map) => {
            let mut result = "".to_string();
            let mut in_middle = false;
            for key in map.keys() {
                // Skip signature field at top level as in python code
                if is_top_level && (key == "signature" || key == "fees" || key == "signatures") { continue; }

                if in_middle {
                    result += "|";
                }

                let mut value = map[key].clone();
                if (_type == Some(ATTRIB) || _type == Some(GET_ATTR)) && (key == "raw" || key == "hash" || key == "enc") {
                    // do it only for attribute related request
                    let mut ctx = Hash::new_context()?;

                    ctx.update(&value
                        .as_str()
                        .ok_or(IndyError::from_msg(IndyErrorKind::InvalidState, "Cannot update hash context"))?
                        .as_bytes())?;

                    value = Value::String(ctx.finish()?.as_ref().to_hex());
                }
                result = result + key + ":" + &_serialize_signature(value, false, _type)?;
                in_middle = true;
            }
            Ok(result)
        }
        _ => Ok("".to_string())
    }
}

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

    #[test]
    fn signature_serialize_works() {
        let data = r#"{
                        "name": "John Doe",
                        "age": 43,
                        "operation": {
                            "dest": 54
                        },
                        "phones": [
                          "1234567",
                          "2345678",
                          {"rust": 5, "age": 1},
                          3
                        ]
                    }"#;
        let msg: Value = serde_json::from_str(data).unwrap();

        let result = "age:43|name:John Doe|operation:dest:54|phones:1234567,2345678,age:1|rust:5,3";

        assert_eq!(serialize_signature(msg).unwrap(), result)
    }

    #[test]
    fn signature_serialize_works_for_skipped_fields() {
        let data = r#"{
                        "name": "John Doe",
                        "age": 43,
                        "operation": {
                            "type": "100",
                            "hash": "cool hash",
                            "dest": 54
                        },
			"fees": "fees1",
			"signature": "sign1",
			"signatures": "sign-m",
                        "phones": [
                          "1234567",
                          "2345678",
                          {"rust": 5, "age": 1},
                          3
                        ]
                    }"#;
        let msg: Value = serde_json::from_str(data).unwrap();

        let result = "age:43|name:John Doe|operation:dest:54|hash:46aa0c92129b33ee72ee1478d2ae62fa6e756869dedc6c858af3214a6fcf1904|type:100|phones:1234567,2345678,age:1|rust:5,3";

        assert_eq!(serialize_signature(msg).unwrap(), result)
    }

    #[test]
    fn signature_serialize_works_with_raw_hash_for_attrib_related_type() {
        let data = r#"{
                        "name": "John Doe",
                        "age": 43,
                        "operation": {
                            "type": "100",
                            "hash": "cool hash",
                            "dest": 54,
                            "raw": "string for hash"
                        },
                        "phones": [
                          "1234567",
                          "2345678",
                          {"rust": 5, "age": 1},
                          3
                        ]
                    }"#;
        let msg: Value = serde_json::from_str(data).unwrap();

        let result = "age:43|name:John Doe|operation:dest:54|hash:46aa0c92129b33ee72ee1478d2ae62fa6e756869dedc6c858af3214a6fcf1904|raw:1dcd0759ce38f57049344a6b3c5fc18144fca1724713090c2ceeffa788c02711|type:100|phones:1234567,2345678,age:1|rust:5,3";

        assert_eq!(serialize_signature(msg).unwrap(), result)
    }

    #[test]
    fn signature_serialize_works_with_raw_hash_for_not_attrib_related_type() {
        let data = r#"{
                        "name": "John Doe",
                        "age": 43,
                        "operation": {
                            "type": "101",
                            "hash": "cool hash",
                            "dest": 54,
                            "raw": "string for hash"
                        },
                        "phones": [
                          "1234567",
                          "2345678",
                          {"rust": 5, "age": 1},
                          3
                        ]
                    }"#;
        let msg: Value = serde_json::from_str(data).unwrap();

        let result = "age:43|name:John Doe|operation:dest:54|hash:cool hash|raw:string for hash|type:101|phones:1234567,2345678,age:1|rust:5,3";

        assert_eq!(serialize_signature(msg).unwrap(), result)
    }

    #[test]
    fn signature_serialize_works_with_null() {
        let data = r#"{"signature": null}"#;
        let v: serde_json::Value = serde_json::from_str(data).unwrap();
        let serialized = serialize_signature(v).unwrap();
        assert_eq!(serialized, "");
    }
}
<|endoftext|>


<commit_before>use regex::Regex;
use std::iter::FromIterator;

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct StarredMessage {
	pub starrers: Vec<String>,
	pub stars   : u64,
	pub sender  : String,
	pub message : String,
}

impl StarredMessage {
	pub fn from_message_content(message: &str, starrer: Option<String>) -> Option<StarredMessage> {
		match starrer {
			Some(starrer) =>
				Self::regex().captures(message).map(move |captures| StarredMessage{
					starrers: vec![starrer],
					stars   : 1u64,
					sender  : captures[1].to_string(),
					message : captures[2].to_string(),
				}),
			None => None,
		}
	}

	fn regex() -> Regex {
		Regex::new(r#"[[:blank:]]*<([[:alnum:]\[\]`_^{|}-]{0,16})>[[:blank:]]*(.+)[[:blank:]]*"#).unwrap()
	}
}

#[cfg(test)]
mod tests {
	use starred_message::StarredMessage;

	#[test]
	fn message_extracts_correctly() {
		let msg = StarredMessage::from_message_content("<nabijaczleweli> I only clean 'round these parts", Some("thecoshman".to_string())).unwrap();
		assert_eq!(msg.sender, "nabijaczleweli");
		assert_eq!(msg.message, "I only clean 'round these parts");
	}

	#[test]
	fn message_fails_properly() {
		assert_eq!(StarredMessage::from_message_content("<nabijaczleweclean 'round these parts", Some("thecoshman".to_string())), None);
		assert_eq!(StarredMessage::from_message_content("<nabijaczleweli>", Some("thecoshman".to_string())), None);
	}

	#[test]
	fn message_propagates_starrer() {
		assert_eq!(StarredMessage::from_message_content("<nabijaczleweli> I only clean 'round these parts", None), None);
		assert_eq!(StarredMessage::from_message_content("<nabijaczleweli> I only clean 'round these parts",
		                                                Some("thecoshman".to_string())).unwrap().starrers, vec!["thecoshman".to_string()]);
	}

	#[test]
	fn message_defaults_to_1_star() {
		assert_eq!(StarredMessage::from_message_content("<nabijaczleweli> I only clean 'round these parts", Some("thecoshman".to_string())).unwrap().stars, 1);
	}
}
<commit_msg>Use Option::and_then()<commit_after>use regex::Regex;
use std::iter::FromIterator;

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct StarredMessage {
	pub starrers: Vec<String>,
	pub stars   : u64,
	pub sender  : String,
	pub message : String,
}

impl StarredMessage {
	pub fn from_message_content(message: &str, starrer: Option<String>) -> Option<StarredMessage> {
		starrer.and_then(move |starrer| Self::regex().captures(message).map(move |captures| StarredMessage{
			starrers: vec![starrer],
			stars   : 1u64,
			sender  : captures[1].to_string(),
			message : captures[2].to_string(),
		}))
	}

	fn regex() -> Regex {
		Regex::new(r#"[[:blank:]]*<([[:alnum:]\[\]`_^{|}-]{0,16})>[[:blank:]]*(.+)[[:blank:]]*"#).unwrap()
	}
}

#[cfg(test)]
mod tests {
	use starred_message::StarredMessage;

	#[test]
	fn message_extracts_correctly() {
		let msg = StarredMessage::from_message_content("<nabijaczleweli> I only clean 'round these parts", Some("thecoshman".to_string())).unwrap();
		assert_eq!(msg.sender, "nabijaczleweli");
		assert_eq!(msg.message, "I only clean 'round these parts");
	}

	#[test]
	fn message_fails_properly() {
		assert_eq!(StarredMessage::from_message_content("<nabijaczleweclean 'round these parts", Some("thecoshman".to_string())), None);
		assert_eq!(StarredMessage::from_message_content("<nabijaczleweli>", Some("thecoshman".to_string())), None);
	}

	#[test]
	fn message_propagates_starrer() {
		assert_eq!(StarredMessage::from_message_content("<nabijaczleweli> I only clean 'round these parts", None), None);
		assert_eq!(StarredMessage::from_message_content("<nabijaczleweli> I only clean 'round these parts",
		                                                Some("thecoshman".to_string())).unwrap().starrers, vec!["thecoshman".to_string()]);
	}

	#[test]
	fn message_defaults_to_1_star() {
		assert_eq!(StarredMessage::from_message_content("<nabijaczleweli> I only clean 'round these parts", Some("thecoshman".to_string())).unwrap().stars, 1);
	}
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FMTResult;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;
use std::fs::File as FSFile;
use std::fs::create_dir_all;
use std::fs::remove_file;
use std::io::Read;
use std::io::Write;
use std::vec::IntoIter;

use glob::glob;
use glob::Paths;

use storage::file::File;
use storage::file_id::*;
use storage::parser::{FileHeaderParser, Parser, ParserError};

use module::Module;
use runtime::Runtime;

pub type BackendOperationResult<T = ()> = Result<T, StorageBackendError>;

pub struct StorageBackend {
    basepath: String,
    storepath: String,
}

impl StorageBackend {

    pub fn new(rt: &Runtime) -> BackendOperationResult<StorageBackend> {
        let storepath = rt.get_rtp() + "/store/";
        debug!("Trying to create {}", storepath);
        create_dir_all(&storepath).and_then(|_| {
            debug!("Creating succeeded, constructing backend instance");
            Ok(StorageBackend {
                basepath: rt.get_rtp(),
                storepath: storepath.clone(),
            })
        }).or_else(|e| {
            debug!("Creating failed, constructing error instance");
            let mut serr = StorageBackendError::build(
                "create_dir_all()",
                "Could not create store directories",
                Some(storepath)
            );
            serr.caused_by = Some(Box::new(e));
            Err(serr)
        })
    }

    fn build<M: Module>(rt: &Runtime, m: &M) -> StorageBackend {
        let path = rt.get_rtp() + m.name() + "/store";
        // TODO: Don't use "/store" but value from configuration
        debug!("Building StorageBackend for {}", path);
        StorageBackend::new(path)
    }

    fn get_file_ids(&self, m: &Module) -> Option<Vec<FileID>> {
        let list = glob(&self.prefix_of_files_for_module(m)[..]);

        if let Ok(globlist) = list {
            let mut v = vec![];
            for entry in globlist {
                if let Ok(path) = entry {
                    debug!(" - File: {:?}", path);
                    v.push(from_pathbuf(&path));
                } else {
                    // Entry is not a path
                }
            }

            Some(v)
        } else {
            None
        }
    }

    pub fn iter_ids(&self, m: &Module) -> Option<IntoIter<FileID>>
    {
        glob(&self.prefix_of_files_for_module(m)[..]).and_then(|globlist| {
            let v = globlist.filter_map(Result::ok)
                            .map(|pbuf| from_pathbuf(&pbuf))
                            .collect::<Vec<FileID>>()
                            .into_iter();
            Ok(v)
        }).ok()
    }

    pub fn iter_files<'a, HP>(&self, m: &'a Module, p: &Parser<HP>)
        -> Option<IntoIter<File<'a>>>
        where HP: FileHeaderParser
    {
        self.iter_ids(m).and_then(|ids| {
            Some(ids.filter_map(|id| self.get_file_by_id(m, &id, p))
                    .collect::<Vec<File>>()
                    .into_iter())
        })
    }

    /*
     * Write a file to disk.
     *
     * The file is moved to this function as the file won't be edited afterwards
     */
    pub fn put_file<HP>(&self, f: File, p: &Parser<HP>) -> BackendOperationResult
        where HP: FileHeaderParser
    {
        let written = write_with_parser(&f, p);
        if written.is_err() { return Err(written.err().unwrap()); }
        let string = written.unwrap();

        let path = self.build_filepath(&f);
        debug!("Writing file: {}", path);
        debug!("      string: {}", string);

        FSFile::create(&path).map(|mut file| {
            debug!("Created file at '{}'", path);
            file.write_all(&string.clone().into_bytes())
                .map_err(|ioerr| {
                    debug!("Could not write file");
                    let mut err = StorageBackendError::build(
                            "File::write_all()",
                            "Could not write out File contents",
                            "", None
                        );
                    err.caused_by = Some(Box::new(ioerr));
                    err
                })
        }).map_err(|writeerr| {
            debug!("Could not create file at '{}'", path);
            let mut err = StorageBackendError::build(
                "File::create()",
                "Creating file on disk failed",
                "", None
            );
            err.caused_by = Some(Box::new(writeerr));
            err
        }).and(Ok(()))
    }

    /*
     * Update a file. We have the UUID and can find the file on FS with it and
     * then replace its contents with the contents of the passed file object
     */
    pub fn update_file<HP>(&self, f: File, p: &Parser<HP>) -> BackendOperationResult
        where HP: FileHeaderParser
    {
        let contents = write_with_parser(&f, p);
        if contents.is_err() { return Err(contents.err().unwrap()); }
        let string = contents.unwrap();

        let path = self.build_filepath(&f);
        debug!("Writing file: {}", path);
        debug!("      string: {}", string);

        FSFile::open(&path).map(|mut file| {
            debug!("Open file at '{}'", path);
            file.write_all(&string.clone().into_bytes())
                .map_err(|ioerr| {
                    debug!("Could not write file");
                    let mut err = StorageBackendError::build(
                            "File::write()",
                            "",
                            "Tried to write contents of this file, though operation did not succeed",
                            Some(string)
                        );
                    err.caused_by = Some(Box::new(ioerr));
                    err
                })
        }).map_err(|writeerr| {
            debug!("Could not write file at '{}'", path);
            let mut err = StorageBackendError::build(
                "File::open()",
                "",
                "Tried to update contents of this file, though file doesn't exist",
                None
            );
            err.caused_by = Some(Box::new(writeerr));
            err
        }).and(Ok(()))
    }

    /*
     * Find a file by its ID and return it if found. Return nothing if not
     * found, of course.
     *
     * TODO: Needs refactoring, as there might be an error when reading from
     * disk OR the id just does not exist.
     */
    pub fn get_file_by_id<'a, HP>(&self, m: &'a Module, id: FileID, p: &Parser<HP>) -> Option<File<'a>>
        where HP: FileHeaderParser
    {
        debug!("Searching for file with id '{}'", id);
        if let Ok(mut fs) = FSFile::open(self.build_filepath_with_id(m, id.clone())) {
            let mut s = String::new();
            fs.read_to_string(&mut s);
            debug!("Success reading file with id '{}'", id);
            debug!("Parsing to internal structure now");
            p.read(s).and_then(|(h, d)| Ok(File::from_parser_result(m, id.clone(), h, d))).ok()
        } else {
            debug!("No file with id '{}'", id);
            None
        }
    }

    pub fn remove_file(&self, m: &Module, file: File, checked: bool) -> BackendOperationResult {
        if checked {
            error!("Checked remove not implemented yet. I will crash now");
            unimplemented!()
        }

        debug!("Doing unchecked remove");
        info!("Going to remove file: {}", file);

        let fp = self.build_filepath(&file);
        remove_file(fp).map_err(|e| {
            let mut serr = StorageBackendError::build(
                "remove_file()",
                "File removal failed",
                Some(format!("{}", file))
            );
            serr.caused_by = Some(Box::new(e));
            serr
        })
    }

    fn build_filepath(&self, f: &File) -> String {
        self.build_filepath_with_id(f.owner(), f.id())
    }

    fn build_filepath_with_id(&self, owner: &Module, id: FileID) -> String {
        debug!("Building filepath with id");
        debug!("  basepath: '{}'", self.basepath);
        debug!(" storepath: '{}'", self.storepath);
        debug!("  id      : '{}'", id);
        self.storepath.clone() + owner.name() + "-" + &id[..] + ".imag"
    }

}

#[derive(Debug)]
pub struct StorageBackendError {
    pub action: String,             // The file system action in words
    pub desc: String,               // A short description
    pub data_dump: Option<String>,  // Data dump, if any
    pub caused_by: Option<Box<Error>>,  // caused from this error
}

impl StorageBackendError {
    fn new(action: String,
           desc  : String,
           data  : Option<String>) -> StorageBackendError<'a>
    {
        StorageBackendError {
            action:         action,
            desc:           desc,
            data_dump:      data,
            caused_by:      None,
        }
    }

    fn build(action: &'static str,
             desc:   &'static str,
             data  : Option<String>) -> StorageBackendError<'a>
    {
        StorageBackendError {
            action:         String::from(action),
            desc:           String::from(desc),
            dataDump:       data,
            caused_by:      None,
        }
    }

}

impl Error for StorageBackendError {

    fn description(&self) -> &str {
        &self.desc[..]
    }

    fn cause(&self) -> Option<&Error> {
        self.caused_by.as_ref().map(|e| &**e)
    }

}

impl<'a> Display for StorageBackendError {
    fn fmt(&self, f: &mut Formatter) -> FMTResult {
        write!(f, "StorageBackendError[{}]: {}",
               self.action, self.desc)
    }
}

fn write_with_parser<'a, HP>(f: &File, p: &Parser<HP>) -> Result<String, StorageBackendError>
    where HP: FileHeaderParser
{
    p.write(f.contents())
        .or_else(|err| {
            let mut serr = StorageBackendError::build(
                "Parser::write()",
                "Cannot parse file contents",
                "Cannot translate internal representation of file contents into on-disk representation",
                None
            );
            serr.caused_by = Some(Box::new(err));
            Err(serr)
        })
}
<commit_msg>Remove explanation member of StorageBackendError to reduce complexibility<commit_after>use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FMTResult;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;
use std::fs::File as FSFile;
use std::fs::create_dir_all;
use std::fs::remove_file;
use std::io::Read;
use std::io::Write;
use std::vec::IntoIter;

use glob::glob;
use glob::Paths;

use storage::file::File;
use storage::file_id::*;
use storage::parser::{FileHeaderParser, Parser, ParserError};

use module::Module;
use runtime::Runtime;

pub type BackendOperationResult<T = ()> = Result<T, StorageBackendError>;

pub struct StorageBackend {
    basepath: String,
    storepath: String,
}

impl StorageBackend {

    pub fn new(rt: &Runtime) -> BackendOperationResult<StorageBackend> {
        let storepath = rt.get_rtp() + "/store/";
        debug!("Trying to create {}", storepath);
        create_dir_all(&storepath).and_then(|_| {
            debug!("Creating succeeded, constructing backend instance");
            Ok(StorageBackend {
                basepath: rt.get_rtp(),
                storepath: storepath.clone(),
            })
        }).or_else(|e| {
            debug!("Creating failed, constructing error instance");
            let mut serr = StorageBackendError::build(
                "create_dir_all()",
                "Could not create store directories",
                Some(storepath)
            );
            serr.caused_by = Some(Box::new(e));
            Err(serr)
        })
    }

    fn build<M: Module>(rt: &Runtime, m: &M) -> StorageBackend {
        let path = rt.get_rtp() + m.name() + "/store";
        // TODO: Don't use "/store" but value from configuration
        debug!("Building StorageBackend for {}", path);
        StorageBackend::new(path)
    }

    fn get_file_ids(&self, m: &Module) -> Option<Vec<FileID>> {
        let list = glob(&self.prefix_of_files_for_module(m)[..]);

        if let Ok(globlist) = list {
            let mut v = vec![];
            for entry in globlist {
                if let Ok(path) = entry {
                    debug!(" - File: {:?}", path);
                    v.push(from_pathbuf(&path));
                } else {
                    // Entry is not a path
                }
            }

            Some(v)
        } else {
            None
        }
    }

    pub fn iter_ids(&self, m: &Module) -> Option<IntoIter<FileID>>
    {
        glob(&self.prefix_of_files_for_module(m)[..]).and_then(|globlist| {
            let v = globlist.filter_map(Result::ok)
                            .map(|pbuf| from_pathbuf(&pbuf))
                            .collect::<Vec<FileID>>()
                            .into_iter();
            Ok(v)
        }).ok()
    }

    pub fn iter_files<'a, HP>(&self, m: &'a Module, p: &Parser<HP>)
        -> Option<IntoIter<File<'a>>>
        where HP: FileHeaderParser
    {
        self.iter_ids(m).and_then(|ids| {
            Some(ids.filter_map(|id| self.get_file_by_id(m, &id, p))
                    .collect::<Vec<File>>()
                    .into_iter())
        })
    }

    /*
     * Write a file to disk.
     *
     * The file is moved to this function as the file won't be edited afterwards
     */
    pub fn put_file<HP>(&self, f: File, p: &Parser<HP>) -> BackendOperationResult
        where HP: FileHeaderParser
    {
        let written = write_with_parser(&f, p);
        if written.is_err() { return Err(written.err().unwrap()); }
        let string = written.unwrap();

        let path = self.build_filepath(&f);
        debug!("Writing file: {}", path);
        debug!("      string: {}", string);

        FSFile::create(&path).map(|mut file| {
            debug!("Created file at '{}'", path);
            file.write_all(&string.clone().into_bytes())
                .map_err(|ioerr| {
                    debug!("Could not write file");
                    let mut err = StorageBackendError::build(
                            "File::write_all()",
                            "Could not write out File contents",
                            None
                        );
                    err.caused_by = Some(Box::new(ioerr));
                    err
                })
        }).map_err(|writeerr| {
            debug!("Could not create file at '{}'", path);
            let mut err = StorageBackendError::build(
                "File::create()",
                "Creating file on disk failed",
                None
            );
            err.caused_by = Some(Box::new(writeerr));
            err
        }).and(Ok(()))
    }

    /*
     * Update a file. We have the UUID and can find the file on FS with it and
     * then replace its contents with the contents of the passed file object
     */
    pub fn update_file<HP>(&self, f: File, p: &Parser<HP>) -> BackendOperationResult
        where HP: FileHeaderParser
    {
        let contents = write_with_parser(&f, p);
        if contents.is_err() { return Err(contents.err().unwrap()); }
        let string = contents.unwrap();

        let path = self.build_filepath(&f);
        debug!("Writing file: {}", path);
        debug!("      string: {}", string);

        FSFile::open(&path).map(|mut file| {
            debug!("Open file at '{}'", path);
            file.write_all(&string.clone().into_bytes())
                .map_err(|ioerr| {
                    debug!("Could not write file");
                    let mut err = StorageBackendError::build(
                            "File::write()",
                            "Tried to write contents of this file, though operation did not succeed",
                            Some(string)
                        );
                    err.caused_by = Some(Box::new(ioerr));
                    err
                })
        }).map_err(|writeerr| {
            debug!("Could not write file at '{}'", path);
            let mut err = StorageBackendError::build(
                "File::open()",
                "Tried to update contents of this file, though file doesn't exist",
                None
            );
            err.caused_by = Some(Box::new(writeerr));
            err
        }).and(Ok(()))
    }

    /*
     * Find a file by its ID and return it if found. Return nothing if not
     * found, of course.
     *
     * TODO: Needs refactoring, as there might be an error when reading from
     * disk OR the id just does not exist.
     */
    pub fn get_file_by_id<'a, HP>(&self, m: &'a Module, id: FileID, p: &Parser<HP>) -> Option<File<'a>>
        where HP: FileHeaderParser
    {
        debug!("Searching for file with id '{}'", id);
        if let Ok(mut fs) = FSFile::open(self.build_filepath_with_id(m, id.clone())) {
            let mut s = String::new();
            fs.read_to_string(&mut s);
            debug!("Success reading file with id '{}'", id);
            debug!("Parsing to internal structure now");
            p.read(s).and_then(|(h, d)| Ok(File::from_parser_result(m, id.clone(), h, d))).ok()
        } else {
            debug!("No file with id '{}'", id);
            None
        }
    }

    pub fn remove_file(&self, m: &Module, file: File, checked: bool) -> BackendOperationResult {
        if checked {
            error!("Checked remove not implemented yet. I will crash now");
            unimplemented!()
        }

        debug!("Doing unchecked remove");
        info!("Going to remove file: {}", file);

        let fp = self.build_filepath(&file);
        remove_file(fp).map_err(|e| {
            let mut serr = StorageBackendError::build(
                "remove_file()",
                "File removal failed",
                Some(format!("{}", file))
            );
            serr.caused_by = Some(Box::new(e));
            serr
        })
    }

    fn build_filepath(&self, f: &File) -> String {
        self.build_filepath_with_id(f.owner(), f.id())
    }

    fn build_filepath_with_id(&self, owner: &Module, id: FileID) -> String {
        debug!("Building filepath with id");
        debug!("  basepath: '{}'", self.basepath);
        debug!(" storepath: '{}'", self.storepath);
        debug!("  id      : '{}'", id);
        self.storepath.clone() + owner.name() + "-" + &id[..] + ".imag"
    }

}

#[derive(Debug)]
pub struct StorageBackendError {
    pub action: String,             // The file system action in words
    pub desc: String,               // A short description
    pub data_dump: Option<String>,  // Data dump, if any
    pub caused_by: Option<Box<Error>>,  // caused from this error
}

impl StorageBackendError {
    fn new(action: String,
           desc  : String,
           data  : Option<String>) -> StorageBackendError
    {
        StorageBackendError {
            action:         action,
            desc:           desc,
            data_dump:      data,
            caused_by:      None,
        }
    }

    fn build(action: &'static str,
             desc:   &'static str,
             data  : Option<String>) -> StorageBackendError
    {
        StorageBackendError {
            action:         String::from(action),
            desc:           String::from(desc),
            dataDump:       data,
            caused_by:      None,
        }
    }

}

impl Error for StorageBackendError {

    fn description(&self) -> &str {
        &self.desc[..]
    }

    fn cause(&self) -> Option<&Error> {
        self.caused_by.as_ref().map(|e| &**e)
    }

}

impl<'a> Display for StorageBackendError {
    fn fmt(&self, f: &mut Formatter) -> FMTResult {
        write!(f, "StorageBackendError[{}]: {}",
               self.action, self.desc)
    }
}

fn write_with_parser<'a, HP>(f: &File, p: &Parser<HP>) -> Result<String, StorageBackendError>
    where HP: FileHeaderParser
{
    p.write(f.contents())
        .or_else(|err| {
            let mut serr = StorageBackendError::build(
                "Parser::write()",
                "Cannot translate internal representation of file contents into on-disk representation",
                None
            );
            serr.caused_by = Some(Box::new(err));
            Err(serr)
        })
}
<|endoftext|>


<commit_before>use std::process::{Command, Output};
use std::fs;
use std::io::prelude::*;
use std::fs::File;
use platform;

#[derive(Clone)]
pub struct SystemServices;

pub enum OSType {
    Debian,
    Redhat,
    Unknown
}

pub trait SystemInterface {
    fn file_exists(&self, path: &String) -> bool;
    fn read_file(&self, path: &String) -> Option<String>;
    fn is_package_installed(&self, package_name: &String) -> bool;
    fn install_package(&self, package: &String) -> Output;
    fn get_hostname(&self) -> Option<String>;
    fn set_hostname(&self, new_hostname: &String) -> bool;
    fn os_type(&self) -> OSType;
}

impl SystemInterface for SystemServices {
     fn file_exists(&self, path: &String) -> bool {
        let metadata = fs::metadata(path);

        match metadata {
            Ok(md) => md.is_dir() || md.is_file(),
            Err(_) => false
        }
    }

     fn read_file(&self, path: &String) -> Option<String> {
        let mut file_handle = File::open(path);
        let mut s = String::new();
        match file_handle {
            Ok(mut handle) => {
                handle.read_to_string(&mut s);
                Some(s)
            },
            Err(err)   => None
        }
    }

     fn is_package_installed(&self, package_name: &String) -> bool {
        let platform = platform::for_os_type(self.os_type()).unwrap();
        let command_str = format!("{} {}", platform.package_installed_command, package_name);
        let cmd_list :Vec<&str> = command_str.split_whitespace().collect();
        let cmd_name :String = cmd_list.first().unwrap().to_string();
        let mut cmd_args = Vec::new();
        cmd_args.extend(cmd_list.iter().skip(1).map(|s| s.to_string()).collect::<Vec<_>>());

        match Command::new(cmd_name).args(&cmd_args[..]).output() {
            Ok(output) => output.status.success(),
            Err(err)   => false
        }
    }

     fn install_package(&self, package: &String) -> Output {
        let platform = platform::for_os_type(self.os_type()).unwrap();
        let cmd_list :Vec<&str> = platform.install_command.split_whitespace().collect();
        let cmd_name :String = cmd_list.first().unwrap().to_string();
        let mut cmd_args = Vec::new();
        cmd_args.extend(cmd_list.iter().skip(1).map(|s| s.to_string()).collect::<Vec<_>>());
        cmd_args.push(package.to_string());

        Command::new(cmd_name)
            .args(&cmd_args[..])
            .output()
            .unwrap()
    }

     fn get_hostname(&self) -> Option<String> {
        let output = Command::new("hostname").output().unwrap();
        if output.status.success() {
            let hostname_with_newline = String::from_utf8(output.stdout).unwrap();
            Some(hostname_with_newline.replace("\n", ""))
        } else {
            None
        }
    }

     fn set_hostname(&self, new_hostname: &String) -> bool {
        let output = Command::new("hostname")
            .arg(new_hostname)
            .output()
            .unwrap();
        output.status.success()
    }

    fn os_type(&self) -> OSType {
        if self.file_exists(&"/etc/redhat-release".to_string()) ||
            self.file_exists(&"/etc/centos-release".to_string()){
                OSType::Redhat
            } else {
                OSType::Unknown
            }
    }
}
<commit_msg>Remove unused enum member<commit_after>use std::process::{Command, Output};
use std::fs;
use std::io::prelude::*;
use std::fs::File;
use platform;

#[derive(Clone)]
pub struct SystemServices;

pub enum OSType {
    Redhat,
    Unknown
}

pub trait SystemInterface {
    fn file_exists(&self, path: &String) -> bool;
    fn read_file(&self, path: &String) -> Option<String>;
    fn is_package_installed(&self, package_name: &String) -> bool;
    fn install_package(&self, package: &String) -> Output;
    fn get_hostname(&self) -> Option<String>;
    fn set_hostname(&self, new_hostname: &String) -> bool;
    fn os_type(&self) -> OSType;
}

impl SystemInterface for SystemServices {
     fn file_exists(&self, path: &String) -> bool {
        let metadata = fs::metadata(path);

        match metadata {
            Ok(md) => md.is_dir() || md.is_file(),
            Err(_) => false
        }
    }

     fn read_file(&self, path: &String) -> Option<String> {
        let mut file_handle = File::open(path);
        let mut s = String::new();
        match file_handle {
            Ok(mut handle) => {
                handle.read_to_string(&mut s);
                Some(s)
            },
            Err(err)   => None
        }
    }

     fn is_package_installed(&self, package_name: &String) -> bool {
        let platform = platform::for_os_type(self.os_type()).unwrap();
        let command_str = format!("{} {}", platform.package_installed_command, package_name);
        let cmd_list :Vec<&str> = command_str.split_whitespace().collect();
        let cmd_name :String = cmd_list.first().unwrap().to_string();
        let mut cmd_args = Vec::new();
        cmd_args.extend(cmd_list.iter().skip(1).map(|s| s.to_string()).collect::<Vec<_>>());

        match Command::new(cmd_name).args(&cmd_args[..]).output() {
            Ok(output) => output.status.success(),
            Err(err)   => false
        }
    }

     fn install_package(&self, package: &String) -> Output {
        let platform = platform::for_os_type(self.os_type()).unwrap();
        let cmd_list :Vec<&str> = platform.install_command.split_whitespace().collect();
        let cmd_name :String = cmd_list.first().unwrap().to_string();
        let mut cmd_args = Vec::new();
        cmd_args.extend(cmd_list.iter().skip(1).map(|s| s.to_string()).collect::<Vec<_>>());
        cmd_args.push(package.to_string());

        Command::new(cmd_name)
            .args(&cmd_args[..])
            .output()
            .unwrap()
    }

     fn get_hostname(&self) -> Option<String> {
        let output = Command::new("hostname").output().unwrap();
        if output.status.success() {
            let hostname_with_newline = String::from_utf8(output.stdout).unwrap();
            Some(hostname_with_newline.replace("\n", ""))
        } else {
            None
        }
    }

     fn set_hostname(&self, new_hostname: &String) -> bool {
        let output = Command::new("hostname")
            .arg(new_hostname)
            .output()
            .unwrap();
        output.status.success()
    }

    fn os_type(&self) -> OSType {
        if self.file_exists(&"/etc/redhat-release".to_string()) ||
            self.file_exists(&"/etc/centos-release".to_string()){
                OSType::Redhat
            } else {
                OSType::Unknown
            }
    }
}
<|endoftext|>


<commit_before>use std::{
    cmp::Ordering,
    fmt,
    ops::{Add, AddAssign, Sub},
};

use metadata::Duration;

use super::{SampleIndexRange, Timestamp};

#[derive(Clone, Copy, Default, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct SampleIndex(usize);

impl SampleIndex {
    pub fn new(value: usize) -> Self {
        SampleIndex(value)
    }

    pub fn from_ts(ts: Timestamp, sample_duration: Duration) -> Self {
        SampleIndex((ts.as_u64() / sample_duration.as_u64()) as usize)
    }

    pub fn get_aligned(self, sample_step: SampleIndexRange) -> SampleIndex {
        SampleIndex(self.0 / sample_step.as_usize() * sample_step.as_usize())
    }

    pub fn get_ts(self, sample_duration: Duration) -> Timestamp {
        Timestamp::new(self.0 as u64 * sample_duration.as_u64())
    }

    pub fn as_usize(self) -> usize {
        self.0
    }

    pub fn as_u64(self) -> u64 {
        self.0 as u64
    }

    pub fn try_dec(&mut self) -> Result<(), String> {
        if self.0 > 0 {
            *self = SampleIndex(self.0 - 1);
            Ok(())
        } else {
            Err("attempting to decrement a sample index past 0".to_string())
        }
    }

    pub fn inc(&mut self) {
        *self = SampleIndex(self.0 + 1);
    }
}

impl From<usize> for SampleIndex {
    fn from(value: usize) -> Self {
        Self(value)
    }
}

impl From<u64> for SampleIndex {
    fn from(value: u64) -> Self {
        Self(value as usize)
    }
}

impl From<SampleIndexRange> for SampleIndex {
    fn from(range: SampleIndexRange) -> Self {
        Self(range.as_usize())
    }
}

impl Sub for SampleIndex {
    type Output = SampleIndexRange;

    fn sub(self, rhs: SampleIndex) -> SampleIndexRange {
        SampleIndexRange::new(self.0 - rhs.0)
    }
}

impl Add<SampleIndexRange> for SampleIndex {
    type Output = SampleIndex;

    fn add(self, rhs: SampleIndexRange) -> SampleIndex {
        SampleIndex(self.0 + rhs.as_usize())
    }
}

impl AddAssign<SampleIndexRange> for SampleIndex {
    fn add_assign(&mut self, rhs: SampleIndexRange) {
        *self = SampleIndex(self.0 + rhs.as_usize());
    }
}

impl Sub<SampleIndexRange> for SampleIndex {
    type Output = SampleIndex;

    fn sub(self, rhs: SampleIndexRange) -> SampleIndex {
        SampleIndex(self.0 - rhs.as_usize())
    }
}

impl PartialOrd<SampleIndexRange> for SampleIndex {
    fn partial_cmp(&self, rhs: &SampleIndexRange) -> Option<Ordering> {
        Some(self.0.cmp(&rhs.as_usize()))
    }
}

impl PartialEq<SampleIndexRange> for SampleIndex {
    fn eq(&self, rhs: &SampleIndexRange) -> bool {
        self.0 == rhs.as_usize()
    }
}

impl fmt::Display for SampleIndex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "idx {}", self.0)
    }
}
<commit_msg>SampleIndex: remove useless String allocation<commit_after>use std::{
    cmp::Ordering,
    fmt,
    ops::{Add, AddAssign, Sub},
};

use metadata::Duration;

use super::{SampleIndexRange, Timestamp};

#[derive(Clone, Copy, Default, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct SampleIndex(usize);

impl SampleIndex {
    pub fn new(value: usize) -> Self {
        SampleIndex(value)
    }

    pub fn from_ts(ts: Timestamp, sample_duration: Duration) -> Self {
        SampleIndex((ts.as_u64() / sample_duration.as_u64()) as usize)
    }

    pub fn get_aligned(self, sample_step: SampleIndexRange) -> SampleIndex {
        SampleIndex(self.0 / sample_step.as_usize() * sample_step.as_usize())
    }

    pub fn get_ts(self, sample_duration: Duration) -> Timestamp {
        Timestamp::new(self.0 as u64 * sample_duration.as_u64())
    }

    pub fn as_usize(self) -> usize {
        self.0
    }

    pub fn as_u64(self) -> u64 {
        self.0 as u64
    }

    pub fn try_dec(&mut self) -> Result<(), ()> {
        if self.0 > 0 {
            *self = SampleIndex(self.0 - 1);
            Ok(())
        } else {
            Err(())
        }
    }

    pub fn inc(&mut self) {
        *self = SampleIndex(self.0 + 1);
    }
}

impl From<usize> for SampleIndex {
    fn from(value: usize) -> Self {
        Self(value)
    }
}

impl From<u64> for SampleIndex {
    fn from(value: u64) -> Self {
        Self(value as usize)
    }
}

impl From<SampleIndexRange> for SampleIndex {
    fn from(range: SampleIndexRange) -> Self {
        Self(range.as_usize())
    }
}

impl Sub for SampleIndex {
    type Output = SampleIndexRange;

    fn sub(self, rhs: SampleIndex) -> SampleIndexRange {
        SampleIndexRange::new(self.0 - rhs.0)
    }
}

impl Add<SampleIndexRange> for SampleIndex {
    type Output = SampleIndex;

    fn add(self, rhs: SampleIndexRange) -> SampleIndex {
        SampleIndex(self.0 + rhs.as_usize())
    }
}

impl AddAssign<SampleIndexRange> for SampleIndex {
    fn add_assign(&mut self, rhs: SampleIndexRange) {
        *self = SampleIndex(self.0 + rhs.as_usize());
    }
}

impl Sub<SampleIndexRange> for SampleIndex {
    type Output = SampleIndex;

    fn sub(self, rhs: SampleIndexRange) -> SampleIndex {
        SampleIndex(self.0 - rhs.as_usize())
    }
}

impl PartialOrd<SampleIndexRange> for SampleIndex {
    fn partial_cmp(&self, rhs: &SampleIndexRange) -> Option<Ordering> {
        Some(self.0.cmp(&rhs.as_usize()))
    }
}

impl PartialEq<SampleIndexRange> for SampleIndex {
    fn eq(&self, rhs: &SampleIndexRange) -> bool {
        self.0 == rhs.as_usize()
    }
}

impl fmt::Display for SampleIndex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "idx {}", self.0)
    }
}
<|endoftext|>


<commit_before>use super::traits::*;
use super::values::*;

type Node = Box<TreeNode>;

///
/// A tree whose structure is kept in-memory and which has no pre-defined structure
///
pub struct MemoryTree {
    child_nodes: Vec<Node>,
    tag: String,
    value: TreeValue
}

impl TreeNode for MemoryTree {
    ///
    /// Counts the number of children of this tree node
    ///
    fn count_children(&self) -> u32 {
        self.child_nodes.len() as u32
    }

    ///
    /// Retrieves the child at the specified index
    ///
    fn get_child(&self, index: u32) -> &TreeNode {
        &*self.child_nodes[index as usize]
    }

    ///
    /// Retrieves the tag attached to this tree node
    ///
    fn get_tag(&self) -> &str {
        &self.tag
    }

    ///
    /// Retrieves the value attached to this node
    ///
    fn get_value(&self) -> &TreeValue {
        &self.value
    }
}

impl MemoryTree {
    ///
    /// Creates a new memory tree node, with a particular tag but no value
    ///
    pub fn new(tag: &str) -> MemoryTree {
        MemoryTree { tag: tag.to_string(), value: TreeValue::Nothing, child_nodes: Vec::<Node>::new() }
    }

    ///
    /// Adds a new child node to this tree
    ///
    pub fn add_child(&mut self, new_node: Box<TreeNode>) {
        self.child_nodes.push(new_node);
    }
}

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

    #[test]
    fn can_add_child() {
        let mut tree = MemoryTree::new("root");
        let child_node = Box::new(MemoryTree::new("child"));

        tree.add_child(child_node);

        assert!(tree.count_children() == 1);
        assert!(tree.get_child(0).get_tag() == "child");
    }
}
<commit_msg>Add a value to the constructor, add suport for slightly more fluent tree building<commit_after>use super::traits::*;
use super::values::*;

type Node = Box<TreeNode>;

///
/// A tree whose structure is kept in-memory and which has no pre-defined structure
///
pub struct MemoryTree {
    child_nodes: Vec<Node>,
    tag: String,
    value: TreeValue
}

impl TreeNode for MemoryTree {
    ///
    /// Counts the number of children of this tree node
    ///
    fn count_children(&self) -> u32 {
        self.child_nodes.len() as u32
    }

    ///
    /// Retrieves the child at the specified index
    ///
    fn get_child(&self, index: u32) -> &TreeNode {
        &*self.child_nodes[index as usize]
    }

    ///
    /// Retrieves the tag attached to this tree node
    ///
    fn get_tag(&self) -> &str {
        &self.tag
    }

    ///
    /// Retrieves the value attached to this node
    ///
    fn get_value(&self) -> &TreeValue {
        &self.value
    }
}

impl MemoryTree {
    ///
    /// Creates a new memory tree node, with a particular tag but no value
    ///
    pub fn new(tag: &str, value: TreeValue) -> MemoryTree {
        MemoryTree { tag: tag.to_string(), value: value, child_nodes: Vec::<Node>::new() }
    }

    ///
    /// Adds a new child node to this tree
    ///
    pub fn add_child(&mut self, new_node: Box<TreeNode>) -> &mut MemoryTree {
        self.child_nodes.push(new_node);
        self
    }

    ///
    /// Removes a node from this tree
    ///
    pub fn remove_child(&mut self, index: u32) -> &mut MemoryTree {
        self.child_nodes.remove(index as usize);
        self
    }
}

#[cfg(test)]
mod memorytree_tests {
    use super::*;
    use tree::traits::*;
    use tree::values::*;

    #[test]
    fn can_add_child() {
        let mut tree = MemoryTree::new("root", TreeValue::Nothing);
        let child_node = Box::new(MemoryTree::new("child", TreeValue::Nothing));

        tree.add_child(child_node);

        assert!(tree.count_children() == 1);
        assert!(tree.get_child(0).get_tag() == "child");
    }
}
<|endoftext|>