<commit_before>extern crate "parser-combinators" as parser_combinators;

use self::parser_combinators::{between, spaces, parser, many, many1, digit, optional, hex_digit, not_followed_by, satisfy, Parser, ParserExt, ParseResult};
use self::parser_combinators::primitives::{State, Stream};
use super::ast::*;
use super::ast::ExprNode::*;
use std::str::FromStr;
use std::num::FromStrRadix;

pub fn number<I>(input: State<I>) -> ParseResult<NumNode, I>
    where I: Stream<Item=char> {
        let signed_int = optional(satisfy(|c| c == '-'))
            .and(
                (satisfy(|c| c == '0')
                    .and(satisfy(|c| c == 'x' || c == 'X')))
                    .with(many1::<Vec<_>, _>(hex_digit()))
                    .map(|x| {
                        isize::from_str_radix(
                            x.iter()
                             .fold(
                                String::new(),
                                |mut s: String, i| { s.push(*i); s })
                             .as_slice(),
                        16).unwrap()
                    })
                .or( many1::<Vec<_>, _>(digit()).map(|x|
                    isize::from_str(x.iter().fold(
                        String::new(), |mut s: String, i| { s.push(*i); s })
                        .as_slice()
                    ).unwrap()
                    )
                )
                )
            .map(|x| {
                if let Some(sign) = x.0 {
                    let mut s = String::new();
                    s.push(sign);
                    s.push('1');
                    x.1 * isize::from_str(s.as_slice()).unwrap()
                } else {
                    x.1
                }
                });

        signed_int
            .map(|x: isize| NumNode::IntConst(IntNode{value: x}))
            .parse_state(input)
}

pub fn name<I>(input: State<I>) -> ParseResult<NameNode, I>
    where I: Stream<Item=char> {
         let initial = satisfy(|c|
            c.is_alphabetic()
                // R6RS 'special initial' characters
                || c == '!' || c == '$' || c == '%' || c == ':' || c == '^'
                || c == '<' || c == '>' || c == '_' || c == '~' || c == '\\'
                || c == '?');
        let subsequent = satisfy(|c|
            c.is_alphanumeric()
                // R6RS 'special initial' characters
                || c == '!' || c == '$' || c == '%' || c == ':' || c == '^'
                || c == '<' || c == '>' || c == '_' || c == '~' || c == '\\'
                || c == '?'
                // R6RS 'special subsequent' characters
                || c == '+' || c == '-' || c == '.' || c == '@');
        initial
            .and(many::<Vec<_>, _>(subsequent).map(|it|
                it.iter().fold(
                    String::new(),
                    |mut s: String, i| {
                        s.push(*i);
                        s
                    }
                    )
                ))
            .parse_state(input)
            .map(|x| {
                let mut s = String::new();
                s.push((x.0).0);
                s.push_str(&(x.0).1);
                (NameNode{ name: s}, x.1)
            })
}

pub fn expr<I>(input: State<I>) -> ParseResult<ExprNode, I>
    where I: Stream<Item=char> {
        let spaces = spaces();
        let sexpr = between(
            satisfy(|c| c == '('),
            satisfy(|c| c == ')'),
            parser(name)
                .and(many(parser(expr)))
                .map(|x| {
                    SExpr(SExprNode {
                        operator: x.0,
                        operands: x.1
                    })
                })
            );
        spaces.clone().with(
            sexpr
                .or(parser(name).map(Name))
            ).parse_state(input)
}

#[cfg(test)]
mod tests {
    use ::ast::*;
    use ::ast::ExprNode::*;
    use super::{expr, number};
    use super::parser_combinators::{Parser,parser};

    #[test]
    fn test_basic_ident() {
        assert_eq!(
            parser(expr).parse("ident").unwrap(),
            (Name(NameNode { name: "ident".to_string() }), "")
            );
    }

    #[test]
    fn test_basic_sexpr() {
        assert_eq!(
            parser(expr).parse("(ident arg1 arg2)").unwrap(),
            (SExpr(SExprNode {
                operator: NameNode { name: "ident".to_string() },
                operands: vec![
                    Name(NameNode { name: "arg1".to_string() }),
                    Name(NameNode { name: "arg2".to_string() })
                ]
            }), "")
            );
    }

    #[test]
    fn test_parse_sint_pos() {
        assert_eq!(
            parser(number).parse("1234"),
            Ok((NumNode::IntConst(IntNode { value: 1234isize }), ""))
            );
    }

    #[test]
    fn test_parse_sint_neg() {
        assert_eq!(
            parser(number).parse("-1234"),
            Ok((NumNode::IntConst(IntNode { value: -1234isize }), ""))
            );
    }

    #[test]
    fn test_parse_sint_hex_upper() {
        assert_eq!(
            parser(number).parse("0x0ff"),
            Ok((NumNode::IntConst(IntNode { value: 0x0ffisize }), ""))
            );
    }
    #[test]
    fn test_parse_sint_hex_neg() {
        assert_eq!(
            parser(number).parse("0X0FF"),
            Ok((NumNode::IntConst(IntNode { value: 0x0ffisize }), ""))
            );
    }

    #[test]
    fn test_parse_uint() {
        assert_eq!(
            parser(number).parse("1234u"),
            Ok((NumNode::UIntConst(UIntNode { value: 1234usize }), ""))
            );
    }

    #[test]
    fn test_parse_float() {
        assert_eq!(
            parser(number).parse("1.0").unwrap(),
            (NumNode::FloatConst(FloatNode { value: 1.0f64 }), "")
            );
        assert_eq!(
            parser(number).parse("1f").unwrap(),
            (NumNode::FloatConst(FloatNode { value: 1.0f64 }), "")
            );
        assert_eq!(
            parser(number).parse("22.2222").unwrap(),
            (NumNode::FloatConst(FloatNode { value: 22.2222f64 }), "")
            );
        assert_eq!(
            parser(number).parse("22.2222f").unwrap(),
            (NumNode::FloatConst(FloatNode { value: 22.2222f64 }), "")
            );
    }

}
<commit_msg>Split parser for SInts into its' own function<commit_after>extern crate "parser-combinators" as parser_combinators;

use self::parser_combinators::{between, spaces, parser, many, many1, digit, optional, hex_digit, not_followed_by, satisfy, Parser, ParserExt, ParseResult};
use self::parser_combinators::primitives::{State, Stream};
use super::ast::*;
use super::ast::ExprNode::*;
use std::str::FromStr;
use std::num::FromStrRadix;

fn sint<I>(input: State<I>) -> ParseResult<NumNode, I>
    where I: Stream<Item=char> {
        optional(satisfy(|c| c == '-'))
            .and(
                (satisfy(|c| c == '0')
                    .and(satisfy(|c| c == 'x' || c == 'X')))
                    .with(many1::<Vec<_>, _>(hex_digit()))
                    .map(|x| {
                        isize::from_str_radix(
                            x.iter()
                             .fold(
                                String::new(),
                                |mut s: String, i| { s.push(*i); s })
                             .as_slice(),
                        16).unwrap()
                    })
                .or( many1::<Vec<_>, _>(digit()).map(|x|
                    isize::from_str(x.iter().fold(
                        String::new(), |mut s: String, i| { s.push(*i); s })
                        .as_slice()
                    ).unwrap()
                    )
                )
                )
            .map(|x| {
                if let Some(sign) = x.0 {
                    let mut s = String::new();
                    s.push(sign);
                    s.push('1');
                    x.1 * isize::from_str(s.as_slice()).unwrap()
                } else {
                    x.1
                }
                })
            .map(|x: isize| NumNode::IntConst(IntNode{value: x}))
            .parse_state(input)
}

pub fn number<I>(input: State<I>) -> ParseResult<NumNode, I>
    where I: Stream<Item=char> {
        parser(sint).parse_state(input)
}

pub fn name<I>(input: State<I>) -> ParseResult<NameNode, I>
    where I: Stream<Item=char> {
         let initial = satisfy(|c|
            c.is_alphabetic()
                // R6RS 'special initial' characters
                || c == '!' || c == '$' || c == '%' || c == ':' || c == '^'
                || c == '<' || c == '>' || c == '_' || c == '~' || c == '\\'
                || c == '?');
        let subsequent = satisfy(|c|
            c.is_alphanumeric()
                // R6RS 'special initial' characters
                || c == '!' || c == '$' || c == '%' || c == ':' || c == '^'
                || c == '<' || c == '>' || c == '_' || c == '~' || c == '\\'
                || c == '?'
                // R6RS 'special subsequent' characters
                || c == '+' || c == '-' || c == '.' || c == '@');
        initial
            .and(many::<Vec<_>, _>(subsequent).map(|it|
                it.iter().fold(
                    String::new(),
                    |mut s: String, i| {
                        s.push(*i);
                        s
                    }
                    )
                ))
            .parse_state(input)
            .map(|x| {
                let mut s = String::new();
                s.push((x.0).0);
                s.push_str(&(x.0).1);
                (NameNode{ name: s}, x.1)
            })
}

pub fn expr<I>(input: State<I>) -> ParseResult<ExprNode, I>
    where I: Stream<Item=char> {
        let spaces = spaces();
        let sexpr = between(
            satisfy(|c| c == '('),
            satisfy(|c| c == ')'),
            parser(name)
                .and(many(parser(expr)))
                .map(|x| {
                    SExpr(SExprNode {
                        operator: x.0,
                        operands: x.1
                    })
                })
            );
        spaces.clone().with(
            sexpr
                .or(parser(name).map(Name))
            ).parse_state(input)
}

#[cfg(test)]
mod tests {
    use ::ast::*;
    use ::ast::ExprNode::*;
    use super::*;
    use super::parser_combinators::{Parser,parser};

    #[test]
    fn test_basic_ident() {
        assert_eq!(
            parser(expr).parse("ident").unwrap(),
            (Name(NameNode { name: "ident".to_string() }), "")
            );
    }

    #[test]
    fn test_basic_sexpr() {
        assert_eq!(
            parser(expr).parse("(ident arg1 arg2)").unwrap(),
            (SExpr(SExprNode {
                operator: NameNode { name: "ident".to_string() },
                operands: vec![
                    Name(NameNode { name: "arg1".to_string() }),
                    Name(NameNode { name: "arg2".to_string() })
                ]
            }), "")
            );
    }

    #[test]
    fn test_parse_sint_pos() {
        assert_eq!(
            parser(number).parse("1234"),
            Ok((NumNode::IntConst(IntNode { value: 1234isize }), ""))
            );
    }

    #[test]
    fn test_parse_sint_neg() {
        assert_eq!(
            parser(number).parse("-1234"),
            Ok((NumNode::IntConst(IntNode { value: -1234isize }), ""))
            );
    }

