<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 lazy_static::lazy_static;
use proc_macro2::Span;
use std::collections::HashMap;
use syn::punctuated::Punctuated;
use syn::Token;
use syn::{
    parse_quote, AngleBracketedGenericArguments, Attribute, FnArg, ForeignItem, ForeignItemFn,
    GenericArgument, Ident, Item, ItemForeignMod, ItemMod, ItemStruct, PatType, Path,
    PathArguments, PathSegment, ReturnType, Type, TypePath, TypePtr, TypeReference,
};

lazy_static! {
    /// Substitutions from the names constructed by bindgen into those
    /// which cxx uses.
    static ref IDENT_REPLACEMENTS: HashMap<&'static str, String> = {
        let mut map = HashMap::new();
        map.insert("std_unique_ptr", "UniquePtr".to_string());
        map.insert("std_string", "CxxString".to_string());
        map
    };
}

#[derive(Debug)]
pub enum ConvertError {
    NoContent,
}

/// Converts the bindings generated by bindgen into a form suitable
/// for use with `cxx`.
/// Tasks current performed:
/// * Replaces certain identifiers e.g. `std_unique_ptr` with `UniquePtr`
/// * Replaces pointers with references
/// * Removes repr attributes
/// * Removes link_name attributes
/// * Adds include! directives
/// * Adds #[cxx::bridge]
/// At the moment this is almost certainly not using the best practice for parsing
/// stuff. It's multiple simple-but-yucky state machines. Can undoubtedly be
/// simplified and made less error-prone: TODO. Probably the right thing to do
/// is just manipulate the syn types directly.
pub(crate) struct BridgeConverter {
    include_list: Vec<String>,
}

impl BridgeConverter {
    pub fn new(include_list: Vec<String>) -> Self {
        Self { include_list }
    }

    /// Convert a TokenStream of bindgen-generated bindings to a form
    /// suitable for cxx.
    pub(crate) fn convert(&self, bindings: ItemMod) -> Result<ItemMod, ConvertError> {
        match bindings.content {
            None => return Err(ConvertError::NoContent),
            Some((brace, items)) => {
                let mut new_items = Vec::new();
                for item in items {
                    new_items.push(match item {
                        Item::ForeignMod(fm) => {
                            self.convert_foreign_mod(fm).map(Item::ForeignMod)?
                        }
                        Item::Struct(s) => self.convert_struct(s).map(Item::Struct)?,
                        _ => item, // TODO - consider rejecting
                    });
                }
                Ok(ItemMod {
                    attrs: bindings.attrs,
                    ident: bindings.ident,
                    semi: bindings.semi,
                    mod_token: bindings.mod_token,
                    vis: bindings.vis,
                    content: Some((brace.clone(), new_items)),
                })
            }
        }
    }

    fn convert_foreign_mod(
        &self,
        foreign_mod: ItemForeignMod,
    ) -> Result<ItemForeignMod, ConvertError> {
        let mut new_items = Vec::new();
        for inc in &self.include_list {
            let new_bit = parse_quote! {
                include!(#inc);
            };
            new_items.push(ForeignItem::Macro(new_bit));
        }
        for i in foreign_mod.items {
            new_items.push(match i {
                ForeignItem::Fn(f) => self.convert_foreign_fn(f).map(ForeignItem::Fn)?,
                _ => i,
            })
        }
        Ok(ItemForeignMod {
            attrs: foreign_mod.attrs,
            abi: foreign_mod.abi,
            brace_token: foreign_mod.brace_token,
            items: new_items,
        })
    }

    fn convert_foreign_fn(&self, fun: ForeignItemFn) -> Result<ForeignItemFn, ConvertError> {
        let mut s = fun.sig.clone();
        s.output = self.convert_return_type(s.output);
        s.inputs = s
            .inputs
            .into_iter()
            .map(|i| self.convert_fn_arg(i))
            .collect();
        Ok(ForeignItemFn {
            attrs: self.strip_attr(fun.attrs, "link_name"),
            vis: fun.vis,
            sig: s,
            semi_token: fun.semi_token,
        })
    }

    fn convert_struct(&self, ty: ItemStruct) -> Result<ItemStruct, ConvertError> {
        Ok(ItemStruct {
            attrs: self.strip_attr(ty.attrs, "repr"),
            vis: ty.vis,
            struct_token: ty.struct_token,
            generics: ty.generics,
            fields: ty.fields,
            semi_token: ty.semi_token,
            ident: ty.ident,
        })
    }

    fn strip_attr(&self, attrs: Vec<Attribute>, to_strip: &str) -> Vec<Attribute> {
        attrs
            .into_iter()
            .filter(|a| {
                let i = a.path.get_ident();
                match i {
                    Some(i2) if i2.to_string() == to_strip => false,
                    _ => true,
                }
            })
            .collect::<Vec<Attribute>>()
    }

    fn convert_fn_arg(&self, arg: FnArg) -> FnArg {
        match arg {
            FnArg::Typed(pt) => FnArg::Typed(PatType {
                attrs: pt.attrs,
                pat: pt.pat,
                colon_token: pt.colon_token,
                ty: self.convert_boxed_type(pt.ty),
            }),
            _ => arg,
        }
    }

    fn convert_return_type(&self, rt: ReturnType) -> ReturnType {
        match rt {
            ReturnType::Default => ReturnType::Default,
            ReturnType::Type(rarrow, typebox) => {
                ReturnType::Type(rarrow, self.convert_boxed_type(typebox))
            }
        }
    }

    fn convert_boxed_type(&self, ty: Box<Type>) -> Box<Type> {
        Box::new(self.convert_type(*ty))
    }

    fn convert_type(&self, ty: Type) -> Type {
        match ty {
            Type::Path(p) => Type::Path(self.convert_type_path(p)),
            Type::Reference(r) => Type::Reference(TypeReference {
                and_token: r.and_token,
                lifetime: r.lifetime,
                mutability: r.mutability,
                elem: self.convert_boxed_type(r.elem),
            }),
            Type::Ptr(ptr) => Type::Reference(self.convert_ptr_to_reference(ptr)),
            _ => ty,
        }
    }

    fn convert_ptr_to_reference(&self, ptr: TypePtr) -> TypeReference {
        TypeReference {
            and_token: Token![&](Span::call_site()),
            lifetime: None,
            mutability: ptr.mutability,
            elem: self.convert_boxed_type(ptr.elem),
        }
    }

    fn convert_type_path(&self, typ: TypePath) -> TypePath {
        let p = typ.path;
        let new_p = Path {
            leading_colon: p.leading_colon,
            segments: p
                .segments
                .into_iter()
                .map(|s| {
                    let old_ident = s.ident.to_string();
                    let args = match s.arguments {
                        PathArguments::AngleBracketed(ab) => {
                            PathArguments::AngleBracketed(AngleBracketedGenericArguments {
                                colon2_token: ab.colon2_token,
                                lt_token: ab.lt_token,
                                gt_token: ab.gt_token,
                                args: self.convert_punctuated(ab.args),
                            })
                        }
                        _ => s.arguments,
                    };
                    let ident = match IDENT_REPLACEMENTS.get(old_ident.as_str()) {
                        None => s.ident,
                        Some(replacement) => Ident::new(replacement, s.ident.span()),
                    };
                    PathSegment {
                        ident: ident,
                        arguments: args,
                    }
                })
                .collect(),
        };
        TypePath {
            qself: typ.qself,
            path: new_p,
        }
    }

    fn convert_punctuated<P>(
        &self,
        pun: Punctuated<GenericArgument, P>,
    ) -> Punctuated<GenericArgument, P>
    where
        P: Default,
    {
        let mut new_pun = Punctuated::new();
        for arg in pun.into_iter() {
            new_pun.push(match arg {
                GenericArgument::Type(t) => GenericArgument::Type(self.convert_type(t)),
                _ => arg,
            });
        }
        new_pun
    }
}
<commit_msg>Clippying.<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 lazy_static::lazy_static;
use proc_macro2::Span;
use std::collections::HashMap;
use syn::punctuated::Punctuated;
use syn::Token;
use syn::{
    parse_quote, AngleBracketedGenericArguments, Attribute, FnArg, ForeignItem, ForeignItemFn,
    GenericArgument, Ident, Item, ItemForeignMod, ItemMod, ItemStruct, PatType, Path,
    PathArguments, PathSegment, ReturnType, Type, TypePath, TypePtr, TypeReference,
};

lazy_static! {
    /// Substitutions from the names constructed by bindgen into those
    /// which cxx uses.
    static ref IDENT_REPLACEMENTS: HashMap<&'static str, String> = {
        let mut map = HashMap::new();
        map.insert("std_unique_ptr", "UniquePtr".to_string());
        map.insert("std_string", "CxxString".to_string());
        map
    };
}

#[derive(Debug)]
pub enum ConvertError {
    NoContent,
}

/// Converts the bindings generated by bindgen into a form suitable
/// for use with `cxx`.
/// Tasks current performed:
/// * Replaces certain identifiers e.g. `std_unique_ptr` with `UniquePtr`
/// * Replaces pointers with references
/// * Removes repr attributes
/// * Removes link_name attributes
/// * Adds include! directives
/// * Adds #[cxx::bridge]
/// At the moment this is almost certainly not using the best practice for parsing
/// stuff. It's multiple simple-but-yucky state machines. Can undoubtedly be
/// simplified and made less error-prone: TODO. Probably the right thing to do
/// is just manipulate the syn types directly.
pub(crate) struct BridgeConverter {
    include_list: Vec<String>,
}

impl BridgeConverter {
    pub fn new(include_list: Vec<String>) -> Self {
        Self { include_list }
    }

    /// Convert a TokenStream of bindgen-generated bindings to a form
    /// suitable for cxx.
    pub(crate) fn convert(&self, bindings: ItemMod) -> Result<ItemMod, ConvertError> {
        match bindings.content {
            None => Err(ConvertError::NoContent),
            Some((brace, items)) => {
                let mut new_items = Vec::new();
                for item in items {
                    new_items.push(match item {
                        Item::ForeignMod(fm) => {
                            self.convert_foreign_mod(fm).map(Item::ForeignMod)?
                        }
                        Item::Struct(s) => self.convert_struct(s).map(Item::Struct)?,
                        _ => item, // TODO - consider rejecting
                    });
                }
                Ok(ItemMod {
                    attrs: bindings.attrs,
                    ident: bindings.ident,
                    semi: bindings.semi,
                    mod_token: bindings.mod_token,
                    vis: bindings.vis,
                    content: Some((brace, new_items)),
                })
            }
        }
    }