    #[test]
    fn test_parse_sint_hex() {
        assert_eq!(
            parser(number).parse("0x0ff"),
            Ok((NumNode::IntConst(IntNode { value: 0x0ffisize }), ""))
            );
        assert_eq!(
            parser(number).parse("0X0FF"),
            Ok((NumNode::IntConst(IntNode { value: 0x0ffisize }), ""))
            );
    }
    /*
    #[test]
    fn test_parse_sint_bin_upper() {
        assert_eq!(
            parser(number).parse("0B01"),
            Ok((NumNode::IntConst(IntNode { value: 0b01isize }), ""))
            );
        assert_eq!(
            parser(number).parse("0b01"),
            Ok((NumNode::IntConst(IntNode { value: 0b01isize }), ""))
            );
    }*/

    #[test]
    fn test_parse_uint() {
        assert_eq!(
            parser(number).parse("1234u"),
            Ok((NumNode::UIntConst(UIntNode { value: 1234usize }), ""))
            );
    }

    #[test]
    fn test_parse_float() {
        assert_eq!(
            parser(number).parse("1.0").unwrap(),
            (NumNode::FloatConst(FloatNode { value: 1.0f64 }), "")
            );
        assert_eq!(
            parser(number).parse("1f").unwrap(),
            (NumNode::FloatConst(FloatNode { value: 1.0f64 }), "")
            );
        assert_eq!(
            parser(number).parse("22.2222").unwrap(),
            (NumNode::FloatConst(FloatNode { value: 22.2222f64 }), "")
            );
        assert_eq!(
            parser(number).parse("22.2222f").unwrap(),
            (NumNode::FloatConst(FloatNode { value: 22.2222f64 }), "")
            );
    }

}
<|endoftext|>


<commit_before>
// make sure to add test calls to tests() func
#![forbid(dead_code)]

extern crate tempdir;

use cpp_parser;
use cpp_data::*;
use utils::PathBufPushTweak;
use std::fs;
use std::fs::File;
use std::io::Write;
use cpp_method::*;
use cpp_type::*;

fn run_parser(code: &'static str) -> CppData {
  assert!(code.ends_with("\n"));
  let dir = tempdir::TempDir::new("test_cpp_parser_run").unwrap();
  let include_dir = dir.path().with_added("include");
  fs::create_dir(&include_dir).unwrap();
  let include_name = "myfakelib.h";
  let include_file_path = include_dir.with_added(&include_name);
  {
    let mut include_file = File::create(&include_file_path).unwrap();
    include_file.write(code.as_bytes()).unwrap();
  }
  let mut result = cpp_parser::run(cpp_parser::CppParserConfig {
    include_dirs: vec![include_dir],
    header_name: include_name.to_string(),
    tmp_cpp_path: dir.path().with_added("1.cpp"),
    name_blacklist: Vec::new(),
  });
  for method in &mut result.methods {
    if let Some(ref mut origin_location) = method.origin_location {
      assert_eq!(origin_location.include_file_path,
                 include_file_path.display().to_string());
      assert!(origin_location.line > 0);
    } else {
      panic!("no origin_location in pare result");
    }
    method.origin_location = None;
  }
  result
}

fn simple_func() {
  let data = run_parser("int func1(int x);\n");
  assert!(data.template_instantiations.is_empty());
  assert!(data.types.is_empty());
  assert!(data.methods.len() == 1);
  assert_eq!(data.methods[0],
             CppMethod {
               name: "func1".to_string(),
               class_membership: None,
               operator: None,
               return_type: Some(CppType {
                 indirection: CppTypeIndirection::None,
                 is_const: false,
                 base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
               }),
               arguments: vec![CppFunctionArgument {
                                 name: "x".to_string(),
                                 argument_type: CppType {
                                   indirection: CppTypeIndirection::None,
                                   is_const: false,
                                   base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
                                 },
                                 has_default_value: false,
                               }],
               allows_variadic_arguments: false,
               include_file: "myfakelib.h".to_string(),
               origin_location: None,
               template_arguments: None,
             });
}

fn simple_func_with_default_value() {
  let data = run_parser("bool func1(int x = 42);\n");
  assert!(data.template_instantiations.is_empty());
  assert!(data.types.is_empty());
  assert!(data.methods.len() == 1);
  assert_eq!(data.methods[0],
             CppMethod {
               name: "func1".to_string(),
               class_membership: None,
               operator: None,
               return_type: Some(CppType {
                 indirection: CppTypeIndirection::None,
                 is_const: false,
                 base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Bool),
               }),
               arguments: vec![CppFunctionArgument {
                                 name: "x".to_string(),
                                 argument_type: CppType {
                                   indirection: CppTypeIndirection::None,
                                   is_const: false,
                                   base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
                                 },
                                 has_default_value: true,
                               }],
               allows_variadic_arguments: false,
               include_file: "myfakelib.h".to_string(),
               origin_location: None,
               template_arguments: None,
             });
}

fn functions_with_class_arg() {
  let data = run_parser("class Magic { public: int a, b; };
  bool func1(Magic x);
  bool func1(Magic* x);
  bool func2(const Magic&);
  \n");
  assert!(data.template_instantiations.is_empty());
  assert_eq!(data.types.len(), 1);
  assert_eq!(data.types[0].name, "Magic");
  match data.types[0].kind {
    CppTypeKind::Class { ref size, ref bases, ref fields, ref template_arguments } => {
      assert!(size.is_some());
      assert!(template_arguments.is_none());
      assert!(bases.is_empty());
      assert_eq!(fields,
                 &vec![CppClassField {
                         name: "a".to_string(),
                         field_type: CppType {
                           indirection: CppTypeIndirection::None,
                           is_const: false,
                           base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
                         },
                         visibility: CppVisibility::Public,
                       },
                       CppClassField {
                         name: "b".to_string(),
                         field_type: CppType {
                           indirection: CppTypeIndirection::None,
                           is_const: false,
                           base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
                         },
                         visibility: CppVisibility::Public,
                       }]);

    }
    _ => panic!("invalid type kind"),
  }
  assert!(data.methods.len() == 3);
  assert_eq!(data.methods[0],
             CppMethod {
               name: "func1".to_string(),
               class_membership: None,
               operator: None,
               return_type: Some(CppType {
                 indirection: CppTypeIndirection::None,
                 is_const: false,
                 base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Bool),
               }),
               arguments: vec![CppFunctionArgument {
                                 name: "x".to_string(),
                                 argument_type: CppType {
                                   indirection: CppTypeIndirection::None,
                                   is_const: false,
                                   base: CppTypeBase::Class {
                                     name: "Magic".to_string(),
                                     template_arguments: None,
                                   },
                                 },
                                 has_default_value: false,
                               }],
               allows_variadic_arguments: false,
               include_file: "myfakelib.h".to_string(),
               origin_location: None,
               template_arguments: None,
             });
  assert_eq!(data.methods[1],
             CppMethod {
               name: "func1".to_string(),
               class_membership: None,
               operator: None,
               return_type: Some(CppType {
                 indirection: CppTypeIndirection::None,
                 is_const: false,
                 base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Bool),
               }),
               arguments: vec![CppFunctionArgument {
                                 name: "x".to_string(),
                                 argument_type: CppType {
                                   indirection: CppTypeIndirection::Ptr,
                                   is_const: false,
                                   base: CppTypeBase::Class {
                                     name: "Magic".to_string(),
                                     template_arguments: None,
                                   },
                                 },
                                 has_default_value: false,
                               }],
               allows_variadic_arguments: false,
               include_file: "myfakelib.h".to_string(),
               origin_location: None,
               template_arguments: None,
             });
  assert_eq!(data.methods[2],
             CppMethod {
               name: "func2".to_string(),
               class_membership: None,
               operator: None,
               return_type: Some(CppType {
                 indirection: CppTypeIndirection::None,
                 is_const: false,
                 base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Bool),
               }),
               arguments: vec![CppFunctionArgument {
                                 name: "arg1".to_string(),
                                 argument_type: CppType {
                                   indirection: CppTypeIndirection::Ref,
                                   is_const: true,
                                   base: CppTypeBase::Class {
                                     name: "Magic".to_string(),
                                     template_arguments: None,
                                   },
                                 },
                                 has_default_value: false,
                               }],
               allows_variadic_arguments: false,
               include_file: "myfakelib.h".to_string(),
               origin_location: None,
               template_arguments: None,
             });
}

fn func_with_unknown_type() {
  let data = run_parser("class SomeClass; \n int func1(SomeClass* x);\n");
  assert!(data.template_instantiations.is_empty());
  assert!(data.types.is_empty());
  assert!(data.methods.is_empty());
}

fn variadic_func() {
  let data = run_parser("int my_printf ( const char * format, ... );\n");
  assert!(data.template_instantiations.is_empty());
  assert!(data.types.is_empty());
  assert!(data.methods.len() == 1);
  assert_eq!(data.methods[0],
             CppMethod {
               name: "my_printf".to_string(),
               class_membership: None,
               operator: None,
               return_type: Some(CppType {
                 indirection: CppTypeIndirection::None,
                 is_const: false,
                 base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
               }),
               arguments: vec![CppFunctionArgument {
                                 name: "format".to_string(),
                                 argument_type: CppType {
                                   indirection: CppTypeIndirection::Ptr,
                                   is_const: true,
                                   base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Char),
                                 },
                                 has_default_value: false,
                               }],
               allows_variadic_arguments: true,
               include_file: "myfakelib.h".to_string(),
               origin_location: None,
               template_arguments: None,
             });
}

#[test]
fn tests() {
  // clang can't be used from multiple threads, so these checks
  // must be run consequently
  simple_func();
  simple_func_with_default_value();
  functions_with_class_arg();
  func_with_unknown_type();
  variadic_func();
}
<commit_msg>more tests<commit_after>
// make sure to add test calls to tests() func
#![forbid(dead_code)]

extern crate tempdir;

use cpp_parser;
use cpp_data::*;
use utils::PathBufPushTweak;
use std::fs;
use std::fs::File;
use std::io::Write;
use cpp_method::*;
use cpp_type::*;
use cpp_operator::CppOperator;

fn run_parser(code: &'static str) -> CppData {
  let dir = tempdir::TempDir::new("test_cpp_parser_run").unwrap();
  let include_dir = dir.path().with_added("include");
  fs::create_dir(&include_dir).unwrap();
  let include_name = "myfakelib.h";
  let include_file_path = include_dir.with_added(&include_name);
  {
    let mut include_file = File::create(&include_file_path).unwrap();
    include_file.write(code.as_bytes()).unwrap();
    include_file.write("\n".as_bytes()).unwrap();
  }
  let mut result = cpp_parser::run(cpp_parser::CppParserConfig {
    include_dirs: vec![include_dir],
    header_name: include_name.to_string(),
    tmp_cpp_path: dir.path().with_added("1.cpp"),
    name_blacklist: Vec::new(),
  });
  for method in &mut result.methods {
    if let Some(ref mut origin_location) = method.origin_location {
      assert_eq!(origin_location.include_file_path,
                 include_file_path.display().to_string());
      assert!(origin_location.line > 0);
    } else {
      panic!("no origin_location in pare result");
    }
    method.origin_location = None;
  }
  result
}

fn simple_func() {
  let data = run_parser("int func1(int x);");
  assert!(data.template_instantiations.is_empty());
  assert!(data.types.is_empty());
  assert!(data.methods.len() == 1);
  assert_eq!(data.methods[0],
             CppMethod {
               name: "func1".to_string(),
               class_membership: None,
               operator: None,
               return_type: Some(CppType {
                 indirection: CppTypeIndirection::None,
                 is_const: false,
                 base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
               }),
               arguments: vec![CppFunctionArgument {
                                 name: "x".to_string(),
                                 argument_type: CppType {
                                   indirection: CppTypeIndirection::None,
                                   is_const: false,
                                   base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
                                 },
                                 has_default_value: false,
                               }],
               allows_variadic_arguments: false,
               include_file: "myfakelib.h".to_string(),
               origin_location: None,
               template_arguments: None,
             });
}

fn simple_func_with_default_value() {
  let data = run_parser("bool func1(int x = 42);");
  assert!(data.template_instantiations.is_empty());
  assert!(data.types.is_empty());
  assert!(data.methods.len() == 1);
  assert_eq!(data.methods[0],
             CppMethod {
               name: "func1".to_string(),
               class_membership: None,
               operator: None,
               return_type: Some(CppType {
                 indirection: CppTypeIndirection::None,
                 is_const: false,
                 base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Bool),
               }),
               arguments: vec![CppFunctionArgument {
                                 name: "x".to_string(),
                                 argument_type: CppType {
                                   indirection: CppTypeIndirection::None,
                                   is_const: false,
                                   base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
                                 },
                                 has_default_value: true,
                               }],
               allows_variadic_arguments: false,
               include_file: "myfakelib.h".to_string(),
               origin_location: None,
               template_arguments: None,
             });
}

fn functions_with_class_arg() {
  let data = run_parser("class Magic { public: int a, b; };
  bool func1(Magic x);
  bool func1(Magic* x);
  bool func2(const Magic&);");
  assert!(data.template_instantiations.is_empty());
  assert_eq!(data.types.len(), 1);
  assert_eq!(data.types[0].name, "Magic");
  match data.types[0].kind {
    CppTypeKind::Class { ref size, ref bases, ref fields, ref template_arguments } => {
      assert!(size.is_some());
      assert!(template_arguments.is_none());
      assert!(bases.is_empty());
      assert_eq!(fields,
                 &vec![CppClassField {
                         name: "a".to_string(),
                         field_type: CppType {
                           indirection: CppTypeIndirection::None,
                           is_const: false,
                           base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
                         },
                         visibility: CppVisibility::Public,
                       },
                       CppClassField {
                         name: "b".to_string(),
                         field_type: CppType {
                           indirection: CppTypeIndirection::None,
                           is_const: false,
                           base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
                         },
                         visibility: CppVisibility::Public,
                       }]);

    }
    _ => panic!("invalid type kind"),
  }
  assert!(data.methods.len() == 3);
  assert_eq!(data.methods[0],
             CppMethod {
               name: "func1".to_string(),
               class_membership: None,
               operator: None,
               return_type: Some(CppType {
                 indirection: CppTypeIndirection::None,
                 is_const: false,
                 base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Bool),
               }),
               arguments: vec![CppFunctionArgument {
                                 name: "x".to_string(),
                                 argument_type: CppType {
                                   indirection: CppTypeIndirection::None,
                                   is_const: false,
                                   base: CppTypeBase::Class {
                                     name: "Magic".to_string(),
                                     template_arguments: None,
                                   },
                                 },
                                 has_default_value: false,
                               }],
               allows_variadic_arguments: false,
               include_file: "myfakelib.h".to_string(),
               origin_location: None,
               template_arguments: None,
             });
  assert_eq!(data.methods[1],
             CppMethod {
               name: "func1".to_string(),
               class_membership: None,
               operator: None,
               return_type: Some(CppType {
                 indirection: CppTypeIndirection::None,
                 is_const: false,
                 base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Bool),
               }),
               arguments: vec![CppFunctionArgument {
                                 name: "x".to_string(),
                                 argument_type: CppType {
                                   indirection: CppTypeIndirection::Ptr,
                                   is_const: false,
                                   base: CppTypeBase::Class {
                                     name: "Magic".to_string(),
                                     template_arguments: None,
                                   },
                                 },
                                 has_default_value: false,
                               }],
               allows_variadic_arguments: false,
               include_file: "myfakelib.h".to_string(),
               origin_location: None,
               template_arguments: None,
             });
  assert_eq!(data.methods[2],
             CppMethod {
               name: "func2".to_string(),
               class_membership: None,
               operator: None,
               return_type: Some(CppType {
                 indirection: CppTypeIndirection::None,
                 is_const: false,
                 base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Bool),
               }),
               arguments: vec![CppFunctionArgument {
                                 name: "arg1".to_string(),
                                 argument_type: CppType {
                                   indirection: CppTypeIndirection::Ref,
                                   is_const: true,
                                   base: CppTypeBase::Class {
                                     name: "Magic".to_string(),
                                     template_arguments: None,
                                   },
                                 },
                                 has_default_value: false,
                               }],
               allows_variadic_arguments: false,
               include_file: "myfakelib.h".to_string(),
               origin_location: None,
               template_arguments: None,
             });
}

fn func_with_unknown_type() {
  let data = run_parser("class SomeClass; \n int func1(SomeClass* x);");
  assert!(data.template_instantiations.is_empty());
  assert!(data.types.is_empty());
  assert!(data.methods.is_empty());
}

fn variadic_func() {
  let data = run_parser("int my_printf ( const char * format, ... );");
  assert!(data.template_instantiations.is_empty());
  assert!(data.types.is_empty());
  assert!(data.methods.len() == 1);
  assert_eq!(data.methods[0],
             CppMethod {
               name: "my_printf".to_string(),
               class_membership: None,
               operator: None,
               return_type: Some(CppType {
                 indirection: CppTypeIndirection::None,
                 is_const: false,
                 base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Int),
               }),
               arguments: vec![CppFunctionArgument {
                                 name: "format".to_string(),
                                 argument_type: CppType {
                                   indirection: CppTypeIndirection::Ptr,
                                   is_const: true,
                                   base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Char),
                                 },
                                 has_default_value: false,
                               }],
               allows_variadic_arguments: true,
               include_file: "myfakelib.h".to_string(),
               origin_location: None,
               template_arguments: None,
             });
}

fn free_template_func() {
  let data = run_parser("template<typename T> T abs(T value) { return 2*value; }");
  assert!(data.template_instantiations.is_empty());
  assert!(data.types.is_empty());
  assert!(data.methods.len() == 1);
  assert_eq!(data.methods[0],
             CppMethod {
               name: "abs".to_string(),
               class_membership: None,
               operator: None,
               return_type: Some(CppType {
                 indirection: CppTypeIndirection::None,
                 is_const: false,
                 base: CppTypeBase::TemplateParameter {
                   nested_level: 0,
                   index: 0,
                 },
               }),
               arguments: vec![CppFunctionArgument {
                                 name: "value".to_string(),
                                 argument_type: CppType {
                                   indirection: CppTypeIndirection::None,
                                   is_const: false,
                                   base: CppTypeBase::TemplateParameter {
                                     nested_level: 0,
                                     index: 0,
                                   },
                                 },
                                 has_default_value: false,
                               }],
               allows_variadic_arguments: false,
               include_file: "myfakelib.h".to_string(),
               origin_location: None,
               template_arguments: Some(vec!["T".to_string()]),
             });
}

fn free_func_operator_sub() {
  for code in &["class C1 {}; \n C1 operator-(C1 a, C1 b);",
                "class C1 {}; \n C1 operator -(C1 a, C1 b);"] {
    let data = run_parser(code);
    assert!(data.template_instantiations.is_empty());
    assert!(data.types.len() == 1);
    assert!(data.methods.len() == 1);
    assert_eq!(data.methods[0],
               CppMethod {
                 name: "operator-".to_string(),
                 class_membership: None,
                 operator: Some(CppOperator::Subtraction),
                 return_type: Some(CppType {
                   indirection: CppTypeIndirection::None,
                   is_const: false,
                   base: CppTypeBase::Class {
                     name: "C1".to_string(),
                     template_arguments: None,
                   },
                 }),
                 arguments: vec![CppFunctionArgument {
                                   name: "a".to_string(),
                                   argument_type: CppType {
                                     indirection: CppTypeIndirection::None,
                                     is_const: false,
                                     base: CppTypeBase::Class {
                                       name: "C1".to_string(),
                                       template_arguments: None,
                                     },
                                   },
                                   has_default_value: false,
                                 },
                                 CppFunctionArgument {
                                   name: "b".to_string(),
                                   argument_type: CppType {
                                     indirection: CppTypeIndirection::None,
                                     is_const: false,
                                     base: CppTypeBase::Class {
                                       name: "C1".to_string(),
                                       template_arguments: None,
                                     },
                                   },
                                   has_default_value: false,
                                 }],
                 allows_variadic_arguments: false,
                 include_file: "myfakelib.h".to_string(),
                 origin_location: None,
                 template_arguments: None,
               });
  }
}

// fn operator_bool() {
// let data = run_parser("class C1 { operator bool(const C1& a); };");
// assert!(data.template_instantiations.is_empty());
// assert!(data.types.len() == 1);
// assert!(data.methods.len() == 1);
// assert_eq!(data.methods[0],
// CppMethod {
// name: "operator bool".to_string(),
// class_membership: None,
// operator: Some(CppOperator::Conversion(CppType {
// indirection: CppTypeIndirection::None,
// is_const: false,
// base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Bool),
// })),
// return_type: Some(CppType {
// indirection: CppTypeIndirection::None,
// is_const: false,
// base: CppTypeBase::BuiltInNumeric(CppBuiltInNumericType::Bool),
// }),
// arguments: vec![CppFunctionArgument {
// name: "a".to_string(),
// argument_type: CppType {
// indirection: CppTypeIndirection::Ref,
// is_const: true,
// base: CppTypeBase::Class {
// name: "C1".to_string(),
// template_arguments: None,
// },
// },
// has_default_value: false,
// }],
// allows_variadic_arguments: false,
// include_file: "myfakelib.h".to_string(),
// origin_location: None,
// template_arguments: None,
// });
// }