    fn convert_foreign_mod(
        &self,
        foreign_mod: ItemForeignMod,
    ) -> Result<ItemForeignMod, ConvertError> {
        let mut new_items = Vec::new();
        for inc in &self.include_list {
            let new_bit = parse_quote! {
                include!(#inc);
            };
            new_items.push(ForeignItem::Macro(new_bit));
        }
        for i in foreign_mod.items {
            new_items.push(match i {
                ForeignItem::Fn(f) => self.convert_foreign_fn(f).map(ForeignItem::Fn)?,
                _ => i,
            })
        }
        Ok(ItemForeignMod {
            attrs: foreign_mod.attrs,
            abi: foreign_mod.abi,
            brace_token: foreign_mod.brace_token,
            items: new_items,
        })
    }

    fn convert_foreign_fn(&self, fun: ForeignItemFn) -> Result<ForeignItemFn, ConvertError> {
        let mut s = fun.sig.clone();
        s.output = self.convert_return_type(s.output);
        s.inputs = s
            .inputs
            .into_iter()
            .map(|i| self.convert_fn_arg(i))
            .collect();
        Ok(ForeignItemFn {
            attrs: self.strip_attr(fun.attrs, "link_name"),
            vis: fun.vis,
            sig: s,
            semi_token: fun.semi_token,
        })
    }

    fn convert_struct(&self, ty: ItemStruct) -> Result<ItemStruct, ConvertError> {
        Ok(ItemStruct {
            attrs: self.strip_attr(ty.attrs, "repr"),
            vis: ty.vis,
            struct_token: ty.struct_token,
            generics: ty.generics,
            fields: ty.fields,
            semi_token: ty.semi_token,
            ident: ty.ident,
        })
    }

    fn strip_attr(&self, attrs: Vec<Attribute>, to_strip: &str) -> Vec<Attribute> {
        attrs
            .into_iter()
            .filter(|a| {
                let i = a.path.get_ident();
                !matches!(i, Some(i2) if *i2 == to_strip)
            })
            .collect::<Vec<Attribute>>()
    }

    fn convert_fn_arg(&self, arg: FnArg) -> FnArg {
        match arg {
            FnArg::Typed(pt) => FnArg::Typed(PatType {
                attrs: pt.attrs,
                pat: pt.pat,
                colon_token: pt.colon_token,
                ty: self.convert_boxed_type(pt.ty),
            }),
            _ => arg,
        }
    }

    fn convert_return_type(&self, rt: ReturnType) -> ReturnType {
        match rt {
            ReturnType::Default => ReturnType::Default,
            ReturnType::Type(rarrow, typebox) => {
                ReturnType::Type(rarrow, self.convert_boxed_type(typebox))
            }
        }
    }

    fn convert_boxed_type(&self, ty: Box<Type>) -> Box<Type> {
        Box::new(self.convert_type(*ty))
    }

    fn convert_type(&self, ty: Type) -> Type {
        match ty {
            Type::Path(p) => Type::Path(self.convert_type_path(p)),
            Type::Reference(r) => Type::Reference(TypeReference {
                and_token: r.and_token,
                lifetime: r.lifetime,
                mutability: r.mutability,
                elem: self.convert_boxed_type(r.elem),
            }),
            Type::Ptr(ptr) => Type::Reference(self.convert_ptr_to_reference(ptr)),
            _ => ty,
        }
    }

    fn convert_ptr_to_reference(&self, ptr: TypePtr) -> TypeReference {
        TypeReference {
            and_token: Token![&](Span::call_site()),
            lifetime: None,
            mutability: ptr.mutability,
            elem: self.convert_boxed_type(ptr.elem),
        }
    }

    fn convert_type_path(&self, typ: TypePath) -> TypePath {
        let p = typ.path;
        let new_p = Path {
            leading_colon: p.leading_colon,
            segments: p
                .segments
                .into_iter()
                .map(|s| {
                    let old_ident = s.ident.to_string();
                    let args = match s.arguments {
                        PathArguments::AngleBracketed(ab) => {
                            PathArguments::AngleBracketed(AngleBracketedGenericArguments {
                                colon2_token: ab.colon2_token,
                                lt_token: ab.lt_token,
                                gt_token: ab.gt_token,
                                args: self.convert_punctuated(ab.args),
                            })
                        }
                        _ => s.arguments,
                    };
                    let ident = match IDENT_REPLACEMENTS.get(old_ident.as_str()) {
                        None => s.ident,
                        Some(replacement) => Ident::new(replacement, s.ident.span()),
                    };
                    PathSegment {
                        ident,
                        arguments: args,
                    }
                })
                .collect(),
        };
        TypePath {
            qself: typ.qself,
            path: new_p,
        }
    }

    fn convert_punctuated<P>(
        &self,
        pun: Punctuated<GenericArgument, P>,
    ) -> Punctuated<GenericArgument, P>
    where
        P: Default,
    {
        let mut new_pun = Punctuated::new();
        for arg in pun.into_iter() {
            new_pun.push(match arg {
                GenericArgument::Type(t) => GenericArgument::Type(self.convert_type(t)),
                _ => arg,
            });
        }
        new_pun
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate validator_derive;
extern crate validator;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate regex;
#[macro_use]
extern crate lazy_static;

use regex::Regex;
use validator::{Validate, ValidationError, ValidationErrors};

fn validate_unique_username(username: &str) -> Result<(), ValidationError> {
    if username == "xXxShad0wxXx" {
        return Err(ValidationError::new("terrible_username"));
    }

    Ok(())
}

fn validate_signup(data: &SignupData) -> Result<(), ValidationError> {
    if data.mail.ends_with("gmail.com") && data.age == 18 {
        return Err(ValidationError::new("stupid_rule"));
    }

    Ok(())
}

#[derive(Debug, Validate, Deserialize)]
#[validate(schema(function = "validate_signup", skip_on_field_errors = "false"))]
struct SignupData {
    #[validate(email)]
    mail: String,
    #[validate(url)]
    site: String,
    #[validate(length(min = "1"), custom = "validate_unique_username")]
    #[serde(rename = "firstName")]
    first_name: String,
    #[validate(range(min = "18", max = "20"))]
    age: u32,
}

#[test]
fn is_fine_with_many_valid_validations() {
    let signup = SignupData {
        mail: "bob@bob.com".to_string(),
        site: "http://hello.com".to_string(),
        first_name: "Bob".to_string(),
        age: 18,
    };

    assert!(signup.validate().is_ok());
}

#[test]
fn failed_validation_points_to_original_field_name() {
    let signup = SignupData {
        mail: "bob@bob.com".to_string(),
        site: "http://hello.com".to_string(),
        first_name: "".to_string(),
        age: 18,
    };
    let res = signup.validate();
    assert!(res.is_err());
    let errs = res.unwrap_err().inner();
    assert!(errs.contains_key("firstName"));
    assert_eq!(errs["firstName"].len(), 1);
    assert_eq!(errs["firstName"][0].code, "length");
}

#[test]
fn test_can_validate_option_fields_with_lifetime() {
    lazy_static! {
        static ref RE2: Regex = Regex::new(r"[a-z]{2}").unwrap();
    }

    #[derive(Debug, Validate)]
    struct PutStruct<'a> {
        #[validate(length(min = "1", max = "10"))]
        name: Option<&'a str>,
        #[validate(range(min = "1", max = "10"))]
        range: Option<usize>,
        #[validate(email)]
        email: Option<&'a str>,
        #[validate(url)]
        url: Option<&'a str>,
        #[validate(contains = "@")]
        text: Option<&'a str>,
        #[validate(regex = "RE2")]
        re: Option<&'a str>,
        #[validate(custom = "check_str")]
        custom: Option<&'a str>,
    }

    fn check_str(_: &str) -> Result<(), ValidationError> {
        Ok(())
    }

    let s = PutStruct {
        name: Some("al"),
        range: Some(2),
        email: Some("hi@gmail.com"),
        url: Some("http://google.com"),
        text: Some("@someone"),
        re: Some("hi"),
        custom: Some("hey"),
    };
    assert!(s.validate().is_ok());
}

#[test]
fn test_can_validate_option_fields_without_lifetime() {
    lazy_static! {
        static ref RE2: Regex = Regex::new(r"[a-z]{2}").unwrap();
    }

    #[derive(Debug, Validate)]
    struct PutStruct {
        #[validate(length(min = "1", max = "10"))]
        name: Option<String>,
        #[validate(length(min = "1", max = "10"))]
        ids: Option<Vec<usize>>,
        #[validate(range(min = "1", max = "10"))]
        range: Option<usize>,
        #[validate(email)]
        email: Option<String>,
        #[validate(url)]
        url: Option<String>,
        #[validate(contains = "@")]
        text: Option<String>,
        #[validate(regex = "RE2")]
        re: Option<String>,
        #[validate(custom = "check_str")]
        custom: Option<String>,
    }

    fn check_str(_: &str) -> Result<(), ValidationError> {
        Ok(())
    }

    let s = PutStruct {
        name: Some("al".to_string()),
        ids: Some(vec![1, 2, 3]),
        range: Some(2),
        email: Some("hi@gmail.com".to_string()),
        url: Some("http://google.com".to_string()),
        text: Some("@someone".to_string()),
        re: Some("hi".to_string()),
        custom: Some("hey".to_string()),
    };
    assert!(s.validate().is_ok());
}

#[test]
fn test_works_with_question_mark_operator() {
    fn some_fn() -> Result<(), ValidationErrors> {
        let signup = SignupData {
            mail: "invalid_email".to_string(),
            site: "http://hello.com".to_string(),
            first_name: "Bob".to_string(),
            age: 18,
        };

        signup.validate()?;
        Ok(())
    }

    assert!(some_fn().is_err());
}
<commit_msg>Added complex tests<commit_after>#[macro_use]
extern crate validator_derive;
extern crate validator;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate regex;
#[macro_use]
extern crate lazy_static;

use regex::Regex;
use validator::{Validate, ValidationError, ValidationErrors};

fn validate_unique_username(username: &str) -> Result<(), ValidationError> {
    if username == "xXxShad0wxXx" {
        return Err(ValidationError::new("terrible_username"));
    }

    Ok(())
}

fn validate_signup(data: &SignupData) -> Result<(), ValidationError> {
    if data.mail.ends_with("gmail.com") && data.age == 18 {
        return Err(ValidationError::new("stupid_rule"));
    }

    Ok(())
}

#[derive(Debug, Validate, Deserialize)]
#[validate(schema(function = "validate_signup", skip_on_field_errors = "false"))]
struct SignupData {
    #[validate(email)]
    mail: String,
    #[validate(url)]
    site: String,
    #[validate(length(min = "1"), custom = "validate_unique_username")]
    #[serde(rename = "firstName")]
    first_name: String,
    #[validate(range(min = "18", max = "20"))]
    age: u32,
}

#[test]
fn is_fine_with_many_valid_validations() {
    let signup = SignupData {
        mail: "bob@bob.com".to_string(),
        site: "http://hello.com".to_string(),
        first_name: "Bob".to_string(),
        age: 18,
    };

    assert!(signup.validate().is_ok());
}

#[test]
fn failed_validation_points_to_original_field_name() {
    let signup = SignupData {
        mail: "bob@bob.com".to_string(),
        site: "http://hello.com".to_string(),
        first_name: "".to_string(),
        age: 18,
    };
    let res = signup.validate();
    assert!(res.is_err());
    let errs = res.unwrap_err().inner();
    assert!(errs.contains_key("firstName"));
    assert_eq!(errs["firstName"].len(), 1);
    assert_eq!(errs["firstName"][0].code, "length");
}

#[test]
fn test_can_validate_option_fields_with_lifetime() {
    lazy_static! {
        static ref RE2: Regex = Regex::new(r"[a-z]{2}").unwrap();
    }

    #[derive(Debug, Validate)]
    struct PutStruct<'a> {
        #[validate(length(min = "1", max = "10"))]
        name: Option<&'a str>,
        #[validate(length(min = "1", max = "10"))]
        address: Option<Option<&'a str>>,
        #[validate(range(min = "1", max = "100"))]
        age: Option<Option<usize>>,
        #[validate(range(min = "1", max = "10"))]
        range: Option<usize>,
        #[validate(email)]
        email: Option<&'a str>,
        #[validate(url)]
        url: Option<&'a str>,
        #[validate(contains = "@")]
        text: Option<&'a str>,
        #[validate(regex = "RE2")]
        re: Option<&'a str>,
        #[validate(custom = "check_str")]
        custom: Option<&'a str>,
    }

    fn check_str(_: &str) -> Result<(), ValidationError> {
        Ok(())
    }

    let s = PutStruct {
        name: Some("al"),
        address: Some(Some("gol")),
        age: Some(Some(20)),
        range: Some(2),
        email: Some("hi@gmail.com"),
        url: Some("http://google.com"),
        text: Some("@someone"),
        re: Some("hi"),
        custom: Some("hey"),
    };
    assert!(s.validate().is_ok());
}

#[test]
fn test_can_validate_option_fields_without_lifetime() {
    lazy_static! {
        static ref RE2: Regex = Regex::new(r"[a-z]{2}").unwrap();
    }

    #[derive(Debug, Validate)]
    struct PutStruct {
        #[validate(length(min = "1", max = "10"))]
        name: Option<String>,
        #[validate(length(min = "1", max = "10"))]
        address: Option<Option<String>>,
        #[validate(length(min = "1", max = "10"))]
        ids: Option<Vec<usize>>,
        #[validate(range(min = "1", max = "100"))]
        age: Option<Option<usize>>,
        #[validate(range(min = "1", max = "10"))]
        range: Option<usize>,
        #[validate(email)]
        email: Option<String>,
        #[validate(url)]
        url: Option<String>,
        #[validate(contains = "@")]
        text: Option<String>,
        #[validate(regex = "RE2")]
        re: Option<String>,
        #[validate(custom = "check_str")]
        custom: Option<String>,
    }

    fn check_str(_: &str) -> Result<(), ValidationError> {
        Ok(())
    }

    let s = PutStruct {
        name: Some("al".to_string()),
        address: Some(Some("gol".to_string())),
        ids: Some(vec![1, 2, 3]),
        age: Some(Some(20)),
        range: Some(2),
        email: Some("hi@gmail.com".to_string()),
        url: Some("http://google.com".to_string()),
        text: Some("@someone".to_string()),
        re: Some("hi".to_string()),
        custom: Some("hey".to_string()),
    };
    assert!(s.validate().is_ok());
}

#[test]
fn test_works_with_question_mark_operator() {
    fn some_fn() -> Result<(), ValidationErrors> {
        let signup = SignupData {
            mail: "invalid_email".to_string(),
            site: "http://hello.com".to_string(),
            first_name: "Bob".to_string(),
            age: 18,
        };

        signup.validate()?;
        Ok(())
    }

    assert!(some_fn().is_err());
}
<|endoftext|>


<commit_before>/// Traversing pages, splits, merges

use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;

use super::*;

pub enum SeekRes {
    ShortCircuitSome(Value),
    Node(tree::Node),
    ShortCircuitNone,
    Split(PageID),
    Merge, // TODO
}

#[derive(Debug, Clone)]
pub struct FragView {
    pub pid: PageID,
    pub head: Raw,
    pub stack: *const Stack<*const Frag>,
    pub depth: usize,
}

impl FragView {
    pub fn stack_len(&self) -> usize {
        unsafe { (*self.stack).len() }
    }

    pub fn stack_iter(&self) -> stack::StackIter<*const Frag> {
        unsafe { (*self.stack).into_iter() }
    }

    pub fn consolidate(&self) -> tree::Node {
        // if let Data::Index(ref mut ptrs) = parent.data {
        // let search =
        // ptrs.binary_search_by(|&(ref k, ref _v)| Bound::Inc(k.clone()).cmp(&lhs.lo));
        // if let Ok(idx) = search {
        // ptrs.remove(idx);
        // }
        // ptrs.push((lhs.lo.inner().unwrap(), lhs.id));
        // ptrs.push((rhs.lo.inner().unwrap(), rhs.id));
        // ptrs.sort_by(|a, b| a.0.cmp(&b.0));
        // }
        //
        unimplemented!()
    }

    pub fn should_split(&self) -> bool {
        unimplemented!()
    }

    /// returns child_split -> lhs, rhs, parent_frag
    pub fn split(&self,
                 new_id: PageID)
                 -> (*const Stack<*const Frag>, *const Stack<*const Frag>, ParentSplit) {
        // let parent_split = Frag::ParentSplit {
        // at: 1,
        // to: 1,
        // from: 1,
        // hi: 1,
        // };
        //
        unimplemented!()
    }
}

pub type Seek = (SeekRes, FragView);

pub struct ParentSplit {
    pub at: Vec<u8>,
    pub to: PageID,
    pub from: PageID,
    pub hi: Vec<u8>, // lets us stop traversing frags
}

pub enum Frag {
    Set(Key, Value),
    Del(Key),
    Base(tree::Node),
    ChildSplit {
        at: Vec<u8>,
        to: PageID, // TODO should this be physical?
    },
    ParentSplit(ParentSplit),
}

pub struct Pages {
    pub inner: Radix<Stack<*const Frag>>,
    max_id: AtomicUsize,
    free: Stack<PageID>,
}

impl Default for Pages {
    fn default() -> Pages {
        Pages {
            inner: Radix::default(),
            max_id: AtomicUsize::new(0),
            free: Stack::default(),
        }
    }
}

impl Pages {
    pub fn seek(&self, pid: PageID, key: Key) -> Seek {
        use self::Frag::*;
        use self::ParentSplit as PS;

        let stack_ptr = self.inner.get(pid).unwrap();

        // welcome to the danger zone
        unsafe {
            let (head, iter) = (*stack_ptr).iter_at_head();
            let mut depth = 0;
            let mut ret = None;

            // traverse stack until we find relevant frag, or base
            for frag_ptr in iter {
                depth += 1;
                if let Some(result) = match **frag_ptr {
                    // if we've traversed to a base node, we can return it
                    Base(ref node) => Some(SeekRes::Node(node.clone())),

                    // if we encounter a set or del for our key,
                    // we can short-circuit the request
                    Set(ref k, ref v) if k == &key => Some(SeekRes::ShortCircuitSome(v.clone())),
                    Del(ref k) if k == &key => Some(SeekRes::ShortCircuitNone),

                    // if we encounter a relevant split, pass it to the caller
                    ChildSplit { ref at, ref to } if at <= &key => Some(SeekRes::Split(*to)),
                    ParentSplit(PS { ref at, ref to, ref from, ref hi }) if at <= &key &&
                                                                            hi > &key => {
                        Some(SeekRes::Split(*to))
                    }
                    _ => None,
                } {
                    ret = Some(result);
                    break;
                }
            }
            let meta = FragView {
                pid: pid,
                head: head,
                stack: stack_ptr,
                depth: depth,
            };
            (ret.unwrap(), meta)
        }
    }

    pub fn cap(&self, pid: PageID, old: Raw, new: *const Frag) -> Result<(), Raw> {
        let stack_ptr = self.inner.get(pid).unwrap();
        unsafe { (*stack_ptr).cap(old, new) }
    }

    pub fn allocate(&self) -> PageID {
        // TODO free list/epoch gc
        let id = self.free.pop().unwrap_or_else(|| self.max_id.fetch_add(1, SeqCst));
        let stack = Stack::default();
        let stack_ptr = raw(stack);
        self.inner.insert(id, stack_ptr).unwrap();
        id
    }

    pub fn free(&self, pid: PageID) {
        // TODO epoch
        self.free.push(pid);
        let stack_ptr = self.inner.get(pid).unwrap();
        unsafe { (*stack_ptr).pop_all() };
    }

    pub fn insert(&self, pid: PageID, frag: *const Frag) -> Result<(), ()> {
        unimplemented!()
    }
}

// consolidation:
//  CAS head of stack with consolidated page
//  add head of stack to GC epoch if successful

// split:
//  new page, consolidated from right side
//  split -> consolidated left page
//  insert dangling right side into radix
//  CAS split -> left page into radix
//      if failed, insta-free right side & pid, don't retry
//  CAS update onto the parent index
//
<commit_msg>push around insertion responsibility<commit_after>/// Traversing pages, splits, merges

use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;

use super::*;

pub enum SeekRes {
    ShortCircuitSome(Value),
    Node(tree::Node),
    ShortCircuitNone,
    Split(PageID),
    Merge, // TODO
}

#[derive(Debug, Clone)]
pub struct FragView {
    pub pid: PageID,
    pub head: Raw,
    pub stack: *const Stack<*const Frag>,
    pub depth: usize,
}

impl FragView {
    pub fn stack_len(&self) -> usize {
        unsafe { (*self.stack).len() }
    }

    pub fn stack_iter(&self) -> stack::StackIter<*const Frag> {
        unsafe { (*self.stack).into_iter() }
    }

    pub fn consolidate(&self) -> tree::Node {
        // if let Data::Index(ref mut ptrs) = parent.data {
        // let search =
        // ptrs.binary_search_by(|&(ref k, ref _v)| Bound::Inc(k.clone()).cmp(&lhs.lo));
        // if let Ok(idx) = search {
        // ptrs.remove(idx);
        // }
        // ptrs.push((lhs.lo.inner().unwrap(), lhs.id));
        // ptrs.push((rhs.lo.inner().unwrap(), rhs.id));
        // ptrs.sort_by(|a, b| a.0.cmp(&b.0));
        // }
        //
        unimplemented!()
    }

    pub fn should_split(&self) -> bool {
        unimplemented!()
    }

    /// returns child_split -> lhs, rhs, parent_frag
    pub fn split(&self,
                 new_id: PageID)
                 -> (*const Stack<*const Frag>, *const Stack<*const Frag>, ParentSplit) {
        // let parent_split = Frag::ParentSplit {
        // at: 1,
        // to: 1,
        // from: 1,
        // hi: 1,
        // };
        //
        unimplemented!()
    }
}

pub type Seek = (SeekRes, FragView);

pub struct ParentSplit {
    pub at: Vec<u8>,
    pub to: PageID,
    pub from: PageID,
    pub hi: Vec<u8>, // lets us stop traversing frags
}

pub enum Frag {
    Set(Key, Value),
    Del(Key),
    Base(tree::Node),
    ChildSplit {
        at: Vec<u8>,
        to: PageID, // TODO should this be physical?
    },
    ParentSplit(ParentSplit),
}

pub struct Pages {
    pub inner: Radix<Stack<*const Frag>>,
    max_id: AtomicUsize,
    free: Stack<PageID>,
}

impl Default for Pages {
    fn default() -> Pages {
        Pages {
            inner: Radix::default(),
            max_id: AtomicUsize::new(0),
            free: Stack::default(),
        }
    }
}

impl Pages {
    pub fn seek(&self, pid: PageID, key: Key) -> Seek {
        use self::Frag::*;
        use self::ParentSplit as PS;

        let stack_ptr = self.inner.get(pid).unwrap();

        // welcome to the danger zone
        unsafe {
            let (head, iter) = (*stack_ptr).iter_at_head();
            let mut depth = 0;
            let mut ret = None;

            // traverse stack until we find relevant frag, or base
            for frag_ptr in iter {
                depth += 1;
                if let Some(result) = match **frag_ptr {
                    // if we've traversed to a base node, we can return it
                    Base(ref node) => Some(SeekRes::Node(node.clone())),

                    // if we encounter a set or del for our key,
                    // we can short-circuit the request
                    Set(ref k, ref v) if k == &key => Some(SeekRes::ShortCircuitSome(v.clone())),
                    Del(ref k) if k == &key => Some(SeekRes::ShortCircuitNone),

                    // if we encounter a relevant split, pass it to the caller
                    ChildSplit { ref at, ref to } if at <= &key => Some(SeekRes::Split(*to)),
                    ParentSplit(PS { ref at, ref to, ref from, ref hi }) if at <= &key &&
                                                                            hi > &key => {
                        Some(SeekRes::Split(*to))
                    }
                    _ => None,
                } {
                    ret = Some(result);
                    break;
                }
            }
            let meta = FragView {
                pid: pid,
                head: head,
                stack: stack_ptr,
                depth: depth,
            };
            (ret.unwrap(), meta)
        }
    }