#[test]
fn tests() {
  // clang can't be used from multiple threads, so these checks
  // must be run consequently
  simple_func();
  simple_func_with_default_value();
  functions_with_class_arg();
  func_with_unknown_type();
  variadic_func();
  free_template_func();
  free_func_operator_sub();
  // free_func_operator_bool();
}
<|endoftext|>


<commit_before>//
// zhtta.rs
//
// Running on Rust 0.8
//
// Starting code for PS3
//
// Note: it would be very unwise to run this server on a machine that is
// on the Internet and contains any sensitive files!
//
// University of Virginia - cs4414 Fall 2013
// Weilin Xu and David Evans
// Version 0.2

extern mod extra;

use std::rt::io::*;
use std::rt::io::net::ip::SocketAddr;
use std::io::println;
use std::cell::Cell;
use std::{os, str, io};
use extra::arc;
use std::comm::*;

static PORT:    int = 4414;
static IP: &'static str = "0.0.0.0";

struct sched_msg {
    stream: Option<std::rt::io::net::tcp::TcpStream>,
    filepath: ~std::path::PosixPath
}

fn main() {
    let req_vec: ~[sched_msg] = ~[];
    let shared_req_vec = arc::RWArc::new(req_vec);
    let visitor_count: uint = 0;
    let shared_vis_count = arc::RWArc::new(visitor_count);
    let add_vec = shared_req_vec.clone();
    let take_vec = shared_req_vec.clone();

    let req_vec_char: ~[sched_msg] = ~[];
    let shared_req_vec_char = arc::RWArc::new(req_vec_char);
    let add_vec_char = shared_req_vec_char.clone();
    let take_vec_char = shared_req_vec_char.clone();
    let (port_char, chan_char) = stream();
    let chan_char = SharedChan::new(chan_char);

    let (port, chan) = stream();
    let chan = SharedChan::new(chan);

    // add file requests into queue.
    do spawn {
        while(true) {
            do add_vec_char.write |vec| {
                let tf:sched_msg = port_char.recv();
                (*vec).push(tf);
                println("add to wa-queue");
            }
        }
    }

    do spawn {
        loop {
            do add_vec.write |vec| {
                // port.recv() will block the code and keep locking the RWArc, so we simply use peek() to check if there's message to recv.
                // But a asynchronous solution will be much better.
                if (port.peek()) {
                    let tf:sched_msg = port.recv();
                    (*vec).push(tf);
                    println(fmt!("add to queue, size: %ud", (*vec).len()));
                }
            }
        }
    }

// take file requests from queue, and send a response.
    // FIFO
    do spawn {
        loop {
            do take_vec_char.write |vec| {
                let mut tf = (*vec).pop();

                match io::read_whole_file(tf.filepath) {
                    Ok(file_data) => {
                        tf.stream.write(file_data);
                    }
                    Err(err) => {
                        println(err);
                    }
                }
            }
            do take_vec.write |vec| {
                if ((*vec).len() > 0) {
                    // FILO didn't make sense in service scheduling, so we modify it as FIFO by using shift_opt() rather than pop().
                    let tf_opt: Option<sched_msg> = (*vec).shift_opt();
                    let mut tf = tf_opt.unwrap();
                    println(fmt!("shift from queue, size: %ud", (*vec).len()));

                    match io::read_whole_file(tf.filepath) { // killed if file size is larger than memory size.
                        Ok(file_data) => {
                            println(fmt!("begin serving file [%?]", tf.filepath));
                            // A web server should always reply a HTTP header for any legal HTTP request.
                            tf.stream.write("HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream; charset=UTF-8\r\n\r\n".as_bytes());
                            tf.stream.write(file_data);
                            println(fmt!("finish file [%?]", tf.filepath));
                        }
                        Err(err) => {
                            println(err);
                        }
                    }
                }
            }
        }
    }

    let ip = match FromStr::from_str(IP) {
        Some(ip) => ip,
        None => {
            println(fmt!("Error: Invalid IP address <%s>", IP));
            return;
        },
    };
    println(fmt!("ip address: %?", ip));

    let socket = net::tcp::TcpListener::bind(SocketAddr {ip: ip, port: PORT as u16});

    println(fmt!("Listening on tcp port %d ...", PORT));
    let mut acceptor = socket.listen().unwrap();

    // we can limit the incoming connection count.
    //for stream in acceptor.incoming().take(10 as uint) {
        for stream in acceptor.incoming() {
            let mut stream = stream;
            let mut peer_name: ~str = ~"";
            match stream {
                Some(ref mut s) => {
                    match s.peer_name() {
                        Some(peername) => {
                        //println( fmt!("Peer address: %s \n", peername.to_str() ));
                        peer_name = peername.to_str();
                    }
                    None => ()
                }
            }
            None => ()
        }
        let stream = Cell::new(stream);
        let peer_name = Cell::new(peer_name);
        let local_vis_count = shared_vis_count.clone();
        // Start a new task to handle the connection
        let child_chan = chan.clone();
        let child_chan_char = chan_char.clone();
        do spawn {
            let mut vis_count = 0;
            let peer_name = peer_name.take();
            do local_vis_count.write |count| {
                (*count) = (*count)+1;
                vis_count = (*count);
            }
            let mut stream = stream.take();
            let mut buf = [0, ..500];
            stream.read(buf);
            let request_str = str::from_utf8(buf);

            let req_group : ~[&str]= request_str.splitn_iter(' ', 3).collect();
            if req_group.len() > 2 {
                let path = req_group[1];
                println(fmt!("Request for path: \n%?", path));

                let file_path = ~os::getcwd().push(path.replace("/../", ""));
                if !os::path_exists(file_path) || os::path_is_dir(file_path) {
                    println(fmt!("Request received:\n%s", request_str));
                    let response: ~str = fmt!(
                        "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n
                        <doctype !html><title>Hello, Rust!</title>

Greetings, Krusty!

Visitor count: %u

                        \r\n", vis_count);

                    stream.write(response.as_bytes());
                }
                else {
                    // may do scheduling here
                    if (peer_name.starts_with("128.143.") || peer_name.starts_with("137.54.") || peer_name.starts_with("127.")) {
                        println(fmt!("peer_name fits into Charlottesville. Going into Wa-queue"));
                        let msg: sched_msg = sched_msg{stream: stream, filepath: file_path.clone()};
                        child_chan_char.send(msg);
                    } else {
                        println(fmt!("peer_name: %?", peer_name));
                        let msg: sched_msg = sched_msg{stream: stream, filepath: file_path.clone()};
                        child_chan.send(msg);
                        println(fmt!("get file request: %?", file_path));
                    }
                }
            }
            println!("connection terminates")
        }
    }
}
<commit_msg>i think i have WahooFirst scheduling working...but it's hard to test right now.<commit_after>//
// zhtta.rs
//
// Running on Rust 0.8
//
// Starting code for PS3
//
// Note: it would be very unwise to run this server on a machine that is
// on the Internet and contains any sensitive files!
//
// University of Virginia - cs4414 Fall 2013
// Weilin Xu and David Evans
// Version 0.2

extern mod extra;

use std::rt::io::*;
use std::rt::io::net::ip::SocketAddr;
use std::io::println;
use std::cell::Cell;
use std::{os, str, io};
use extra::arc;
use std::comm::*;

static PORT:    int = 4414;
static IP: &'static str = "0.0.0.0";

struct sched_msg {
    stream: Option<std::rt::io::net::tcp::TcpStream>,
    filepath: ~std::path::PosixPath
}

fn main() {
    let req_vec: ~[sched_msg] = ~[];
    let shared_req_vec = arc::RWArc::new(req_vec);
    let visitor_count: uint = 0;
    let shared_vis_count = arc::RWArc::new(visitor_count);
    let add_vec = shared_req_vec.clone();
    let take_vec = shared_req_vec.clone();

    let req_vec_char: ~[sched_msg] = ~[];
    let shared_req_vec_char = arc::RWArc::new(req_vec_char);
    let add_vec_char = shared_req_vec_char.clone();
    let take_vec_char = shared_req_vec_char.clone();
    let (port_char, chan_char) = stream();
    let chan_char = SharedChan::new(chan_char);

    let (port, chan) = stream();
    let chan = SharedChan::new(chan);

    // add file requests into queue.
    do spawn {
        loop {
            do add_vec.write |vec| {
                // port.recv() will block the code and keep locking the RWArc, so we simply use peek() to check if there's message to recv.
                // But a asynchronous solution will be much better.
                if (port.peek()) {
                    let tf:sched_msg = port.recv();
                    (*vec).push(tf);
                    println(fmt!("add to queue, size: %ud", (*vec).len()));
                }
            }
            do add_vec_char.write |vec| {
                // port.recv() will block the code and keep locking the RWArc, so we simply use peek() to check if there's message to recv.
                // But a asynchronous solution will be much better.
                if (port_char.peek()) {
                    let tf:sched_msg = port_char.recv();
                    (*vec).push(tf);
                    println(fmt!("add to wa-queue, size: %ud", (*vec).len()));
                }
            }
        }
    }

    // take file requests from queue, and send a response.
    // FIFO
    do spawn {
        loop {
            do take_vec_char.write |vec| {
                if ((*vec).len() > 0) {
                    // FILO didn't make sense in service scheduling, so we modify it as FIFO by using shift_opt() rather than pop().
                    let tf_opt: Option<sched_msg> = (*vec).shift_opt();
                    let mut tf = tf_opt.unwrap();
                    println(fmt!("shift from wa-queue, size: %ud", (*vec).len()));

                    match io::read_whole_file(tf.filepath) { // killed if file size is larger than memory size.
                        Ok(file_data) => {
                            println(fmt!("begin serving file [%?]", tf.filepath));
                            // A web server should always reply a HTTP header for any legal HTTP request.
                            tf.stream.write("HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream; charset=UTF-8\r\n\r\n".as_bytes());
                            tf.stream.write(file_data);
                            println(fmt!("finish file [%?]", tf.filepath));
                        }
                        Err(err) => {
                            println(err);
                        }
                    }
                }
            }

            do take_vec.write |vec| {
                if ((*vec).len() > 0) {
                    // FILO didn't make sense in service scheduling, so we modify it as FIFO by using shift_opt() rather than pop().
                    let tf_opt: Option<sched_msg> = (*vec).shift_opt();
                    let mut tf = tf_opt.unwrap();
                    println(fmt!("shift from queue, size: %ud", (*vec).len()));

                    match io::read_whole_file(tf.filepath) { // killed if file size is larger than memory size.
                        Ok(file_data) => {
                            println(fmt!("begin serving file [%?]", tf.filepath));
                            // A web server should always reply a HTTP header for any legal HTTP request.
                            tf.stream.write("HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream; charset=UTF-8\r\n\r\n".as_bytes());
                            tf.stream.write(file_data);
                            println(fmt!("finish file [%?]", tf.filepath));
                        }
                        Err(err) => {
                            println(err);
                        }
                    }
                }
            }
        }
    }

    let ip = match FromStr::from_str(IP) {
        Some(ip) => ip,
        None => {
            println(fmt!("Error: Invalid IP address <%s>", IP));
            return;
        },
    };

    let socket = net::tcp::TcpListener::bind(SocketAddr {ip: ip, port: PORT as u16});

    println(fmt!("Listening on tcp port %d ...", PORT));
    let mut acceptor = socket.listen().unwrap();

    // we can limit the incoming connection count.
    //for stream in acceptor.incoming().take(10 as uint) {
        for stream in acceptor.incoming() {
            let mut stream = stream;
            let mut peer_name: ~str = ~"";
            match stream {
                Some(ref mut s) => {
                    match s.peer_name() {
                        Some(peername) => {
                        //println( fmt!("Peer address: %s \n", peername.to_str() ));
                        peer_name = peername.to_str();
                    }
                    None => ()
                }
            }
            None => ()
        }
        let stream = Cell::new(stream);
        let peer_name = Cell::new(peer_name);
        let local_vis_count = shared_vis_count.clone();
        // Start a new task to handle the connection
        let child_chan = chan.clone();
        let child_chan_char = chan_char.clone();
        do spawn {
            let mut vis_count = 0;
            let peer_name = peer_name.take();
            do local_vis_count.write |count| {
                (*count) = (*count)+1;
                vis_count = (*count);
            }
            let mut stream = stream.take();
            let mut buf = [0, ..500];
            stream.read(buf);
            let request_str = str::from_utf8(buf);

            let req_group : ~[&str]= request_str.splitn_iter(' ', 3).collect();
            if req_group.len() > 2 {
                let path = req_group[1];
                println(fmt!("Request for path: \n%?", path));

                let file_path = ~os::getcwd().push(path.replace("/../", ""));
                if !os::path_exists(file_path) || os::path_is_dir(file_path) {
                    println(fmt!("Request received:\n%s", request_str));
                    let response: ~str = fmt!(
                        "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n
                        <doctype !html><title>Hello, Rust!</title>

Greetings, Krusty!

Visitor count: %u

                        \r\n", vis_count);

                    stream.write(response.as_bytes());
                }
                else {
                    // may do scheduling here
                    if (peer_name.starts_with("128.143.") || peer_name.starts_with("137.54.") || peer_name.starts_with("127.")) {
                        println(fmt!("peer_name fits into Charlottesville. Going into Wa-queue"));
                        let msg: sched_msg = sched_msg{stream: stream, filepath: file_path.clone()};
                        child_chan_char.send(msg);
                    } else {
                        println(fmt!("peer_name: %?", peer_name));
                        let msg: sched_msg = sched_msg{stream: stream, filepath: file_path.clone()};
                        child_chan.send(msg);
                        println(fmt!("get file request: %?", file_path));
                    }
                }
            }
            println!("connection terminates")
        }
    }
}
<|endoftext|>


<commit_before>use num::BigUint;
use num::bigint::ToBigUint;

pub fn main() {
    let ans = (1..10_000).filter(|n| is_lychrel(&n)).count();
    println!("{}", ans);
}

fn is_lychrel(&n: &u64) -> bool {
    let is_palindrom = |l: &str| l.chars().zip(l.chars().rev()).all(|(i, j)| i == j);
    let reverse_add = |n: &BigUint| {
        n + n.to_string().chars().rev().collect::<String>().parse::<BigUint>().unwrap()
    };

    let mut n = n.to_biguint().unwrap();
    for _ in 0..50 {
        let m = reverse_add(&n);
        if is_palindrom(&m.to_string()) {
            return false;
        }
        n = m;
    }
    true
}
<commit_msg>went a little far here with & usage<commit_after>use num::BigUint;
use num::bigint::ToBigUint;

pub fn main() {
    let ans = (1..10_000).filter(|n| is_lychrel(n)).count();
    println!("{}", ans);
}

fn is_lychrel(n: &u64) -> bool {
    let is_palindrom = |l: &str| l.chars().zip(l.chars().rev()).all(|(i, j)| i == j);
    let reverse_add = |n: &BigUint| {
        n + n.to_string().chars().rev().collect::<String>().parse::<BigUint>().unwrap()
    };

    let mut n = n.to_biguint().unwrap();
    for _ in 0..50 {
        let m = reverse_add(&n);
        if is_palindrom(&m.to_string()) {
            return false;
        }
        n = m;
    }
    true
}
<|endoftext|>


<commit_before>use std::time::Duration;
use std::collections::HashMap;

use telebot;
use telebot::functions::*;
use tokio_core::reactor::{Interval, Handle, Timeout};
use futures::{self, Future, Stream, IntoFuture};
use tokio_curl::Session;
use regex::Regex;

use data;
use feed;
use utlis::{Escape, EscapeUrl, send_multiple_messages, format_and_split_msgs, to_chinese_error_msg,
            truncate_message, log_error, TELEGRAM_MAX_MSG_LEN};

// 5 minute
const FREQUENCY_SECOND: u64 = 300;

lazy_static!{
    // it's different from `feed::HOST`, so maybe need a better name?
    static ref HOST: Regex = Regex::new(r"^(?:https?://)?([^/]+)").unwrap();
}

pub fn spawn_fetcher(bot: telebot::RcBot, db: data::Database, handle: Handle) {
    handle.clone().spawn(Interval::new(Duration::from_secs(FREQUENCY_SECOND), &handle)
                             .expect("failed to start feed loop")
                             .map_err(|e| error!("feed loop error: {}", e))
                             .for_each(move |_| {
        let feeds = db.get_all_feeds();
        let grouped_feeds = grouping_by_host(feeds);
        let handle2 = handle.clone();
        let bot = bot.clone();
        let db = db.clone();
        let fetcher = futures::stream::iter(grouped_feeds.into_iter().map(|x| Ok(x)))
            .for_each(move |group| {
                let session = Session::new(handle2.clone());
                let bot = bot.clone();
                let db = db.clone();
                let group_fetcher = futures::stream::iter(group.into_iter().map(|x| Ok(x)))
                    .for_each(move |feed| {
                                  fetch_feed_updates(bot.clone(), db.clone(), session.clone(), feed)
                              });
                handle2.spawn(group_fetcher);
                Timeout::new(Duration::from_secs(1), &handle2)
                    .expect("failed to start sleep")
                    .map_err(|e| error!("feed loop sleep error: {}", e))
            });
        handle.spawn(fetcher);
        Ok(())
    }))
}

fn grouping_by_host(feeds: Vec<data::Feed>) -> Vec<Vec<data::Feed>> {
    let mut result = HashMap::new();
    for feed in feeds {
        let host = get_host(&feed.link).to_owned();
        let group = result.entry(host).or_insert_with(|| Vec::new());
        group.push(feed);
    }
    result.into_iter().map(|(_, v)| v).collect()
}

fn get_host(url: &str) -> &str {
    HOST.captures(url).map_or(url, |r| r.get(0).unwrap().as_str())
}

fn shoud_unsubscribe_for_user(tg_err_msg: &str) -> bool {
    tg_err_msg.contains("Forbidden") || tg_err_msg.contains("chat not found") ||
    tg_err_msg.contains("group chat was migrated to a supergroup chat")
}

fn fetch_feed_updates<'a>(bot: telebot::RcBot,
                          db: data::Database,
                          session: Session,
                          feed: data::Feed)
                          -> impl Future<Item = (), Error = ()> + 'a {
    let bot_ = bot.clone();
    let db_ = db.clone();
    let feed_ = feed.clone();
    feed::fetch_feed(session, feed.link.to_owned())
        .map(move |rss| (bot_, db_, rss, feed_))
        .or_else(move |e| {
            if db.inc_error_count(&feed.link) > 1440 {
                    Err((bot, db, feed))
                } else {
                    Ok(())
                }
                .into_future()
                .or_else(|(bot, db, feed)| {
                    // 1440 * 5 minute = 5 days
                    db.reset_error_count(&feed.link);
                    let err_msg = to_chinese_error_msg(e);
                    let mut msgs = Vec::with_capacity(feed.subscribers.len());
                    for &subscriber in &feed.subscribers {
                        let m = bot.message(subscriber,
                                            format!("《{}》\
                                                     已经连续 5 天拉取出错 ({}),\
                                                     可能已经关闭, 请取消订阅",
                                                    EscapeUrl(&feed.link),
                                                    Escape(&feed.title),
                                                    Escape(&err_msg)))
                            .parse_mode("HTML")
                            .disable_web_page_preview(true)
                            .send();
                        let feed_link = feed.link.clone();
                        let db = db.clone();
                        let bot = bot.clone();
                        let r = m.or_else(move |e| {
                            match e {
                                    telebot::error::Error::Telegram(ref s)
                                        if shoud_unsubscribe_for_user(s) => {
                                            Err((bot, db, s.to_owned(), subscriber, feed_link))
                                        }
                                    _ => {
                                        warn!("failed to send error to {}, {:?}", subscriber, e);
                                        Ok(())
                                    }
                                }
                                .into_future()
                                .or_else(|(bot, db, s, subscriber, feed_link)| {
                                    if let Err(e) = db.unsubscribe(subscriber, &feed_link) {
                                        log_error(&e);
                                    }
                                    bot.message(subscriber,
                                                 format!("无法修复的错误 ({}), 自动退订",
                                                         s))
                                        .send()
                                        .then(|_| Err(()))
                                })
                                .and_then(|_| Err(()))
                        });
                        // if not use Box, rustc will panic
                        msgs.push(Box::new(r) as Box<Future<Item = _, Error = _>>);
                    }
                    futures::future::join_all(msgs).then(|_| Err(()))
                })
                .and_then(|_| Err(()))
        })
        .and_then(|(bot, db, rss, feed)| {
            if rss.title != feed.title {
                db.update_title(&feed.link, &rss.title);
            }
            let updates = db.update(&feed.link, rss.items);
            if updates.is_empty() {
                futures::future::err(())
            } else {
                futures::future::ok((bot, db, feed, rss.title, rss.link, updates))
            }
        })
        .and_then(|(bot, db, feed, rss_title, rss_link, updates)| {
            let msgs = format_and_split_msgs(format!("{}", rss_title), &updates, |item| {
                let title = item.title
                    .as_ref()
                    .map(|s| s.as_str())
                    .unwrap_or_else(|| &rss_title);
                let link = item.link
                    .as_ref()
                    .map(|s| s.as_str())
                    .unwrap_or_else(|| &rss_link);
                format!("{}",
                        EscapeUrl(link),
                        Escape(&truncate_message(title, TELEGRAM_MAX_MSG_LEN - 500)))
            });

            let mut msg_futures = Vec::with_capacity(feed.subscribers.len());
            for &subscriber in &feed.subscribers {
                let feed_link = feed.link.clone();
                let db = db.clone();
                let bot = bot.clone();
                let r = send_multiple_messages(&bot, subscriber, msgs.clone())
                    .or_else(move |e| {
                        match e {
                                telebot::error::Error::Telegram(ref s)
                                if shoud_unsubscribe_for_user(s) => {
                                Err((bot, db, s.to_owned(), subscriber, feed_link))
                            }
                                _ => {
                                    warn!("failed to send updates to {}, {:?}", subscriber, e);
                                    Ok(())
                                }
                            }
                            .into_future()
                            .or_else(|(bot, db, s, subscriber, feed_link)| {
                                if let Err(e) = db.unsubscribe(subscriber, &feed_link) {
                                    log_error(&e);
                                }
                                bot.message(subscriber,
                                             format!("无法修复的错误 ({}), 自动退订", s))
                                    .send()
                                    .then(|_| Err(()))
                            })
                    });
                msg_futures.push(Box::new(r) as Box<Future<Item = _, Error = _>>);
            }
            futures::future::join_all(msg_futures).then(|_| Ok(()))
        })
}
<commit_msg>fix missing escapes<commit_after>use std::time::Duration;
use std::collections::HashMap;