    pub fn cap(&self, pid: PageID, old: Raw, new: *const Frag) -> Result<(), Raw> {
        let stack_ptr = self.inner.get(pid).unwrap();
        unsafe { (*stack_ptr).cap(old, new) }
    }

    pub fn allocate(&self) -> PageID {
        // TODO free list/epoch gc
        let id = self.free.pop().unwrap_or_else(|| self.max_id.fetch_add(1, SeqCst));
        id
    }

    pub fn free(&self, pid: PageID) {
        // TODO epoch
        self.free.push(pid);
        let stack_ptr = self.inner.get(pid).unwrap();
        unsafe { (*stack_ptr).pop_all() };
    }

    pub fn insert(&self, pid: PageID, frag: *const Frag) -> Result<(), ()> {
        let stack = Stack::default();
        stack.push(frag);
        self.inner.insert(pid, raw(stack)).map(|_| ()).map_err(|_| ())
    }
}

// consolidation:
//  CAS head of stack with consolidated page
//  add head of stack to GC epoch if successful

// split:
//  new page, consolidated from right side
//  split -> consolidated left page
//  insert dangling right side into radix
//  CAS split -> left page into radix
//      if failed, insta-free right side & pid, don't retry
//  CAS update onto the parent index
//
<|endoftext|>


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

use dom::bindings::codegen::BindingDeclarations::HTMLLIElementBinding;
use dom::bindings::codegen::InheritTypes::HTMLLIElementDerived;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::error::ErrorResult;
use dom::document::Document;
use dom::element::HTMLLIElementTypeId;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId};
use servo_util::str::DOMString;

#[deriving(Encodable)]
pub struct HTMLLIElement {
    pub htmlelement: HTMLElement,
}

impl HTMLLIElementDerived for EventTarget {
    fn is_htmllielement(&self) -> bool {
        self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLLIElementTypeId))
    }
}

impl HTMLLIElement {
    pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLLIElement {
        HTMLLIElement {
            htmlelement: HTMLElement::new_inherited(HTMLLIElementTypeId, localName, document)
        }
    }

    pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLLIElement> {
        let element = HTMLLIElement::new_inherited(localName, document);
        Node::reflect_node(box element, document, HTMLLIElementBinding::Wrap)
    }
}

pub trait HTMLLIElementMethods {
    fn Value(&self) -> i32;
    fn SetValue(&mut self, _value: i32) -> ErrorResult;
    fn Type(&self) -> DOMString;
    fn SetType(&mut self, _type: DOMString) -> ErrorResult;
}

impl<'a> HTMLLIElementMethods for JSRef<'a, HTMLLIElement> {
    fn Value(&self) -> i32 {
        0
    }

    fn SetValue(&mut self, _value: i32) -> ErrorResult {
        Ok(())
    }

    fn Type(&self) -> DOMString {
        "".to_owned()
    }

    fn SetType(&mut self, _type: DOMString) -> ErrorResult {
        Ok(())
    }
}
<commit_msg>Remove needless '&mut self' from HTMLLIElementMethods.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::BindingDeclarations::HTMLLIElementBinding;
use dom::bindings::codegen::InheritTypes::HTMLLIElementDerived;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::error::ErrorResult;
use dom::document::Document;
use dom::element::HTMLLIElementTypeId;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId};
use servo_util::str::DOMString;

#[deriving(Encodable)]
pub struct HTMLLIElement {
    pub htmlelement: HTMLElement,
}

impl HTMLLIElementDerived for EventTarget {
    fn is_htmllielement(&self) -> bool {
        self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLLIElementTypeId))
    }
}

impl HTMLLIElement {
    pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLLIElement {
        HTMLLIElement {
            htmlelement: HTMLElement::new_inherited(HTMLLIElementTypeId, localName, document)
        }
    }

    pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLLIElement> {
        let element = HTMLLIElement::new_inherited(localName, document);
        Node::reflect_node(box element, document, HTMLLIElementBinding::Wrap)
    }
}

pub trait HTMLLIElementMethods {
    fn Value(&self) -> i32;
    fn SetValue(&self, _value: i32) -> ErrorResult;
    fn Type(&self) -> DOMString;
    fn SetType(&self, _type: DOMString) -> ErrorResult;
}

impl<'a> HTMLLIElementMethods for JSRef<'a, HTMLLIElement> {
    fn Value(&self) -> i32 {
        0
    }

    fn SetValue(&self, _value: i32) -> ErrorResult {
        Ok(())
    }

    fn Type(&self) -> DOMString {
        "".to_owned()
    }

    fn SetType(&self, _type: DOMString) -> ErrorResult {
        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate ncurses;
use std::mem::uninitialized;
use std::ptr::null_mut;
use color::ColorPair;
use color::Color::*;
use terminal::Terminal;

pub struct SystemTerminal {
    default_color: ColorPair,
}

/// Initializes system terminal. **WARNING: Must NOT be called more than once in runtime.**
pub fn init_terminal() -> Result<SystemTerminal,()> {
    if ncurses::initscr() == null_mut()
    || ncurses::noecho() != ncurses::OK
    || ncurses::start_color() != ncurses::OK {
        return Err(())
    }

    for i in 0..8 {
        for j in 0..8 {
            if ncurses::init_pair(i * 8 + j + 1, i, j) != ncurses::OK {
                return Err(())
            }
        }
    }

    Ok(SystemTerminal { default_color: ColorPair { fg: Some(DarkWhite), bg: Some(DarkBlack) }})
}

impl Terminal for SystemTerminal {
    fn size(&self) -> (u16, u16) {
        let mut x = unsafe { uninitialized() };
        let mut y = unsafe { uninitialized() };
        ncurses::getmaxyx(ncurses::stdscr, &mut y, &mut x);
        (x as u16, y as u16)
    }

    fn resize(&mut self, _: u16, _: u16) -> Result<(),()> {
        Err(()) // unsupported
    }

    fn get_cursor_pos(&self) -> (u16, u16) {
        let mut x = unsafe { uninitialized() };
        let mut y = unsafe { uninitialized() };
        ncurses::getyx(ncurses::stdscr, &mut y, &mut x);
        (x as u16, y as u16)
    }

    fn set_cursor_pos(&mut self, x: u16, y: u16) ->  Result<(),()> {
        if ncurses::mv(y as i32, x as i32) == ncurses::OK {
            Ok(())
        } else {
            Err(())
        }
    }

    fn get_default_color(&self) -> ColorPair {
        self.default_color
    }

    fn set_default_color(&mut self, color: ColorPair) {
        self.default_color = color.compose(self.default_color);
    }

    fn write_colored<T>(&mut self, s: &str, colors: T) where T: Iterator<Item=ColorPair> {
        for (ch, co) in s.chars().zip(colors) {
            set_color(co.compose(self.default_color));
            ncurses::addch(ch as u64);
        }
        ncurses::refresh();
    }
}

impl Drop for SystemTerminal {
    fn drop(&mut self) {
        ncurses::endwin();
    }
}

fn set_color(color: ColorPair) {
    ncurses::attrset(match color.fg.unwrap() {
        LightBlack| LightRed | LightGreen | LightYellow | LightBlue | LightMagenta | LightCyan
        | LightWhite => ncurses::A_BOLD(),
        _ => 0,
    } | ncurses::COLOR_PAIR(match color.fg.unwrap() {
        DarkBlack | LightBlack => 0,
        DarkRed | LightRed => 1,
        DarkGreen | LightGreen => 2,
        DarkYellow | LightYellow => 3,
        DarkBlue | LightBlue => 4,
        DarkMagenta | LightMagenta => 5,
        DarkCyan | LightCyan => 6,
        DarkWhite | LightWhite => 7,
    } * 8 + match color.bg.unwrap() {
        DarkBlack | LightBlack => 0,
        DarkRed | LightRed => 1,
        DarkGreen | LightGreen => 2,
        DarkYellow | LightYellow => 3,
        DarkBlue | LightBlue => 4,
        DarkMagenta | LightMagenta => 5,
        DarkCyan | LightCyan => 6,
        DarkWhite | LightWhite => 7,
    } + 1));
}
<commit_msg>Adapted ncurses impl to new interface<commit_after>extern crate ncurses;
use std::cmp::{min, max};
use std::mem::uninitialized;
use std::ptr::null_mut;
use color::ColorPair;
use color::Color::*;
use terminal::Terminal;

pub struct SystemTerminal {
    default_color: ColorPair,
}

/// Initializes system terminal. **WARNING: Must NOT be called more than once in runtime.**
pub fn init_terminal() -> Result<SystemTerminal,()> {
    if ncurses::initscr() == null_mut()
    || ncurses::noecho() != ncurses::OK
    || ncurses::start_color() != ncurses::OK {
        return Err(())
    }

    for i in 0..8 {
        for j in 0..8 {
            if ncurses::init_pair(i * 8 + j + 1, i, j) != ncurses::OK {
                return Err(())
            }
        }
    }

    Ok(SystemTerminal { default_color: ColorPair { fg: Some(DarkWhite), bg: Some(DarkBlack) }})
}

impl Terminal for SystemTerminal {
    fn size(&self) -> (u16, u16) {
        let mut x = unsafe { uninitialized() };
        let mut y = unsafe { uninitialized() };
        ncurses::getmaxyx(ncurses::stdscr, &mut y, &mut x);
        (x as u16, y as u16)
    }

    fn resize(&mut self, _: u16, _: u16) -> Result<(),()> {
        Err(()) // unsupported
    }

    fn get_cursor_pos(&self) -> (u16, u16) {
        let mut x = unsafe { uninitialized() };
        let mut y = unsafe { uninitialized() };
        ncurses::getyx(ncurses::stdscr, &mut y, &mut x);
        (x as u16, y as u16)
    }

    fn set_cursor_pos(&mut self, x: u16, y: u16) ->  Result<(),()> {
        if ncurses::mv(y as i32, x as i32) == ncurses::OK {
            Ok(())
        } else {
            Err(())
        }
    }

    fn get_default_color(&self) -> ColorPair {
        self.default_color
    }

    fn set_default_color(&mut self, color: ColorPair) {
        self.default_color = color.compose(self.default_color);
    }

    #[allow(unused_must_use)]
    fn print_at<T>(&mut self, x: i16, y: i16, line: T) where T: Iterator<Item=(char, ColorPair)> {
        let (max_x, max_y) = self.size();
        if y < 0 || y as u16 > max_y {
            return;
        }
        let (old_x, old_y) = self.get_cursor_pos();
        self.set_cursor_pos(min(max(0, x) as u16, max_x - 1), y as u16);

        let s = max(0 - x, 0) as usize;
        for (ch, co) in line.skip(s).take_while(|&(c, _)| c != '\n') {
            set_color(co.compose(self.default_color));
            ncurses::addch(ch as u64);
        }
        self.set_cursor_pos(old_x, old_y);
    }

    fn present(&mut self) {
        ncurses::refresh();
    }
}

impl Drop for SystemTerminal {
    fn drop(&mut self) {
        ncurses::endwin();
    }
}

fn set_color(color: ColorPair) {
    ncurses::attrset(match color.fg.unwrap() {
        LightBlack| LightRed | LightGreen | LightYellow | LightBlue | LightMagenta | LightCyan
        | LightWhite => ncurses::A_BOLD(),
        _ => 0,
    } | ncurses::COLOR_PAIR(match color.fg.unwrap() {
        DarkBlack | LightBlack => 0,
        DarkRed | LightRed => 1,
        DarkGreen | LightGreen => 2,
        DarkYellow | LightYellow => 3,
        DarkBlue | LightBlue => 4,
        DarkMagenta | LightMagenta => 5,
        DarkCyan | LightCyan => 6,
        DarkWhite | LightWhite => 7,
    } * 8 + match color.bg.unwrap() {
        DarkBlack | LightBlack => 0,
        DarkRed | LightRed => 1,
        DarkGreen | LightGreen => 2,
        DarkYellow | LightYellow => 3,
        DarkBlue | LightBlue => 4,
        DarkMagenta | LightMagenta => 5,
        DarkCyan | LightCyan => 6,
        DarkWhite | LightWhite => 7,
    } + 1));
}
<|endoftext|>


<commit_before>use ascii::{AsAsciiStrError, AsciiChar, AsciiStr, ToAsciiChar, ToAsciiCharError};
use std::ascii::AsciiExt;
use std::borrow::{Borrow, BorrowMut};
use std::cmp::{Eq, PartialEq};
use std::convert::{AsRef, AsMut, From, Into};
use std::hash::{Hash, Hasher};
use std::ops::{Deref, DerefMut};
use std::{fmt, mem, ptr, slice, str};
use ::{PascalStr, PASCAL_STRING_BUF_SIZE};

/// An owned `PascalString`. This string type stores its data the stack. It is always 256 bytes long, with
/// the first byte storing the length.
///
/// This string type uses Ascii encoding.
pub struct PascalString {
    /// The length of this string.
    len: u8,
    /// The characters of this string, encoded as an ascii array.
    chars: [AsciiChar; PASCAL_STRING_BUF_SIZE]
}

impl PascalString {
    /// Creates a new, empty `PascalString`.
    #[inline]
    pub fn new() -> Self {
        Default::default()
    }

    /// Create a new `PascalString` using the contents of `bytes`.
    ///
    /// Returns an `Err` if `bytes` is longer than 255 characters, or it does not contain
    /// Ascii encoded characters.
    #[inline]
    pub fn from<B: AsRef<[u8]>>(bytes: B) -> Result<Self, PascalStringCreateError> {
        PascalString::_from(bytes.as_ref())
    }

    fn _from(bytes: &[u8]) -> Result<Self, PascalStringCreateError>  {
        let len = bytes.len();
        if len > PASCAL_STRING_BUF_SIZE {
            return Err(PascalStringCreateError::InputTooLong);
        }
        // Perform ascii check
        let ascii = try!(AsciiStr::from_ascii(bytes));

        let mut string = PascalString::new();
        string.len = len as u8;
        for i in 0..len {
            string[i] = ascii[i];
        }
        Ok(string)
    }

    /// Push an ascii convertible character onto this string.
    ///
    /// # Panics
    ///
    /// Panics if the string is full, of if the `character` is not a valid ascii character.
    #[inline]
    pub fn push<C: ToAsciiChar>(&mut self, character: C) {
        self.try_push(character).unwrap();
    }

    /// Attempt to push an ascii convertible character onto the end of this `PascalString`.
    ///
    /// Returns `Err(_)` if the character cannot be pushed because this `PascalString` is full, or if
    /// `character` is not a valid ascii character.
    #[inline]
    pub fn try_push<C: ToAsciiChar>(&mut self, character: C) -> Result<(), PascalStringAppendError> {
        self._try_push(try!(AsciiChar::from(character)))
    }

    fn _try_push(&mut self, ch: AsciiChar) -> Result<(), PascalStringAppendError> {
        if self.is_full() {
            return Err(PascalStringAppendError::AppendedStringTooLong)
        }
        let len = self.len();
        self[len] = ch;
        self.len += 1;
        Ok(())
    }

    /// Append a given string slice onto the end of this `PascalString`.
    ///
    /// # Panics
    ///
    /// Panics if the string cannot be pushed to because this `PascalString` is full.
    #[inline]
    pub fn push_str<S: AsRef<str>>(&mut self, s: S) {
        self.try_push_str(s).unwrap();
    }

    /// Attempt to append a given string slice onto the end of this `PascalString`.
    ///
    /// Returns `Err(_)` if the string cannot be pushed because this `PascalString` is full.
    #[inline]
    pub fn try_push_str<S: AsRef<str>>(&mut self, s: S) -> Result<(), PascalStringAppendError> {
        self._try_push_str(s.as_ref())
    }

    fn _try_push_str(&mut self, s: &str) -> Result<(), PascalStringAppendError> {
        unimplemented!()
    }

    /// Removes the last character from the string buffer and returns it.
    ///
    /// Returns `None` if this `PascalString` is empty.
    pub fn pop(&mut self) -> Option<AsciiChar> {
        if self.is_empty() {
            return None;
        }
        let c = self.chars[self.len as usize];
        self.len -= 1;
        Some(c)
    }

    pub fn remove(&mut self, index: u8) -> AsciiChar {
        assert!(self.len < index);
        let len = self.len as usize;
        let index = index as isize;
        let c = self[len];
        // Shift everything to the right of the removed character to the left to cover up the hole
        // left.
        unsafe {
            let ptr = self.as_mut_ptr().offset(index);
            ptr::copy(ptr.offset(1), ptr, len - index.abs() as usize - 1);
        }
        self.len -= 1;
        c
    }

    #[inline]
    pub fn insert<C: ToAsciiChar>(&mut self, ch: C, index: u8) {
        self._insert(AsciiChar::from(ch).unwrap(), index)
    }

    fn _insert(&mut self, ch: AsciiChar, index: u8) {
        assert!(self.len < index);
        assert!(!self.is_full());
        let len = self.len as usize;
        let index = index as isize;
        // Shift everything to the right of `index` 1 place to the right to make room for the
        // new character.
        unsafe {
            let ptr = self.as_mut_ptr().offset(index);
            ptr::copy(ptr, ptr.offset(1), len - index.abs() as usize - 1);
        }
        self[len] = ch;
        self.len += 1;
    }

    /// Truncates this String, removing all contents.
    ///
    /// Does not zero the values of the string.
    #[inline]
    pub fn clear(&mut self) {
        self.len = 0;
    }

    /// Consumes this `PascalString`, and returns its inner state as a `[u8; 256]`, where the first byte
    /// is the length.
    ///
    /// Note that if the string has been truncated, bytes beyond the end of the string will not have been
    /// zeroed.
    #[inline]
    pub fn to_array(self) -> [u8; PASCAL_STRING_BUF_SIZE + 1] {
        self.into()
    }

    /// Get a character in the string, without checking if the index is within the bounds of `len()`.
    ///
    /// This method cannot cause memory unsafety because of the size of `index`. However, it can give access
    /// to stale characters if `index` is greater than `len()`, and `len() < 255`.
    #[inline]
    pub fn get_unchecked(&self, index: u8) -> AsciiChar {
        self.chars[index as usize]
    }
}

impl Default for PascalString {
    #[inline]
    fn default() -> Self {
        PascalString {
            len: 0,
            chars: [AsciiChar::Null; PASCAL_STRING_BUF_SIZE]
        }
    }
}

impl Clone for PascalString {
    fn clone(&self) -> Self {
        let mut clone = PascalString::default();
        clone.len = self.len;
        unsafe {
            ptr::copy_nonoverlapping(&self.chars, &mut clone.chars, PASCAL_STRING_BUF_SIZE);
        }
        clone
    }
}

impl Eq for PascalString { }

impl PartialEq for PascalString {
    fn eq(&self, other: &PascalString) -> bool {
        if self.len != other.len {
            return false;
        }
        self.chars.iter().zip(other.chars.iter()).all(|(c0, c1)| c0 == c1)
    }
}

impl Hash for PascalString {
    fn hash<H: Hasher>(&self, state: &mut H) {
        state.write_u8(self.len);
        state.write(self.as_ref());
    }
}

impl AsciiExt for PascalString {
    type Owned = Self;

    fn is_ascii(&self) -> bool {
        true
    }

    fn to_ascii_uppercase(&self) -> Self::Owned {
        let mut upper = self.clone();
        upper.make_ascii_uppercase();
        upper
    }

    fn to_ascii_lowercase(&self) -> Self::Owned {
        let mut lower = self.clone();
        lower.make_ascii_lowercase();
        lower
    }

    fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
        self.chars.iter().zip(other.chars.iter()).all(|(c0, c1)| c0.eq_ignore_ascii_case(&c1))
    }

    fn make_ascii_uppercase(&mut self) {
        for c in self.chars.iter_mut() {
            c.make_ascii_uppercase();
        }
    }

    fn make_ascii_lowercase(&mut self) {
        for c in self.chars.iter_mut() {
            c.make_ascii_lowercase();
        }
    }
}

impl fmt::Debug for PascalString {
    fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
        write!(fmtr, "{}", self)
    }
}

impl fmt::Display for PascalString {
    fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
        fmtr.pad(self.as_ref())
    }
}

impl Deref for PascalString {
    type Target = PascalStr;
    fn deref(&self) -> &Self::Target {
        let ascii_str: &[AsciiChar] = self.as_ref();
        unsafe {
            mem::transmute(ascii_str)
        }
    }
}

impl DerefMut for PascalString {
    fn deref_mut(&mut self) -> &mut Self::Target {
        let ascii_str: &mut [AsciiChar] = self.as_mut();
        unsafe {
            mem::transmute(ascii_str)
        }
    }
}

impl AsRef<PascalStr> for PascalString {
    fn as_ref(&self) -> &PascalStr {
        self.deref()
    }
}

impl AsRef<str> for PascalString {
    fn as_ref(&self) -> &str {
        let bytes: &[u8] = self.as_ref();
        str::from_utf8(bytes).unwrap()
    }
}

impl AsRef<[u8]> for PascalString {
    fn as_ref(&self) -> &[u8] {
        unsafe {
            slice::from_raw_parts(self.chars.as_ptr() as *const u8, self.len as usize)
        }
    }
}

impl AsRef<AsciiStr> for PascalString {
    fn as_ref(&self) -> &AsciiStr {
        let bytes: &[u8] = self.as_ref();
        AsciiStr::from_ascii(bytes).unwrap()
    }
}

impl AsRef<[AsciiChar]> for PascalString {
    fn as_ref(&self) -> &[AsciiChar] {
        unsafe {
            slice::from_raw_parts(self.chars.as_ptr(), self.len as usize)
        }
    }
}

impl AsMut<[AsciiChar]> for PascalString {
    fn as_mut(&mut self) -> &mut [AsciiChar] {
        unsafe {
            slice::from_raw_parts_mut(self.chars.as_mut_ptr(), self.len as usize)
        }
    }
}

impl Borrow<PascalStr> for PascalString {
    fn borrow(&self) -> &PascalStr {
        self.deref()
    }
}

impl BorrowMut<PascalStr> for PascalString {
    fn borrow_mut(&mut self) -> &mut PascalStr {
        self.deref_mut()
    }
}

impl Borrow<str> for PascalString {
    fn borrow(&self) -> &str {
        self.as_ref()
    }
}

impl Borrow<[u8]> for PascalString {
    fn borrow(&self) -> &[u8] {
        self.as_ref()
    }
}

impl Borrow<AsciiStr> for PascalString {
    fn borrow(&self) -> &AsciiStr {
        self.as_ref()
    }
}

impl Borrow<[AsciiChar]> for PascalString {
    fn borrow(&self) -> &[AsciiChar] {
        self.as_ref()
    }
}

impl BorrowMut<[AsciiChar]> for PascalString {
    fn borrow_mut(&mut self) -> &mut [AsciiChar] {
        self.as_mut()
    }
}

impl Into<[u8; PASCAL_STRING_BUF_SIZE + 1]> for PascalString {
    fn into(self) -> [u8; PASCAL_STRING_BUF_SIZE + 1] {
        let mut array = [0u8; PASCAL_STRING_BUF_SIZE + 1];
        array[0] = self.len;
        unsafe {
            let chars_ptr = &self.chars as *const _ as *const [u8; PASCAL_STRING_BUF_SIZE];
            let array_ptr = (&mut array as *mut _).offset(1) as *mut [u8; PASCAL_STRING_BUF_SIZE];
            ptr::copy_nonoverlapping(chars_ptr, array_ptr, PASCAL_STRING_BUF_SIZE);
        }
        array
    }
}