use telebot;
use telebot::functions::*;
use tokio_core::reactor::{Interval, Handle, Timeout};
use futures::{self, Future, Stream, IntoFuture};
use tokio_curl::Session;
use regex::Regex;

use data;
use feed;
use utlis::{Escape, EscapeUrl, send_multiple_messages, format_and_split_msgs, to_chinese_error_msg,
            truncate_message, log_error, TELEGRAM_MAX_MSG_LEN};

// 5 minute
const FREQUENCY_SECOND: u64 = 300;

lazy_static!{
    // it's different from `feed::HOST`, so maybe need a better name?
    static ref HOST: Regex = Regex::new(r"^(?:https?://)?([^/]+)").unwrap();
}

pub fn spawn_fetcher(bot: telebot::RcBot, db: data::Database, handle: Handle) {
    handle.clone().spawn(Interval::new(Duration::from_secs(FREQUENCY_SECOND), &handle)
                             .expect("failed to start feed loop")
                             .map_err(|e| error!("feed loop error: {}", e))
                             .for_each(move |_| {
        let feeds = db.get_all_feeds();
        let grouped_feeds = grouping_by_host(feeds);
        let handle2 = handle.clone();
        let bot = bot.clone();
        let db = db.clone();
        let fetcher = futures::stream::iter(grouped_feeds.into_iter().map(|x| Ok(x)))
            .for_each(move |group| {
                let session = Session::new(handle2.clone());
                let bot = bot.clone();
                let db = db.clone();
                let group_fetcher = futures::stream::iter(group.into_iter().map(|x| Ok(x)))
                    .for_each(move |feed| {
                                  fetch_feed_updates(bot.clone(), db.clone(), session.clone(), feed)
                              });
                handle2.spawn(group_fetcher);
                Timeout::new(Duration::from_secs(1), &handle2)
                    .expect("failed to start sleep")
                    .map_err(|e| error!("feed loop sleep error: {}", e))
            });
        handle.spawn(fetcher);
        Ok(())
    }))
}