impl Into<String> for PascalString {
    fn into(self) -> String {
        String::from_utf8_lossy(self.as_ref()).into_owned()
    }
}

#[derive(Debug, PartialEq)]
pub enum PascalStringCreateError {
    InputTooLong,
    NotValidAscii(AsAsciiStrError)
}

impl From<AsAsciiStrError> for PascalStringCreateError {
    fn from(e: AsAsciiStrError) -> Self {
        PascalStringCreateError::NotValidAscii(e)
    }
}

#[derive(Debug, PartialEq)]
pub enum PascalStringAppendError {
    AppendedStringTooLong,
    InvalidCharAppended(ToAsciiCharError),
    InvalidStringAppended(AsAsciiStrError)
}

impl From<ToAsciiCharError> for PascalStringAppendError {
    fn from(e: ToAsciiCharError) -> Self {
        PascalStringAppendError::InvalidCharAppended(e)
    }
}

impl From<AsAsciiStrError> for PascalStringAppendError {
    fn from(e: AsAsciiStrError) -> Self {
        PascalStringAppendError::InvalidStringAppended(e)
    }
}<commit_msg>Fix PascalString::_push to avoid bounds check error<commit_after>use ascii::{AsAsciiStrError, AsciiChar, AsciiStr, ToAsciiChar, ToAsciiCharError};
use std::ascii::AsciiExt;
use std::borrow::{Borrow, BorrowMut};
use std::cmp::{Eq, PartialEq};
use std::convert::{AsRef, AsMut, From, Into};
use std::hash::{Hash, Hasher};
use std::ops::{Deref, DerefMut};
use std::{fmt, mem, ptr, slice, str};
use ::{PascalStr, PASCAL_STRING_BUF_SIZE};

/// An owned `PascalString`. This string type stores its data the stack. It is always 256 bytes long, with
/// the first byte storing the length.
///
/// This string type uses Ascii encoding.
pub struct PascalString {
    /// The length of this string.
    len: u8,
    /// The characters of this string, encoded as an ascii array.
    chars: [AsciiChar; PASCAL_STRING_BUF_SIZE]
}

impl PascalString {
    /// Creates a new, empty `PascalString`.
    #[inline]
    pub fn new() -> Self {
        Default::default()
    }

    /// Create a new `PascalString` using the contents of `bytes`.
    ///
    /// Returns an `Err` if `bytes` is longer than 255 characters, or it does not contain
    /// Ascii encoded characters.
    #[inline]
    pub fn from<B: AsRef<[u8]>>(bytes: B) -> Result<Self, PascalStringCreateError> {
        PascalString::_from(bytes.as_ref())
    }

    fn _from(bytes: &[u8]) -> Result<Self, PascalStringCreateError>  {
        let len = bytes.len();
        if len > PASCAL_STRING_BUF_SIZE {
            return Err(PascalStringCreateError::InputTooLong);
        }
        // Perform ascii check
        let ascii = try!(AsciiStr::from_ascii(bytes));

        let mut string = PascalString::new();
        string.len = len as u8;
        for i in 0..len {
            string[i] = ascii[i];
        }
        Ok(string)
    }

    /// Push an ascii convertible character onto this string.
    ///
    /// # Panics
    ///
    /// Panics if the string is full, of if the `character` is not a valid ascii character.
    #[inline]
    pub fn push<C: ToAsciiChar>(&mut self, character: C) {
        self.try_push(character).unwrap();
    }

    /// Attempt to push an ascii convertible character onto the end of this `PascalString`.
    ///
    /// Returns `Err(_)` if the character cannot be pushed because this `PascalString` is full, or if
    /// `character` is not a valid ascii character.
    #[inline]
    pub fn try_push<C: ToAsciiChar>(&mut self, character: C) -> Result<(), PascalStringAppendError> {
        self._try_push(try!(AsciiChar::from(character)))
    }

    fn _try_push(&mut self, ch: AsciiChar) -> Result<(), PascalStringAppendError> {
        if self.is_full() {
            return Err(PascalStringAppendError::AppendedStringTooLong)
        }
        self.len += 1;
        let idx = self.len - 1;
        self[idx] = ch;
        Ok(())
    }

    /// Append a given string slice onto the end of this `PascalString`.
    ///
    /// # Panics
    ///
    /// Panics if the string cannot be pushed to because this `PascalString` is full.
    #[inline]
    pub fn push_str<S: AsRef<str>>(&mut self, s: S) {
        self.try_push_str(s).unwrap();
    }

    /// Attempt to append a given string slice onto the end of this `PascalString`.
    ///
    /// Returns `Err(_)` if the string cannot be pushed because this `PascalString` is full.
    #[inline]
    pub fn try_push_str<S: AsRef<str>>(&mut self, s: S) -> Result<(), PascalStringAppendError> {
        self._try_push_str(s.as_ref())
    }

    fn _try_push_str(&mut self, s: &str) -> Result<(), PascalStringAppendError> {
        unimplemented!()
    }

    /// Removes the last character from the string buffer and returns it.
    ///
    /// Returns `None` if this `PascalString` is empty.
    pub fn pop(&mut self) -> Option<AsciiChar> {
        if self.is_empty() {
            return None;
        }
        let c = self.chars[self.len as usize];
        self.len -= 1;
        Some(c)
    }

    pub fn remove(&mut self, index: u8) -> AsciiChar {
        assert!(self.len < index);
        let len = self.len as usize;
        let index = index as isize;
        let c = self[len];
        // Shift everything to the right of the removed character to the left to cover up the hole
        // left.
        unsafe {
            let ptr = self.as_mut_ptr().offset(index);
            ptr::copy(ptr.offset(1), ptr, len - index.abs() as usize - 1);
        }
        self.len -= 1;
        c
    }

    #[inline]
    pub fn insert<C: ToAsciiChar>(&mut self, ch: C, index: u8) {
        self._insert(AsciiChar::from(ch).unwrap(), index)
    }

    fn _insert(&mut self, ch: AsciiChar, index: u8) {
        assert!(self.len < index);
        assert!(!self.is_full());
        let len = self.len as usize;
        let index = index as isize;
        // Shift everything to the right of `index` 1 place to the right to make room for the
        // new character.
        unsafe {
            let ptr = self.as_mut_ptr().offset(index);
            ptr::copy(ptr, ptr.offset(1), len - index.abs() as usize - 1);
        }
        self[len] = ch;
        self.len += 1;
    }

    /// Truncates this String, removing all contents.
    ///
    /// Does not zero the values of the string.
    #[inline]
    pub fn clear(&mut self) {
        self.len = 0;
    }

    /// Consumes this `PascalString`, and returns its inner state as a `[u8; 256]`, where the first byte
    /// is the length.
    ///
    /// Note that if the string has been truncated, bytes beyond the end of the string will not have been
    /// zeroed.
    #[inline]
    pub fn to_array(self) -> [u8; PASCAL_STRING_BUF_SIZE + 1] {
        self.into()
    }

    /// Get a character in the string, without checking if the index is within the bounds of `len()`.
    ///
    /// This method cannot cause memory unsafety because of the size of `index`. However, it can give access
    /// to stale characters if `index` is greater than `len()`, and `len() < 255`.
    #[inline]
    pub fn get_unchecked(&self, index: u8) -> AsciiChar {
        self.chars[index as usize]
    }
}

impl Default for PascalString {
    #[inline]
    fn default() -> Self {
        PascalString {
            len: 0,
            chars: [AsciiChar::Null; PASCAL_STRING_BUF_SIZE]
        }
    }
}

impl Clone for PascalString {
    fn clone(&self) -> Self {
        let mut clone = PascalString::default();
        clone.len = self.len;
        unsafe {
            ptr::copy_nonoverlapping(&self.chars, &mut clone.chars, PASCAL_STRING_BUF_SIZE);
        }
        clone
    }
}

impl Eq for PascalString { }

impl PartialEq for PascalString {
    fn eq(&self, other: &PascalString) -> bool {
        if self.len != other.len {
            return false;
        }
        self.chars.iter().zip(other.chars.iter()).all(|(c0, c1)| c0 == c1)
    }
}

impl Hash for PascalString {
    fn hash<H: Hasher>(&self, state: &mut H) {
        state.write_u8(self.len);
        state.write(self.as_ref());
    }
}

impl AsciiExt for PascalString {
    type Owned = Self;

    fn is_ascii(&self) -> bool {
        true
    }

    fn to_ascii_uppercase(&self) -> Self::Owned {
        let mut upper = self.clone();
        upper.make_ascii_uppercase();
        upper
    }

    fn to_ascii_lowercase(&self) -> Self::Owned {
        let mut lower = self.clone();
        lower.make_ascii_lowercase();
        lower
    }

    fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
        self.chars.iter().zip(other.chars.iter()).all(|(c0, c1)| c0.eq_ignore_ascii_case(&c1))
    }

    fn make_ascii_uppercase(&mut self) {
        for c in self.chars.iter_mut() {
            c.make_ascii_uppercase();
        }
    }

    fn make_ascii_lowercase(&mut self) {
        for c in self.chars.iter_mut() {
            c.make_ascii_lowercase();
        }
    }
}

impl fmt::Debug for PascalString {
    fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
        write!(fmtr, "{}", self)
    }
}

impl fmt::Display for PascalString {
    fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
        fmtr.pad(self.as_ref())
    }
}

impl Deref for PascalString {
    type Target = PascalStr;
    fn deref(&self) -> &Self::Target {
        let ascii_str: &[AsciiChar] = self.as_ref();
        unsafe {
            mem::transmute(ascii_str)
        }
    }
}

impl DerefMut for PascalString {
    fn deref_mut(&mut self) -> &mut Self::Target {
        let ascii_str: &mut [AsciiChar] = self.as_mut();
        unsafe {
            mem::transmute(ascii_str)
        }
    }
}

impl AsRef<PascalStr> for PascalString {
    fn as_ref(&self) -> &PascalStr {
        self.deref()
    }
}

impl AsRef<str> for PascalString {
    fn as_ref(&self) -> &str {
        let bytes: &[u8] = self.as_ref();
        str::from_utf8(bytes).unwrap()
    }
}

impl AsRef<[u8]> for PascalString {
    fn as_ref(&self) -> &[u8] {
        unsafe {
            slice::from_raw_parts(self.chars.as_ptr() as *const u8, self.len as usize)
        }
    }
}

impl AsRef<AsciiStr> for PascalString {
    fn as_ref(&self) -> &AsciiStr {
        let bytes: &[u8] = self.as_ref();
        AsciiStr::from_ascii(bytes).unwrap()
    }
}

impl AsRef<[AsciiChar]> for PascalString {
    fn as_ref(&self) -> &[AsciiChar] {
        unsafe {
            slice::from_raw_parts(self.chars.as_ptr(), self.len as usize)
        }
    }
}

impl AsMut<[AsciiChar]> for PascalString {
    fn as_mut(&mut self) -> &mut [AsciiChar] {
        unsafe {
            slice::from_raw_parts_mut(self.chars.as_mut_ptr(), self.len as usize)
        }
    }
}

impl Borrow<PascalStr> for PascalString {
    fn borrow(&self) -> &PascalStr {
        self.deref()
    }
}

impl BorrowMut<PascalStr> for PascalString {
    fn borrow_mut(&mut self) -> &mut PascalStr {
        self.deref_mut()
    }
}

impl Borrow<str> for PascalString {
    fn borrow(&self) -> &str {
        self.as_ref()
    }
}