fn grouping_by_host(feeds: Vec<data::Feed>) -> Vec<Vec<data::Feed>> {
    let mut result = HashMap::new();
    for feed in feeds {
        let host = get_host(&feed.link).to_owned();
        let group = result.entry(host).or_insert_with(|| Vec::new());
        group.push(feed);
    }
    result.into_iter().map(|(_, v)| v).collect()
}

fn get_host(url: &str) -> &str {
    HOST.captures(url).map_or(url, |r| r.get(0).unwrap().as_str())
}

fn shoud_unsubscribe_for_user(tg_err_msg: &str) -> bool {
    tg_err_msg.contains("Forbidden") || tg_err_msg.contains("chat not found") ||
    tg_err_msg.contains("group chat was migrated to a supergroup chat")
}

fn fetch_feed_updates<'a>(bot: telebot::RcBot,
                          db: data::Database,
                          session: Session,
                          feed: data::Feed)
                          -> impl Future<Item = (), Error = ()> + 'a {
    let bot_ = bot.clone();
    let db_ = db.clone();
    let feed_ = feed.clone();
    feed::fetch_feed(session, feed.link.to_owned())
        .map(move |rss| (bot_, db_, rss, feed_))
        .or_else(move |e| {
            if db.inc_error_count(&feed.link) > 1440 {
                    Err((bot, db, feed))
                } else {
                    Ok(())
                }
                .into_future()
                .or_else(|(bot, db, feed)| {
                    // 1440 * 5 minute = 5 days
                    db.reset_error_count(&feed.link);
                    let err_msg = to_chinese_error_msg(e);
                    let mut msgs = Vec::with_capacity(feed.subscribers.len());
                    for &subscriber in &feed.subscribers {
                        let m = bot.message(subscriber,
                                            format!("《{}》\
                                                     已经连续 5 天拉取出错 ({}),\
                                                     可能已经关闭, 请取消订阅",
                                                    EscapeUrl(&feed.link),
                                                    Escape(&feed.title),
                                                    Escape(&err_msg)))
                            .parse_mode("HTML")
                            .disable_web_page_preview(true)
                            .send();
                        let feed_link = feed.link.clone();
                        let db = db.clone();
                        let bot = bot.clone();
                        let r = m.or_else(move |e| {
                            match e {
                                    telebot::error::Error::Telegram(ref s)
                                        if shoud_unsubscribe_for_user(s) => {
                                            Err((bot, db, s.to_owned(), subscriber, feed_link))
                                        }
                                    _ => {
                                        warn!("failed to send error to {}, {:?}", subscriber, e);
                                        Ok(())
                                    }
                                }
                                .into_future()
                                .or_else(|(bot, db, s, subscriber, feed_link)| {
                                    if let Err(e) = db.unsubscribe(subscriber, &feed_link) {
                                        log_error(&e);
                                    }
                                    bot.message(subscriber,
                                                 format!("无法修复的错误 ({}), 自动退订",
                                                         s))
                                        .send()
                                        .then(|_| Err(()))
                                })
                                .and_then(|_| Err(()))
                        });
                        // if not use Box, rustc will panic
                        msgs.push(Box::new(r) as Box<Future<Item = _, Error = _>>);
                    }
                    futures::future::join_all(msgs).then(|_| Err(()))
                })
                .and_then(|_| Err(()))
        })
        .and_then(|(bot, db, rss, feed)| {
            if rss.title != feed.title {
                db.update_title(&feed.link, &rss.title);
            }
            let updates = db.update(&feed.link, rss.items);
            if updates.is_empty() {
                futures::future::err(())
            } else {
                futures::future::ok((bot, db, feed, rss.title, rss.link, updates))
            }
        })
        .and_then(|(bot, db, feed, rss_title, rss_link, updates)| {
            let msgs =
                format_and_split_msgs(format!("{}", Escape(&rss_title)), &updates, |item| {
                    let title = item.title
                        .as_ref()
                        .map(|s| s.as_str())
                        .unwrap_or_else(|| &rss_title);
                    let link = item.link
                        .as_ref()
                        .map(|s| s.as_str())
                        .unwrap_or_else(|| &rss_link);
                    format!("{}",
                            EscapeUrl(link),
                            Escape(&truncate_message(title, TELEGRAM_MAX_MSG_LEN - 500)))
                });

            let mut msg_futures = Vec::with_capacity(feed.subscribers.len());
            for &subscriber in &feed.subscribers {
                let feed_link = feed.link.clone();
                let db = db.clone();
                let bot = bot.clone();
                let r = send_multiple_messages(&bot, subscriber, msgs.clone())
                    .or_else(move |e| {
                        match e {
                                telebot::error::Error::Telegram(ref s)
                                if shoud_unsubscribe_for_user(s) => {
                                Err((bot, db, s.to_owned(), subscriber, feed_link))
                            }
                                _ => {
                                    warn!("failed to send updates to {}, {:?}", subscriber, e);
                                    Ok(())
                                }
                            }
                            .into_future()
                            .or_else(|(bot, db, s, subscriber, feed_link)| {
                                if let Err(e) = db.unsubscribe(subscriber, &feed_link) {
                                    log_error(&e);
                                }
                                bot.message(subscriber,
                                             format!("无法修复的错误 ({}), 自动退订", s))
                                    .send()
                                    .then(|_| Err(()))
                            })
                    });
                msg_futures.push(Box::new(r) as Box<Future<Item = _, Error = _>>);
            }
            futures::future::join_all(msg_futures).then(|_| Ok(()))
        })
}
<|endoftext|>


<commit_before>use syntax::ast;
use syntax::codemap::{DUMMY_SP, Span, respan};
use syntax::ptr::P;

use ident::ToIdent;
use invoke::{Invoke, Identity};
use ty::TyBuilder;

//////////////////////////////////////////////////////////////////////////////

pub struct FnDeclBuilder<F=Identity> {
    callback: F,
    span: Span,
    args: Vec<ast::Arg>,
    variadic: bool,
}

impl FnDeclBuilder {
    pub fn new() -> FnDeclBuilder {
        FnDeclBuilder::new_with_callback(Identity)
    }
}

impl<F> FnDeclBuilder<F>
    where F: Invoke<P<ast::FnDecl>>,
{
    pub fn new_with_callback(callback: F) -> Self {
        FnDeclBuilder {
            callback: callback,
            span: DUMMY_SP,
            args: Vec::new(),
            variadic: false,
        }
    }

    pub fn span(mut self, span: Span) -> Self {
        self.span = span;
        self
    }

    pub fn variadic(mut self) -> Self {
        self.variadic = true;
        self
    }

    pub fn with_arg(mut self, arg: ast::Arg) -> Self {
        self.args.push(arg);
        self
    }

    pub fn arg<I>(self, id: I) -> ArgBuilder<Self>
        where I: ToIdent,
    {
        ArgBuilder::new_with_callback(id, self)
    }

    pub fn no_return(self) -> F::Result {
        let ret_ty = ast::FunctionRetTy::NoReturn(self.span);
        self.build(ret_ty)
    }

    pub fn default_return(self) -> F::Result {
        let ret_ty = ast::FunctionRetTy::DefaultReturn(self.span);
        self.build(ret_ty)
    }

    pub fn build_return(self, ty: P<ast::Ty>) -> F::Result {
        self.build(ast::FunctionRetTy::Return(ty))
    }

    pub fn return_(self) -> TyBuilder<Self> {
        TyBuilder::new_with_callback(self)
    }

    pub fn build(self, output: ast::FunctionRetTy) -> F::Result {
        self.callback.invoke(P(ast::FnDecl {
            inputs: self.args,
            output: output,
            variadic: self.variadic,
        }))
    }
}

impl<F> Invoke<ast::Arg> for FnDeclBuilder<F>
    where F: Invoke<P<ast::FnDecl>>
{
    type Result = Self;

    fn invoke(self, arg: ast::Arg) -> Self {
        self.with_arg(arg)
    }
}

impl<F> Invoke<P<ast::Ty>> for FnDeclBuilder<F>
    where F: Invoke<P<ast::FnDecl>>,
{
    type Result = F::Result;

    fn invoke(self, ty: P<ast::Ty>) -> F::Result {
        self.build_return(ty)
    }
}

//////////////////////////////////////////////////////////////////////////////

pub struct ArgBuilder<F=Identity> {
    callback: F,
    span: Span,
    id: ast::Ident,
}

impl ArgBuilder {
    pub fn new<I>(id: I) -> Self where I: ToIdent {
        ArgBuilder::new_with_callback(id, Identity)
    }
}

impl<F> ArgBuilder<F>
    where F: Invoke<ast::Arg>,
{
    pub fn new_with_callback<I>(id: I, callback: F) -> ArgBuilder<F>
        where I: ToIdent,
    {
        ArgBuilder {
            callback: callback,
            span: DUMMY_SP,
            id: id.to_ident(),
        }
    }

    pub fn span(mut self, span: Span) -> Self {
        self.span = span;
        self
    }

    pub fn build_ty(self, ty: P<ast::Ty>) -> F::Result {
        let path = respan(self.span, self.id);

        self.callback.invoke(ast::Arg {
            id: ast::DUMMY_NODE_ID,
            ty: ty,
            pat: P(ast::Pat {
                id: ast::DUMMY_NODE_ID,
                node: ast::PatIdent(
                    ast::BindByValue(ast::Mutability::MutImmutable),
                    path,
                    None,
                ),
                span: self.span,
            }),
        })
    }

    pub fn ty(self) -> TyBuilder<ArgTyBuilder<F>> {
        TyBuilder::new_with_callback(ArgTyBuilder(self))
    }
}

//////////////////////////////////////////////////////////////////////////////

pub struct ArgTyBuilder<F>(ArgBuilder<F>);

impl<F: Invoke<ast::Arg>> Invoke<P<ast::Ty>> for ArgTyBuilder<F>
{
    type Result = F::Result;

    fn invoke(self, ty: P<ast::Ty>) -> F::Result {
        self.0.build_ty(ty)
    }
}
<commit_msg>Add with_args to FnDeclBuilder<commit_after>use syntax::ast;
use syntax::codemap::{DUMMY_SP, Span, respan};
use syntax::ptr::P;

use ident::ToIdent;
use invoke::{Invoke, Identity};
use ty::TyBuilder;

//////////////////////////////////////////////////////////////////////////////

pub struct FnDeclBuilder<F=Identity> {
    callback: F,
    span: Span,
    args: Vec<ast::Arg>,
    variadic: bool,
}

impl FnDeclBuilder {
    pub fn new() -> FnDeclBuilder {
        FnDeclBuilder::new_with_callback(Identity)
    }
}

impl<F> FnDeclBuilder<F>
    where F: Invoke<P<ast::FnDecl>>,
{
    pub fn new_with_callback(callback: F) -> Self {
        FnDeclBuilder {
            callback: callback,
            span: DUMMY_SP,
            args: Vec::new(),
            variadic: false,
        }
    }

    pub fn span(mut self, span: Span) -> Self {
        self.span = span;
        self
    }

    pub fn variadic(mut self) -> Self {
        self.variadic = true;
        self
    }

    pub fn with_arg(mut self, arg: ast::Arg) -> Self {
        self.args.push(arg);
        self
    }

    pub fn with_args<I>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=ast::Arg>
    {
        self.args.extend(iter);
        self
    }

    pub fn arg<I>(self, id: I) -> ArgBuilder<Self>
        where I: ToIdent,
    {
        ArgBuilder::new_with_callback(id, self)
    }

    pub fn no_return(self) -> F::Result {
        let ret_ty = ast::FunctionRetTy::NoReturn(self.span);
        self.build(ret_ty)
    }

    pub fn default_return(self) -> F::Result {
        let ret_ty = ast::FunctionRetTy::DefaultReturn(self.span);
        self.build(ret_ty)
    }

    pub fn build_return(self, ty: P<ast::Ty>) -> F::Result {
        self.build(ast::FunctionRetTy::Return(ty))
    }

    pub fn return_(self) -> TyBuilder<Self> {
        TyBuilder::new_with_callback(self)
    }

    pub fn build(self, output: ast::FunctionRetTy) -> F::Result {
        self.callback.invoke(P(ast::FnDecl {
            inputs: self.args,
            output: output,
            variadic: self.variadic,
        }))
    }
}

impl<F> Invoke<ast::Arg> for FnDeclBuilder<F>
    where F: Invoke<P<ast::FnDecl>>
{
    type Result = Self;

    fn invoke(self, arg: ast::Arg) -> Self {
        self.with_arg(arg)
    }
}

impl<F> Invoke<P<ast::Ty>> for FnDeclBuilder<F>
    where F: Invoke<P<ast::FnDecl>>,
{
    type Result = F::Result;

    fn invoke(self, ty: P<ast::Ty>) -> F::Result {
        self.build_return(ty)
    }
}

//////////////////////////////////////////////////////////////////////////////

pub struct ArgBuilder<F=Identity> {
    callback: F,
    span: Span,
    id: ast::Ident,
}

impl ArgBuilder {
    pub fn new<I>(id: I) -> Self where I: ToIdent {
        ArgBuilder::new_with_callback(id, Identity)
    }
}

impl<F> ArgBuilder<F>
    where F: Invoke<ast::Arg>,
{
    pub fn new_with_callback<I>(id: I, callback: F) -> ArgBuilder<F>
        where I: ToIdent,
    {
        ArgBuilder {
            callback: callback,
            span: DUMMY_SP,
            id: id.to_ident(),
        }
    }

    pub fn span(mut self, span: Span) -> Self {
        self.span = span;
        self
    }

    pub fn build_ty(self, ty: P<ast::Ty>) -> F::Result {
        let path = respan(self.span, self.id);

        self.callback.invoke(ast::Arg {
            id: ast::DUMMY_NODE_ID,
            ty: ty,
            pat: P(ast::Pat {
                id: ast::DUMMY_NODE_ID,
                node: ast::PatIdent(
                    ast::BindByValue(ast::Mutability::MutImmutable),
                    path,
                    None,
                ),
                span: self.span,
            }),
        })
    }

    pub fn ty(self) -> TyBuilder<ArgTyBuilder<F>> {
        TyBuilder::new_with_callback(ArgTyBuilder(self))
    }
}

//////////////////////////////////////////////////////////////////////////////

pub struct ArgTyBuilder<F>(ArgBuilder<F>);

impl<F: Invoke<ast::Arg>> Invoke<P<ast::Ty>> for ArgTyBuilder<F>
{
    type Result = F::Result;

    fn invoke(self, ty: P<ast::Ty>) -> F::Result {
        self.0.build_ty(ty)
    }
}
<|endoftext|>


<commit_before>use std::collections::VecDeque;

use bytes::Buf;
use bytes::Bytes;

use futures::stream::Stream;
use futures::stream::StreamExt;

use crate::error::*;
use crate::proto::grpc_frame_parser::GrpcFrameParser;
use crate::result;
use futures::task::Context;
use httpbis::DataOrTrailers;
use httpbis::HttpStreamAfterHeaders;
use std::pin::Pin;
use std::task::Poll;

fn write_u32_be(v: u32) -> [u8; 4] {
    [
        (v >> 24) as u8,
        (v >> 16) as u8,
        (v >> 8) as u8,
        (v >> 0) as u8,
    ]
}

pub const GRPC_HEADER_LEN: usize = 5;

/// Return frame len
pub fn parse_grpc_frame_0(stream: &[u8]) -> result::Result<Option<usize>> {
    let mut stream_copy = stream;
    let len = match parse_grpc_frame_header(&mut stream_copy)? {
        Some(len) => len,
        None => return Ok(None),
    };

    let end = len as usize + GRPC_HEADER_LEN;
    if end > stream.len() {
        return Ok(None);
    }

    Ok(Some(len as usize))
}

pub fn parse_grpc_frame_header<B: Buf>(stream: &mut B) -> result::Result<Option<u32>> {
    if stream.remaining() < GRPC_HEADER_LEN {
        return Ok(None);
    }

    let compressed = match stream.get_u8() {
        0 => false,
        1 => true,
        _ => return Err(Error::Other("unknown compression flag")),
    };
    if compressed {
        return Err(Error::Other("compression is not implemented"));
    }
    Ok(Some(stream.get_u32()))
}

// return message and size consumed
pub fn parse_grpc_frame(stream: &[u8]) -> result::Result<Option<(&[u8], usize)>> {
    parse_grpc_frame_0(stream).map(|o| {
        o.map(|len| {
            (
                &stream[GRPC_HEADER_LEN..len + GRPC_HEADER_LEN],
                len + GRPC_HEADER_LEN,
            )
        })
    })
}

pub fn parse_grpc_frame_from_bytes(stream: &mut Bytes) -> result::Result<Option<Bytes>> {
    if let Some(len) = parse_grpc_frame_0(&stream)? {
        let r = stream.slice(GRPC_HEADER_LEN..len + GRPC_HEADER_LEN);
        stream.advance(len + GRPC_HEADER_LEN);
        Ok(Some(r))
    } else {
        Ok(None)
    }
}

/// Encode data into grpc frame (add frame prefix)
pub fn write_grpc_frame_cb<F, E>(stream: &mut Vec<u8>, estimate: u32, frame: F) -> Result<(), E>
where
    F: FnOnce(&mut Vec<u8>) -> Result<(), E>,
{
    stream.reserve(estimate as usize + GRPC_HEADER_LEN);

    stream.push(0); // compressed flag
    let size_pos = stream.len();
    stream.extend_from_slice(&[0, 0, 0, 0]); // len
    let frame_start = stream.len();
    frame(stream)?;
    let frame_size = stream.len() - frame_start;
    assert!(frame_size <= u32::max_value() as usize);
    stream[size_pos..size_pos + 4].copy_from_slice(&write_u32_be(frame_size as u32));
    Ok(())
}

trait RequestOrResponse {
    fn need_trailing_header() -> bool;
}

pub struct GrpcFrameFromHttpFramesStreamRequest {
    http_stream_stream: HttpStreamAfterHeaders,
    buf: GrpcFrameParser,
    parsed_frames: VecDeque<Bytes>,
}