impl Borrow<[u8]> for PascalString {
    fn borrow(&self) -> &[u8] {
        self.as_ref()
    }
}

impl Borrow<AsciiStr> for PascalString {
    fn borrow(&self) -> &AsciiStr {
        self.as_ref()
    }
}

impl Borrow<[AsciiChar]> for PascalString {
    fn borrow(&self) -> &[AsciiChar] {
        self.as_ref()
    }
}

impl BorrowMut<[AsciiChar]> for PascalString {
    fn borrow_mut(&mut self) -> &mut [AsciiChar] {
        self.as_mut()
    }
}

impl Into<[u8; PASCAL_STRING_BUF_SIZE + 1]> for PascalString {
    fn into(self) -> [u8; PASCAL_STRING_BUF_SIZE + 1] {
        let mut array = [0u8; PASCAL_STRING_BUF_SIZE + 1];
        array[0] = self.len;
        unsafe {
            let chars_ptr = &self.chars as *const _ as *const [u8; PASCAL_STRING_BUF_SIZE];
            let array_ptr = (&mut array as *mut _).offset(1) as *mut [u8; PASCAL_STRING_BUF_SIZE];
            ptr::copy_nonoverlapping(chars_ptr, array_ptr, PASCAL_STRING_BUF_SIZE);
        }
        array
    }
}

impl Into<String> for PascalString {
    fn into(self) -> String {
        String::from_utf8_lossy(self.as_ref()).into_owned()
    }
}

#[derive(Debug, PartialEq)]
pub enum PascalStringCreateError {
    InputTooLong,
    NotValidAscii(AsAsciiStrError)
}

impl From<AsAsciiStrError> for PascalStringCreateError {
    fn from(e: AsAsciiStrError) -> Self {
        PascalStringCreateError::NotValidAscii(e)
    }
}

#[derive(Debug, PartialEq)]
pub enum PascalStringAppendError {
    AppendedStringTooLong,
    InvalidCharAppended(ToAsciiCharError),
    InvalidStringAppended(AsAsciiStrError)
}

impl From<ToAsciiCharError> for PascalStringAppendError {
    fn from(e: ToAsciiCharError) -> Self {
        PascalStringAppendError::InvalidCharAppended(e)
    }
}

impl From<AsAsciiStrError> for PascalStringAppendError {
    fn from(e: AsAsciiStrError) -> Self {
        PascalStringAppendError::InvalidStringAppended(e)
    }
}<|endoftext|>


<commit_before>use std::time::{Duration, UNIX_EPOCH};

use assign::assign;
use maplit::btreemap;
use matches::assert_matches;
#[cfg(feature = "unstable-pre-spec")]
use ruma_events::{
    key::verification::VerificationMethod, room::message::KeyVerificationRequestEventContent,
};
use ruma_events::{
    room::{
        message::{
            AudioMessageEventContent, CustomEventContent, MessageEventContent, Relation,
            TextMessageEventContent,
        },
        relationships::InReplyTo,
    },
    MessageEvent, Unsigned,
};
#[cfg(feature = "unstable-pre-spec")]
use ruma_identifiers::DeviceIdBox;
use ruma_identifiers::{event_id, room_id, user_id};
use ruma_serde::Raw;
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};

#[test]
fn serialization() {
    let ev = MessageEvent {
        content: MessageEventContent::Audio(AudioMessageEventContent {
            body: "test".into(),
            info: None,
            url: Some("http://example.com/audio.mp3".into()),
            file: None,
        }),
        event_id: event_id!("$143273582443PhrSn:example.org"),
        origin_server_ts: UNIX_EPOCH + Duration::from_millis(10_000),
        room_id: room_id!("!testroomid:example.org"),
        sender: user_id!("@user:example.org"),
        unsigned: Unsigned::default(),
    };

    assert_eq!(
        to_json_value(ev).unwrap(),
        json!({
            "type": "m.room.message",
            "event_id": "$143273582443PhrSn:example.org",
            "origin_server_ts": 10_000,
            "room_id": "!testroomid:example.org",
            "sender": "@user:example.org",
            "content": {
                "body": "test",
                "msgtype": "m.audio",
                "url": "http://example.com/audio.mp3",
            }
        })
    );
}

#[test]
fn content_serialization() {
    let message_event_content = MessageEventContent::Audio(AudioMessageEventContent {
        body: "test".into(),
        info: None,
        url: Some("http://example.com/audio.mp3".into()),
        file: None,
    });

    assert_eq!(
        to_json_value(&message_event_content).unwrap(),
        json!({
            "body": "test",
            "msgtype": "m.audio",
            "url": "http://example.com/audio.mp3"
        })
    );
}

#[test]
fn custom_content_serialization() {
    let json_data = btreemap! {
        "custom_field".into() => json!("baba"),
        "another_one".into() => json!("abab"),
    };
    let custom_event_content = MessageEventContent::_Custom(CustomEventContent {
        msgtype: "my_custom_msgtype".into(),
        data: json_data,
    });

    assert_eq!(
        to_json_value(&custom_event_content).unwrap(),
        json!({
            "msgtype": "my_custom_msgtype",
            "custom_field": "baba",
            "another_one": "abab",
        })
    );
}

#[test]
fn custom_content_deserialization() {
    let json_data = json!({
        "msgtype": "my_custom_msgtype",
        "custom_field": "baba",
        "another_one": "abab",
    });

    let expected_json_data = btreemap! {
        "custom_field".into() => json!("baba"),
        "another_one".into() => json!("abab"),
    };

    assert_matches!(
        from_json_value::<Raw<MessageEventContent>>(json_data)
            .unwrap()
            .deserialize()
            .unwrap(),
        MessageEventContent::_Custom(CustomEventContent {
            msgtype,
            data
        }) if msgtype == "my_custom_msgtype"
            && data == expected_json_data
    );
}

#[test]
fn formatted_body_serialization() {
    let message_event_content = MessageEventContent::Text(TextMessageEventContent::html(
        "Hello, World!",
        "Hello, World!",
    ));

    assert_eq!(
        to_json_value(&message_event_content).unwrap(),
        json!({
            "body": "Hello, World!",
            "msgtype": "m.text",
            "format": "org.matrix.custom.html",
            "formatted_body": "Hello, World!",
        })
    );
}

#[test]
fn plain_text_content_serialization() {
    let message_event_content = MessageEventContent::Text(TextMessageEventContent::plain(
        "> <@test:example.com> test\n\ntest reply",
    ));

    assert_eq!(
        to_json_value(&message_event_content).unwrap(),
        json!({
            "body": "> <@test:example.com> test\n\ntest reply",
            "msgtype": "m.text"
        })
    );
}

#[test]
fn relates_to_content_serialization() {
    let message_event_content = MessageEventContent::Text(
        assign!(TextMessageEventContent::plain("> <@test:example.com> test\n\ntest reply"), {
            relates_to: Some(Relation::Reply {
                in_reply_to: InReplyTo { event_id: event_id!("$15827405538098VGFWH:example.com") },
            }),
        }),
    );

    let json_data = json!({
        "body": "> <@test:example.com> test\n\ntest reply",
        "msgtype": "m.text",
        "m.relates_to": {
            "m.in_reply_to": {
                "event_id": "$15827405538098VGFWH:example.com"
            }
        }
    });

    assert_eq!(to_json_value(&message_event_content).unwrap(), json_data);
}

#[test]
#[cfg(not(feature = "unstable-pre-spec"))]
fn edit_deserialization_061() {
    let json_data = json!({
        "body": "s/foo/bar",
        "msgtype": "m.text",
        "m.relates_to": {
            "rel_type": "m.replace",
            "event_id": event_id!("$1598361704261elfgc:localhost"),
        },
        "m.new_content": {
            "body": "bar",
        },
    });

    assert_matches!(
        from_json_value::<MessageEventContent>(json_data).unwrap(),
        MessageEventContent::Text(TextMessageEventContent {
            body,
            formatted: None,
            relates_to: Some(Relation::Custom(_)),
            ..
        }) if body == "s/foo/bar"
    );
}

#[test]
#[cfg(feature = "unstable-pre-spec")]
fn edit_deserialization_future() {
    use ruma_events::room::relationships::Replacement;

    let ev_id = event_id!("$1598361704261elfgc:localhost");
    let json_data = json!({
        "body": "s/foo/bar",
        "msgtype": "m.text",
        "m.relates_to": {
            "rel_type": "m.replace",
            "event_id": ev_id,
        },
        "m.new_content": {
            "body": "bar",
            "msgtype": "m.text",
        },
    });

    assert_matches!(
        from_json_value::<MessageEventContent>(json_data).unwrap(),
        MessageEventContent::Text(TextMessageEventContent {
            body,
            formatted: None,
            relates_to: Some(Relation::Replacement(Replacement { event_id })),
            new_content: Some(new_content),
            ..
        }) if body == "s/foo/bar"
            && event_id == ev_id
            && matches!(
                &*new_content,
                MessageEventContent::Text(TextMessageEventContent {
                    body,
                    formatted: None,
                    relates_to: None,
                    new_content: None,
                    ..
                }) if body == "bar"
            )
    );
}

#[test]
#[cfg(feature = "unstable-pre-spec")]
fn verification_request_deserialization() {
    let user_id = user_id!("@example2:localhost");
    let device_id: DeviceIdBox = "XOWLHHFSWM".into();

    let json_data = json!({
        "body": "@example:localhost is requesting to verify your key, ...",
        "msgtype": "m.key.verification.request",
        "to": user_id,
        "from_device": device_id,
        "methods": [
            "m.sas.v1",
            "m.qr_code.show.v1",
            "m.reciprocate.v1"
        ]
    });

    assert_matches!(
        from_json_value::<MessageEventContent>(json_data).unwrap(),
        MessageEventContent::VerificationRequest(KeyVerificationRequestEventContent {
            body,
            to,
            from_device,
            methods,
        }) if body == "@example:localhost is requesting to verify your key, ..."
            && to == user_id
            && from_device == device_id
            && methods.contains(&VerificationMethod::MSasV1)
    );
}

#[test]
#[cfg(feature = "unstable-pre-spec")]
fn verification_request_serialization() {
    let user_id = user_id!("@example2:localhost");
    let device_id: DeviceIdBox = "XOWLHHFSWM".into();
    let body = "@example:localhost is requesting to verify your key, ...".to_string();

    let methods = vec![
        VerificationMethod::MSasV1,
        VerificationMethod::_Custom("m.qr_code.show.v1".to_string()),
        VerificationMethod::_Custom("m.reciprocate.v1".to_string()),
    ];

    let json_data = json!({
        "body": body,
        "msgtype": "m.key.verification.request",
        "to": user_id,
        "from_device": device_id,
        "methods": methods
    });

    let content = MessageEventContent::VerificationRequest(KeyVerificationRequestEventContent {
        to: user_id,
        from_device: device_id,
        body,
        methods,
    });

    assert_eq!(to_json_value(&content).unwrap(), json_data,);
}

#[test]
fn content_deserialization() {
    let json_data = json!({
        "body": "test",
        "msgtype": "m.audio",
        "url": "http://example.com/audio.mp3"
    });

    assert_matches!(
        from_json_value::<Raw<MessageEventContent>>(json_data)
            .unwrap()
            .deserialize()
            .unwrap(),
        MessageEventContent::Audio(AudioMessageEventContent {
            body,
            info: None,
            url: Some(url),
            file: None,
        }) if body == "test" && url == "http://example.com/audio.mp3"
    );
}

#[test]
fn content_deserialization_failure() {
    let json_data = json!({
        "body": "test","msgtype": "m.location",
        "url": "http://example.com/audio.mp3"
    });
    assert!(from_json_value::<Raw<MessageEventContent>>(json_data).unwrap().deserialize().is_err());
}
<commit_msg>events: Use more specific MessageEvent type in room_message test<commit_after>use std::time::{Duration, UNIX_EPOCH};

use assign::assign;
use maplit::btreemap;
use matches::assert_matches;
#[cfg(feature = "unstable-pre-spec")]
use ruma_events::{
    key::verification::VerificationMethod, room::message::KeyVerificationRequestEventContent,
};
use ruma_events::{
    room::{
        message::{
            AudioMessageEventContent, CustomEventContent, MessageEvent, MessageEventContent,
            Relation, TextMessageEventContent,
        },
        relationships::InReplyTo,
    },
    Unsigned,
};
#[cfg(feature = "unstable-pre-spec")]
use ruma_identifiers::DeviceIdBox;
use ruma_identifiers::{event_id, room_id, user_id};
use ruma_serde::Raw;
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};

#[test]
fn serialization() {
    let ev = MessageEvent {
        content: MessageEventContent::Audio(AudioMessageEventContent {
            body: "test".into(),
            info: None,
            url: Some("http://example.com/audio.mp3".into()),
            file: None,
        }),
        event_id: event_id!("$143273582443PhrSn:example.org"),
        origin_server_ts: UNIX_EPOCH + Duration::from_millis(10_000),
        room_id: room_id!("!testroomid:example.org"),
        sender: user_id!("@user:example.org"),
        unsigned: Unsigned::default(),
    };

    assert_eq!(
        to_json_value(ev).unwrap(),
        json!({
            "type": "m.room.message",
            "event_id": "$143273582443PhrSn:example.org",
            "origin_server_ts": 10_000,
            "room_id": "!testroomid:example.org",
            "sender": "@user:example.org",
            "content": {
                "body": "test",
                "msgtype": "m.audio",
                "url": "http://example.com/audio.mp3",
            }
        })
    );
}

#[test]
fn content_serialization() {
    let message_event_content = MessageEventContent::Audio(AudioMessageEventContent {
        body: "test".into(),
        info: None,
        url: Some("http://example.com/audio.mp3".into()),
        file: None,
    });

    assert_eq!(
        to_json_value(&message_event_content).unwrap(),
        json!({
            "body": "test",
            "msgtype": "m.audio",
            "url": "http://example.com/audio.mp3"
        })
    );
}

#[test]
fn custom_content_serialization() {
    let json_data = btreemap! {
        "custom_field".into() => json!("baba"),
        "another_one".into() => json!("abab"),
    };
    let custom_event_content = MessageEventContent::_Custom(CustomEventContent {
        msgtype: "my_custom_msgtype".into(),
        data: json_data,
    });

    assert_eq!(
        to_json_value(&custom_event_content).unwrap(),
        json!({
            "msgtype": "my_custom_msgtype",
            "custom_field": "baba",
            "another_one": "abab",
        })
    );
}

#[test]
fn custom_content_deserialization() {
    let json_data = json!({
        "msgtype": "my_custom_msgtype",
        "custom_field": "baba",
        "another_one": "abab",
    });

    let expected_json_data = btreemap! {
        "custom_field".into() => json!("baba"),
        "another_one".into() => json!("abab"),
    };

    assert_matches!(
        from_json_value::<Raw<MessageEventContent>>(json_data)
            .unwrap()
            .deserialize()
            .unwrap(),
        MessageEventContent::_Custom(CustomEventContent {
            msgtype,
            data
        }) if msgtype == "my_custom_msgtype"
            && data == expected_json_data
    );
}

#[test]
fn formatted_body_serialization() {
    let message_event_content = MessageEventContent::Text(TextMessageEventContent::html(
        "Hello, World!",
        "Hello, World!",
    ));

    assert_eq!(
        to_json_value(&message_event_content).unwrap(),
        json!({
            "body": "Hello, World!",
            "msgtype": "m.text",
            "format": "org.matrix.custom.html",
            "formatted_body": "Hello, World!",
        })
    );
}

#[test]
fn plain_text_content_serialization() {
    let message_event_content = MessageEventContent::Text(TextMessageEventContent::plain(
        "> <@test:example.com> test\n\ntest reply",
    ));

    assert_eq!(
        to_json_value(&message_event_content).unwrap(),
        json!({
            "body": "> <@test:example.com> test\n\ntest reply",
            "msgtype": "m.text"
        })
    );
}

#[test]
fn relates_to_content_serialization() {
    let message_event_content = MessageEventContent::Text(
        assign!(TextMessageEventContent::plain("> <@test:example.com> test\n\ntest reply"), {
            relates_to: Some(Relation::Reply {
                in_reply_to: InReplyTo { event_id: event_id!("$15827405538098VGFWH:example.com") },
            }),
        }),
    );

    let json_data = json!({
        "body": "> <@test:example.com> test\n\ntest reply",
        "msgtype": "m.text",
        "m.relates_to": {
            "m.in_reply_to": {
                "event_id": "$15827405538098VGFWH:example.com"
            }
        }
    });

    assert_eq!(to_json_value(&message_event_content).unwrap(), json_data);
}

#[test]
#[cfg(not(feature = "unstable-pre-spec"))]
fn edit_deserialization_061() {
    let json_data = json!({
        "body": "s/foo/bar",
        "msgtype": "m.text",
        "m.relates_to": {
            "rel_type": "m.replace",
            "event_id": event_id!("$1598361704261elfgc:localhost"),
        },
        "m.new_content": {
            "body": "bar",
        },
    });

    assert_matches!(
        from_json_value::<MessageEventContent>(json_data).unwrap(),
        MessageEventContent::Text(TextMessageEventContent {
            body,
            formatted: None,
            relates_to: Some(Relation::Custom(_)),
            ..
        }) if body == "s/foo/bar"
    );
}

#[test]
#[cfg(feature = "unstable-pre-spec")]
fn edit_deserialization_future() {
    use ruma_events::room::relationships::Replacement;

    let ev_id = event_id!("$1598361704261elfgc:localhost");
    let json_data = json!({
        "body": "s/foo/bar",
        "msgtype": "m.text",
        "m.relates_to": {
            "rel_type": "m.replace",
            "event_id": ev_id,
        },
        "m.new_content": {
            "body": "bar",
            "msgtype": "m.text",
        },
    });

    assert_matches!(
        from_json_value::<MessageEventContent>(json_data).unwrap(),
        MessageEventContent::Text(TextMessageEventContent {
            body,
            formatted: None,
            relates_to: Some(Relation::Replacement(Replacement { event_id })),
            new_content: Some(new_content),
            ..
        }) if body == "s/foo/bar"
            && event_id == ev_id
            && matches!(
                &*new_content,
                MessageEventContent::Text(TextMessageEventContent {
                    body,
                    formatted: None,
                    relates_to: None,
                    new_content: None,
                    ..
                }) if body == "bar"
            )
    );
}

#[test]
#[cfg(feature = "unstable-pre-spec")]
fn verification_request_deserialization() {
    let user_id = user_id!("@example2:localhost");
    let device_id: DeviceIdBox = "XOWLHHFSWM".into();

    let json_data = json!({
        "body": "@example:localhost is requesting to verify your key, ...",
        "msgtype": "m.key.verification.request",
        "to": user_id,
        "from_device": device_id,
        "methods": [
            "m.sas.v1",
            "m.qr_code.show.v1",
            "m.reciprocate.v1"
        ]
    });

    assert_matches!(
        from_json_value::<MessageEventContent>(json_data).unwrap(),
        MessageEventContent::VerificationRequest(KeyVerificationRequestEventContent {
            body,
            to,
            from_device,
            methods,
        }) if body == "@example:localhost is requesting to verify your key, ..."
            && to == user_id
            && from_device == device_id
            && methods.contains(&VerificationMethod::MSasV1)
    );
}

#[test]
#[cfg(feature = "unstable-pre-spec")]
fn verification_request_serialization() {
    let user_id = user_id!("@example2:localhost");
    let device_id: DeviceIdBox = "XOWLHHFSWM".into();
    let body = "@example:localhost is requesting to verify your key, ...".to_string();

    let methods = vec![
        VerificationMethod::MSasV1,
        VerificationMethod::_Custom("m.qr_code.show.v1".to_string()),
        VerificationMethod::_Custom("m.reciprocate.v1".to_string()),
    ];

    let json_data = json!({
        "body": body,
        "msgtype": "m.key.verification.request",
        "to": user_id,
        "from_device": device_id,
        "methods": methods
    });

    let content = MessageEventContent::VerificationRequest(KeyVerificationRequestEventContent {
        to: user_id,
        from_device: device_id,
        body,
        methods,
    });

    assert_eq!(to_json_value(&content).unwrap(), json_data,);
}

#[test]
fn content_deserialization() {
    let json_data = json!({
        "body": "test",
        "msgtype": "m.audio",
        "url": "http://example.com/audio.mp3"
    });

    assert_matches!(
        from_json_value::<Raw<MessageEventContent>>(json_data)
            .unwrap()
            .deserialize()
            .unwrap(),
        MessageEventContent::Audio(AudioMessageEventContent {
            body,
            info: None,
            url: Some(url),
            file: None,
        }) if body == "test" && url == "http://example.com/audio.mp3"
    );
}

#[test]
fn content_deserialization_failure() {
    let json_data = json!({
        "body": "test","msgtype": "m.location",
        "url": "http://example.com/audio.mp3"
    });
    assert!(from_json_value::<Raw<MessageEventContent>>(json_data).unwrap().deserialize().is_err());
}
<|endoftext|>


<commit_before>#[macro_use]
pub mod compiler_test;

compiler_tests! {
    child_references_parent: "a = 1; { a + 2 }" => value(3),
    child_references_parent_too_early_error: "{ a + 2 }; a = 1" => error(NoSuchField@2),
    // child_references_parent_container_error: "a = { a + 1 }; a" => error(NoSuchField@6),
    child_references_parent_container_circular_error: "a = 1; a = { a + 1 }; a" => error(CircularDependency@13),
    child_defines_value_early: "x = { a = 1; a+2 }; a = x + 3; a" => value(6),
    child_updates_parent: "a = 1; { a += 2 }; a + 3" => value(6),
    child_overwrites_parent: "a = 1; { a = 2 }; a + 3" => value(5),
    child_overwrites_and_references_parent: "a = 1; { a = 2; a + 3 } + a" => value(7),
    parent_references_child_error: "{ a = 0; a }; a" => error(NoSuchField@14),
}
<commit_msg>Add tests for lazy eval / circular error<commit_after>#[macro_use]
pub mod compiler_test;

compiler_tests! {
    child_references_parent: "a = 1; { a + 2 }" => value(3),
    child_references_parent_too_early_error: "{ a + 2 }; a = 1" => error(NoSuchField@2),
    child_references_parent_circular_error: "a = { a + 1 }; a" => error(CircularDependency@6),
    child_references_parent_lazy_circular_error: "a = 1; a = { a + 1 }; a" => error(CircularDependency@13),
    child_defines_value_early: "x = { a = 1; a+2 }; a = x + 3; a" => value(6),
    child_updates_parent: "a = 1; { a += 2 }; a + 3" => value(6),
    child_overwrites_parent: "a = 1; { a = 2 }; a + 3" => value(5),
    child_overwrites_and_references_parent: "a = 1; { a = 2; a + 3 } + a" => value(7),
    child_redefines_parent: "a = 1; { :a = 2 }; a" => value(1),
    parent_references_child_error: "{ a = 0; a }; a" => error(NoSuchField@14),
    parent_defines_value_late: "x = { a+2 }; a = 3; x" => error(NoSuchField@6),
    child_evaluated_lazily: "a = 1; x = { a }; a = 2; x" => value(2),
}
<|endoftext|>