impl GrpcFrameFromHttpFramesStreamRequest {
    pub fn _new(http_stream_stream: HttpStreamAfterHeaders) -> Self {
        GrpcFrameFromHttpFramesStreamRequest {
            http_stream_stream,
            buf: GrpcFrameParser::default(),
            parsed_frames: VecDeque::new(),
        }
    }
}

impl Stream for GrpcFrameFromHttpFramesStreamRequest {
    type Item = crate::Result<Bytes>;

    fn poll_next(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<Option<crate::Result<Bytes>>> {
        loop {
            let parsed_frames = self.buf.next_frames()?;

            self.parsed_frames.extend(parsed_frames);

            if let Some(frame) = self.parsed_frames.pop_front() {
                return Poll::Ready(Some(Ok(frame)));
            }

            let part_opt = match self.http_stream_stream.poll_next_unpin(cx)? {
                Poll::Pending => return Poll::Pending,
                Poll::Ready(part_opt) => part_opt,
            };
            let part = match part_opt {
                None => {
                    self.buf.check_empty()?;
                    return Poll::Ready(None);
                }
                Some(part) => part,
            };

            match part {
                // unexpected but OK
                DataOrTrailers::Trailers(..) => (),
                DataOrTrailers::Data(data, ..) => {
                    self.buf.enqueue(data);
                }
            }
        }
    }
}

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

    fn parse_grpc_frames_from_bytes(stream: &mut Bytes) -> result::Result<Vec<Bytes>> {
        let mut r = Vec::new();
        loop {
            match parse_grpc_frame_from_bytes(stream)? {
                Some(bytes) => {
                    r.push(bytes);
                }
                None => {
                    return Ok(r);
                }
            }
        }
    }

    #[test]
    fn test_parse_grpc_frame() {
        assert_eq!(None, parse_grpc_frame(b"").unwrap());
        assert_eq!(None, parse_grpc_frame(b"1").unwrap());
        assert_eq!(None, parse_grpc_frame(b"14sc").unwrap());
        assert_eq!(
            None,
            parse_grpc_frame(b"\x00\x00\x00\x00\x07\x0a\x05wo").unwrap()
        );
        assert_eq!(
            Some((&b"\x0a\x05world"[..], 12)),
            parse_grpc_frame(b"\x00\x00\x00\x00\x07\x0a\x05world").unwrap()
        );
    }

    #[test]
    fn test_parse_grpc_frames_from_bytes() {
        fn t(r: &[&[u8]], input: &[u8], trail: &[u8]) {
            let mut b = Bytes::new();
            bytes_extend_from_slice(&mut b, input);
            bytes_extend_from_slice(&mut b, trail);

            let r: Vec<Bytes> = r.into_iter().map(|&s| Bytes::copy_from_slice(s)).collect();

            let rr = parse_grpc_frames_from_bytes(&mut b).unwrap();
            assert_eq!(r, rr);
            assert_eq!(trail, b.as_ref());
        }

        t(&[], &[], &[]);
        t(&[], &[], &b"1"[..]);
        t(&[], &[], &b"14sc"[..]);
        t(
            &[&b"\x0a\x05world"[..]],
            &b"\x00\x00\x00\x00\x07\x0a\x05world"[..],
            &[],
        );
        t(
            &[&b"ab"[..], &b"cde"[..]],
            &b"\0\x00\x00\x00\x02ab\0\x00\x00\x00\x03cde"[..],
            &b"\x00"[..],
        );
    }

    #[test]
    fn test_write_grpc_frame_cb() {
        let mut f = Vec::new();
        write_grpc_frame_cb(&mut f, 0, |f| {
            Ok::<_, ()>(f.extend_from_slice(&[0x17, 0x19]))
        })
        .unwrap();
        assert_eq!(&b"\x00\x00\x00\x00\x02\x17\x19"[..], f.as_slice());
    }
}
<commit_msg>Use modern Rust functions<commit_after>use std::collections::VecDeque;

use bytes::Buf;
use bytes::Bytes;

use futures::stream::Stream;
use futures::stream::StreamExt;

use crate::error::*;
use crate::proto::grpc_frame_parser::GrpcFrameParser;
use crate::result;
use futures::task::Context;
use httpbis::DataOrTrailers;
use httpbis::HttpStreamAfterHeaders;
use std::pin::Pin;
use std::task::Poll;

pub const GRPC_HEADER_LEN: usize = 5;

/// Return frame len
pub fn parse_grpc_frame_0(stream: &[u8]) -> result::Result<Option<usize>> {
    let mut stream_copy = stream;
    let len = match parse_grpc_frame_header(&mut stream_copy)? {
        Some(len) => len,
        None => return Ok(None),
    };

    let end = len as usize + GRPC_HEADER_LEN;
    if end > stream.len() {
        return Ok(None);
    }

    Ok(Some(len as usize))
}

pub fn parse_grpc_frame_header<B: Buf>(stream: &mut B) -> result::Result<Option<u32>> {
    if stream.remaining() < GRPC_HEADER_LEN {
        return Ok(None);
    }

    let compressed = match stream.get_u8() {
        0 => false,
        1 => true,
        _ => return Err(Error::Other("unknown compression flag")),
    };
    if compressed {
        return Err(Error::Other("compression is not implemented"));
    }
    Ok(Some(stream.get_u32()))
}

// return message and size consumed
pub fn parse_grpc_frame(stream: &[u8]) -> result::Result<Option<(&[u8], usize)>> {
    parse_grpc_frame_0(stream).map(|o| {
        o.map(|len| {
            (
                &stream[GRPC_HEADER_LEN..len + GRPC_HEADER_LEN],
                len + GRPC_HEADER_LEN,
            )
        })
    })
}

pub fn parse_grpc_frame_from_bytes(stream: &mut Bytes) -> result::Result<Option<Bytes>> {
    if let Some(len) = parse_grpc_frame_0(&stream)? {
        let r = stream.slice(GRPC_HEADER_LEN..len + GRPC_HEADER_LEN);
        stream.advance(len + GRPC_HEADER_LEN);
        Ok(Some(r))
    } else {
        Ok(None)
    }
}

/// Encode data into grpc frame (add frame prefix)
pub fn write_grpc_frame_cb<F, E>(stream: &mut Vec<u8>, estimate: u32, frame: F) -> Result<(), E>
where
    F: FnOnce(&mut Vec<u8>) -> Result<(), E>,
{
    stream.reserve(estimate as usize + GRPC_HEADER_LEN);

    stream.push(0); // compressed flag
    let size_pos = stream.len();
    stream.extend_from_slice(&[0, 0, 0, 0]); // len
    let frame_start = stream.len();
    frame(stream)?;
    let frame_size = stream.len() - frame_start;
    assert!(frame_size <= u32::max_value() as usize);
    stream[size_pos..size_pos + 4].copy_from_slice(&(frame_size as u32).to_be_bytes());
    Ok(())
}

trait RequestOrResponse {
    fn need_trailing_header() -> bool;
}

pub struct GrpcFrameFromHttpFramesStreamRequest {
    http_stream_stream: HttpStreamAfterHeaders,
    buf: GrpcFrameParser,
    parsed_frames: VecDeque<Bytes>,
}

impl GrpcFrameFromHttpFramesStreamRequest {
    pub fn _new(http_stream_stream: HttpStreamAfterHeaders) -> Self {
        GrpcFrameFromHttpFramesStreamRequest {
            http_stream_stream,
            buf: GrpcFrameParser::default(),
            parsed_frames: VecDeque::new(),
        }
    }
}

impl Stream for GrpcFrameFromHttpFramesStreamRequest {
    type Item = crate::Result<Bytes>;

    fn poll_next(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<Option<crate::Result<Bytes>>> {
        loop {
            let parsed_frames = self.buf.next_frames()?;

            self.parsed_frames.extend(parsed_frames);

            if let Some(frame) = self.parsed_frames.pop_front() {
                return Poll::Ready(Some(Ok(frame)));
            }

            let part_opt = match self.http_stream_stream.poll_next_unpin(cx)? {
                Poll::Pending => return Poll::Pending,
                Poll::Ready(part_opt) => part_opt,
            };
            let part = match part_opt {
                None => {
                    self.buf.check_empty()?;
                    return Poll::Ready(None);
                }
                Some(part) => part,
            };

            match part {
                // unexpected but OK
                DataOrTrailers::Trailers(..) => (),
                DataOrTrailers::Data(data, ..) => {
                    self.buf.enqueue(data);
                }
            }
        }
    }
}

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

    fn parse_grpc_frames_from_bytes(stream: &mut Bytes) -> result::Result<Vec<Bytes>> {
        let mut r = Vec::new();
        loop {
            match parse_grpc_frame_from_bytes(stream)? {
                Some(bytes) => {
                    r.push(bytes);
                }
                None => {
                    return Ok(r);
                }
            }
        }
    }

    #[test]
    fn test_parse_grpc_frame() {
        assert_eq!(None, parse_grpc_frame(b"").unwrap());
        assert_eq!(None, parse_grpc_frame(b"1").unwrap());
        assert_eq!(None, parse_grpc_frame(b"14sc").unwrap());
        assert_eq!(
            None,
            parse_grpc_frame(b"\x00\x00\x00\x00\x07\x0a\x05wo").unwrap()
        );
        assert_eq!(
            Some((&b"\x0a\x05world"[..], 12)),
            parse_grpc_frame(b"\x00\x00\x00\x00\x07\x0a\x05world").unwrap()
        );
    }

    #[test]
    fn test_parse_grpc_frames_from_bytes() {
        fn t(r: &[&[u8]], input: &[u8], trail: &[u8]) {
            let mut b = Bytes::new();
            bytes_extend_from_slice(&mut b, input);
            bytes_extend_from_slice(&mut b, trail);

            let r: Vec<Bytes> = r.into_iter().map(|&s| Bytes::copy_from_slice(s)).collect();

            let rr = parse_grpc_frames_from_bytes(&mut b).unwrap();
            assert_eq!(r, rr);
            assert_eq!(trail, b.as_ref());
        }

        t(&[], &[], &[]);
        t(&[], &[], &b"1"[..]);
        t(&[], &[], &b"14sc"[..]);
        t(
            &[&b"\x0a\x05world"[..]],
            &b"\x00\x00\x00\x00\x07\x0a\x05world"[..],
            &[],
        );
        t(
            &[&b"ab"[..], &b"cde"[..]],
            &b"\0\x00\x00\x00\x02ab\0\x00\x00\x00\x03cde"[..],
            &b"\x00"[..],
        );
    }

    #[test]
    fn test_write_grpc_frame_cb() {
        let mut f = Vec::new();
        write_grpc_frame_cb(&mut f, 0, |f| {
            Ok::<_, ()>(f.extend_from_slice(&[0x17, 0x19]))
        })
        .unwrap();
        assert_eq!(&b"\x00\x00\x00\x00\x02\x17\x19"[..], f.as_slice());
    }
}
<|endoftext|>