<commit_before>use ws;
use uuid::Uuid;
use std::rc::Rc;
use lib::ParseFrom;
use super::{Item,Action};

#[derive(Debug)]
pub struct MsgCell<T,U> {
    pub val:  T,
    pub next: U,
}

#[derive(Debug)]
pub struct UnitMsgCell<T> {
    pub val: T,
}

#[derive(Debug)]
struct PendingMsg<T> {
    pending: T,
}

#[derive(Debug)]
struct PartialMsg<T,P> {
    parsed:  P,
    pending: Option<T>,
}

impl PendingMsg <ws::Message> {
    fn new (msg: ws::Message) -> Self {
        PendingMsg {
            pending: msg,
        }
    }
}

// TODO(jordan): investigate whether clone is necessary
impl <T: Clone> PendingMsg <T> {
    fn parse_next <F,Tnew,V> (&self, parser: F)
        -> Result<PartialMsg<Tnew,Rc<UnitMsgCell<V>>>, String>
        where F: Fn(Option<T>) -> Result<(V, Option<Tnew>), String> {
        parser(Some(self.pending.clone()))
            .map(|(newly_parsed, still_pending)| {
                PartialMsg {
                    parsed: Rc::new(UnitMsgCell {
                        val: newly_parsed,
                    }),
                    pending: still_pending,
                }
            })
    }
}

impl <T: Clone,P> PartialMsg <T,Rc<P>> {
    fn parse_next <F,Tnew,V> (&self, parser: F)
        -> Result<PartialMsg<Tnew,Rc<MsgCell<V,Rc<P>>>>, String>
        where F: Fn(Option<T>) -> Result<(V, Option<Tnew>), String> {
        parser(self.pending.clone())
            .map(|(newly_parsed, still_pending)| {
                PartialMsg {
                    parsed: Rc::new(MsgCell {
                        next: self.parsed.clone(),
                        val:  newly_parsed,
                    }),
                    pending: still_pending,
                }
            })
    }
}

fn parse_ticket (msg: Option<ws::Message>) -> Result<(Uuid, Option<String>), String> {
    fn parse_error_msg (reason: &str) -> String {
        format!("parse_ticket failure: {}", reason)
    }

    msg.ok_or(parse_error_msg("there is no message"))
        ?.into_text()
        .or(Err(parse_error_msg("cannot get text from Message; is it a binary Message?")))
        ?.splitn(2, ':')
        .map(|p| p.to_string())
        .collect::<Vec<String>>()
        .split_first()
        .ok_or(parse_error_msg("no ticket"))
        .map(|(ticket_string, msg_remainder)| {
            Uuid::parse_str(ticket_string)
                .or(Err(parse_error_msg("ticket is invalid uuidv4")))
                .map(|uuid| (uuid, Vec::from(msg_remainder).pop()))
        })?
}

fn parse_action (action_string: Option<String>) -> Result<(Action, Option<String>), String> {
    let mut action_string: String = action_string
        .ok_or(String::from("parse_action failure: got None for action_string"))?;

    let left_paren: usize = action_string
        .find("(")
        .ok_or(String::from("parse_action failure: could not find \"(\""))?;

    let action_type: String = action_string.drain(..left_paren).collect();

    let right_paren: usize = action_string
        .find(")")
        .ok_or(String::from("parse_action failure: could not find \")\""))?;

    let mut action_parameters: Vec<String> = action_string
        .drain(1..right_paren)
        .collect::<String>()
        .split(",")
        .map(|s| s.to_string())
        .filter(|s| s.len() > 0)
        .collect();

    let remainder: Option<String> = if action_string.len() > 0 {
        Some(action_string)
    } else {
        None
    };

    if action_type.len() == 0 {
        Err(String::from("missing action type"))
    } else {
        match action_type.as_str() {
            "addItemToInventory" => {
                action_parameters.pop()
                    .ok_or(String::from("addItemToInventory: no item"))
                    .and_then(|item_str| {
                        item_str.parse::<i32>()
                            .map_err(|_err| String::from("invalid i32"))
                    })
                    .and_then(|item_code| Item::parse(item_code))
                    .map(|item| (Action::addItemToInventory(item), remainder))
            },
            _ => Err(String::from(format!("unrecognized action {}", action_type)))
        }
    }
}

pub type ActionMsg = Rc<MsgCell<Action,Rc<UnitMsgCell<Uuid>>>>;

pub fn parse (msg: ws::Message) -> Result<ActionMsg, String> {
    PendingMsg::new(msg)
        .parse_next(parse_ticket)
        ?.parse_next(parse_action)
        .map(|partial_msg| partial_msg.parsed)
}
<commit_msg>Optimize: parse_next consumes its {Partial,Pending}Msg<commit_after>use ws;
use uuid::Uuid;
use std::rc::Rc;
use lib::ParseFrom;
use super::{Item,Action};

#[derive(Debug)]
pub struct MsgCell<T,U> {
    pub val:  T,
    pub next: U,
}

#[derive(Debug)]
pub struct UnitMsgCell<T> {
    pub val: T,
}

#[derive(Debug)]
struct PendingMsg<T> {
    pending: T,
}

#[derive(Debug)]
struct PartialMsg<T,P> {
    parsed:  P,
    pending: Option<T>,
}

impl PendingMsg <ws::Message> {
    fn new (msg: ws::Message) -> Self {
        PendingMsg {
            pending: msg,
        }
    }
}

// NOTE(jordan): after parse_next the old PendingMsg is dropped.
impl <T> PendingMsg <T> {
    fn parse_next <F,Tnew,V> (self, parser: F)
        -> Result<PartialMsg<Tnew,Rc<UnitMsgCell<V>>>, String>
        where F: Fn(Option<T>) -> Result<(V, Option<Tnew>), String> {
        parser(Some(self.pending))
            .map(|(newly_parsed, still_pending)| {
                PartialMsg {
                    parsed: Rc::new(UnitMsgCell {
                        val: newly_parsed,
                    }),
                    pending: still_pending,
                }
            })
    }
}

impl <T,P> PartialMsg <T,Rc<P>> {
    fn parse_next <F,Tnew,V> (self, parser: F)
        -> Result<PartialMsg<Tnew,Rc<MsgCell<V,Rc<P>>>>, String>
        where F: Fn(Option<T>) -> Result<(V, Option<Tnew>), String> {
        let parsed = self.parsed;
        parser(self.pending)
            .map(|(newly_parsed, still_pending)| {
                PartialMsg {
                    parsed: Rc::new(MsgCell {
                        next: parsed,
                        val:  newly_parsed,
                    }),
                    pending: still_pending,
                }
            })
    }
}

fn parse_ticket (msg: Option<ws::Message>) -> Result<(Uuid, Option<String>), String> {
    fn parse_error_msg (reason: &str) -> String {
        format!("parse_ticket failure: {}", reason)
    }

    msg.ok_or(parse_error_msg("there is no message"))
        ?.into_text()
        .or(Err(parse_error_msg("cannot get text from Message; is it a binary Message?")))
        ?.splitn(2, ':')
        .map(|p| p.to_string())
        .collect::<Vec<String>>()
        .split_first()
        .ok_or(parse_error_msg("no ticket"))
        .map(|(ticket_string, msg_remainder)| {
            Uuid::parse_str(ticket_string)
                .or(Err(parse_error_msg("ticket is invalid uuidv4")))
                .map(|uuid| (uuid, Vec::from(msg_remainder).pop()))
        })?
}

fn parse_action (action_string: Option<String>) -> Result<(Action, Option<String>), String> {
    let mut action_string: String = action_string
        .ok_or(String::from("parse_action failure: got None for action_string"))?;

    let left_paren: usize = action_string
        .find("(")
        .ok_or(String::from("parse_action failure: could not find \"(\""))?;

    let action_type: String = action_string.drain(..left_paren).collect();

    let right_paren: usize = action_string
        .find(")")
        .ok_or(String::from("parse_action failure: could not find \")\""))?;

    let mut action_parameters: Vec<String> = action_string
        .drain(1..right_paren)
        .collect::<String>()
        .split(",")
        .map(|s| s.to_string())
        .filter(|s| s.len() > 0)
        .collect();

    let remainder: Option<String> = if action_string.len() > 0 {
        Some(action_string)
    } else {
        None
    };

    if action_type.len() == 0 {
        Err(String::from("missing action type"))
    } else {
        match action_type.as_str() {
            "addItemToInventory" => {
                action_parameters.pop()
                    .ok_or(String::from("addItemToInventory: no item"))
                    .and_then(|item_str| {
                        item_str.parse::<i32>()
                            .map_err(|_err| String::from("invalid i32"))
                    })
                    .and_then(|item_code| Item::parse(item_code))
                    .map(|item| (Action::addItemToInventory(item), remainder))
            },
            _ => Err(String::from(format!("unrecognized action {}", action_type)))
        }
    }
}

pub type ActionMsg = Rc<MsgCell<Action,Rc<UnitMsgCell<Uuid>>>>;

pub fn parse (msg: ws::Message) -> Result<ActionMsg, String> {
    PendingMsg::new(msg)
        .parse_next(parse_ticket)
        ?.parse_next(parse_action)
        .map(|partial_msg| partial_msg.parsed)
}
<|endoftext|>


<commit_before>extern crate robin_core;

#[cfg(test)]
mod parser_tests {
    use robin_core::parser;
    use robin_core::ast::Expression;

    #[test]
    fn booleans_should_return_a_boolean_node() {
        assert_eq!(parser::parse("true"), Ok(Expression::Boolean(true)))
    }

    #[test]
    fn identifiers_that_look_like_booleans_should_return_identifiers() {
        assert_eq!(parser::parse("truedog"),
                   Ok(Expression::Identifier("truedog".to_string())))
    }

    #[test]
    fn identifiers_should_return_a_identifier_node() {
        assert_eq!(parser::parse("hello"),
                   Ok(Expression::Identifier("hello".to_string())))
    }

    #[test]
    fn decimals_should_return_a_number_node() {
        assert_eq!(parser::parse("47"), Ok(Expression::Number(47.0)))
    }

    #[test]
    fn exponents_should_return_a_number_node() {
        assert_eq!(parser::parse("5e1"), Ok(Expression::Number(50.0)))
    }

    #[test]
    fn binary_should_return_a_number_node() {
        assert_eq!(parser::parse("0b1"), Ok(Expression::Number(1.0)))
    }

    #[test]
    fn octal_should_return_a_number_node() {
        assert_eq!(parser::parse("0o2"), Ok(Expression::Number(2.0)))
    }

    #[test]
    fn hexadecimal_should_return_a_number_node() {
        assert_eq!(parser::parse("0xA"), Ok(Expression::Number(10.0)))
    }

    #[test]
    fn list_should_return_a_list_node() {
        let expr = Expression::List(vec![Box::new(Expression::Boolean(true))]);

        assert_eq!(parser::parse("[true]"), Ok(expr));
    }

    #[test]
    fn function_literal_should_return_a_function_literal_node() {
        let expr = Expression::FuncLiteral("example".to_string(),
                                           vec!["x".to_string()],
                                           Box::new(Expression::Boolean(true)));

        assert_eq!(parser::parse("(example [x] true)"), Ok(expr));
    }

    #[test]
    fn function_literal_with_multiple_arguments() {
        let expr = Expression::FuncLiteral("example".to_string(),
                                           vec!["x".to_string(), "y".to_string()],
                                           Box::new(Expression::Boolean(true)));

        assert_eq!(parser::parse("(example [x y] true)"), Ok(expr));
    }

    #[test]
    fn function_call_should_return_a_function_call_node() {
        let expr = Expression::FuncCall("example".to_string(),
                                        vec![Box::new(Expression::Boolean(true))]);

        assert_eq!(parser::parse("(example true)"), Ok(expr));
    }

    #[test]
    fn function_call_with_multiple_arguments() {
        let expr = Expression::FuncCall("example".to_string(),
                                        vec![Box::new(Expression::Boolean(true)),
                                             Box::new(Expression::Boolean(false))]);

        assert_eq!(parser::parse("(example true false)"), Ok(expr));
    }
}
<commit_msg>Update tests<commit_after>extern crate robin_core;

#[cfg(test)]
mod parser_tests {
    use robin_core::parser;
    use robin_core::ast::Expression;
    use robin_core::error::{Error, ErrorLevel, ErrorKind};

    #[test]
    fn booleans_should_return_a_boolean_node() {
        assert_eq!(parser::parse("true"), Ok(Expression::Boolean(true)))
    }

    #[test]
    fn identifiers_that_look_like_booleans_should_return_identifiers() {
        assert_eq!(parser::parse("truedog"),
                   Ok(Expression::Identifier("truedog".to_string())))
    }

    #[test]
    fn identifiers_should_return_a_identifier_node() {
        assert_eq!(parser::parse("hello"),
                   Ok(Expression::Identifier("hello".to_string())))
    }

    #[test]
    fn decimals_should_return_a_number_node() {
        assert_eq!(parser::parse("47"), Ok(Expression::Number(47.0)))
    }

    #[test]
    fn exponents_should_return_a_number_node() {
        assert_eq!(parser::parse("5e1"), Ok(Expression::Number(50.0)))
    }

    #[test]
    fn binary_should_return_a_number_node() {
        assert_eq!(parser::parse("0b1"), Ok(Expression::Number(1.0)))
    }

    #[test]
    fn octal_should_return_a_number_node() {
        assert_eq!(parser::parse("0o2"), Ok(Expression::Number(2.0)))
    }

    #[test]
    fn hexadecimal_should_return_a_number_node() {
        assert_eq!(parser::parse("0xA"), Ok(Expression::Number(10.0)))
    }

    #[test]
    fn list_should_return_a_list_node() {
        let expr = Expression::List(vec![Box::new(Expression::Boolean(true))]);

        assert_eq!(parser::parse("[true]"), Ok(expr));
    }

    #[test]
    fn function_literal_should_return_a_function_literal_node() {
        let expr = Expression::FuncLiteral("example".to_string(),
                                           vec!["x".to_string()],
                                           Box::new(Expression::Boolean(true)));

        assert_eq!(parser::parse("(example [x] true)"), Ok(expr));
    }

    #[test]
    fn function_literal_with_multiple_arguments() {
        let expr = Expression::FuncLiteral("example".to_string(),
                                           vec!["x".to_string(), "y".to_string()],
                                           Box::new(Expression::Boolean(true)));

        assert_eq!(parser::parse("(example [x y] true)"), Ok(expr));
    }

    #[test]
    fn function_call_should_return_a_function_call_node() {
        let expr = Expression::FuncCall("example".to_string(),
                                        vec![Box::new(Expression::Boolean(true))]);

        assert_eq!(parser::parse("(example true)"), Ok(expr));
    }

    #[test]
    fn function_call_with_multiple_arguments() {
        let expr = Expression::FuncCall("example".to_string(),
                                        vec![Box::new(Expression::Boolean(true)),
                                             Box::new(Expression::Boolean(false))]);

        assert_eq!(parser::parse("(example true false)"), Ok(expr));
    }
}
<|endoftext|>


<commit_before>#![crate_id="CSVProvider#0.1-pre"]
#![crate_type="dylib"]

// why doesn't "lib" work?

// rustc 0.11.0-pre (b47f2226a25654c5b781d27a91f2fa5274b3a347 2014-06-28 14:31:37 +0000)

#![feature(globs, macro_rules, quote, managed_boxes, plugin_registrar)]

#![allow(unused_imports)]
#![allow(unused_variable)]

extern crate syntax;
extern crate rustc;

use syntax::ast::{Name,
                  TokenTree,
                  Expr,
                  ExprLit,
                  ExprVec,
                  LitStr,
                  DUMMY_NODE_ID,
                  MutImmutable};
use syntax::codemap::Span;
use syntax::ext::base::{SyntaxExtension,
                        BasicMacroExpander,
                        ExtCtxt,
                        MacResult,
                        MacPat,
                        MacItem,
                        DummyResult,
                        MacExpr,
                        NormalTT};
use std::gc::Gc;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{InternedString, COMMA, EOF};

use std::io;
use std::io::fs;
use std::os;
use std::str;
use std::io::Process;
use syntax::ast;
use syntax::ast::Name;

use syntax::util::small_vector::SmallVector;

use std::collections::HashMap;
use std::gc::{Gc, GC};

use rustc::plugin::Registry;

#[plugin_registrar]
pub fn macro_registrar(reg: &mut Registry) {
    reg.register_macro("ProvideCSV_labels", provide_csv_given_labels);
}

#[deriving(Clone)]
struct Entry {
    str: InternedString,
    expr: Gc<Expr>
}

// see https://github.com/sfackler/syntax-ext-talk/blob/gh-pages/simple-ext/lib.rs
fn parse_entries(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
    let mut parser = parse::new_parser_from_tts(
         cx.parse_sess(),
         cx.cfg(),
         tts.iter()
            .map(|x| (*x).clone())
            .collect()
    );

    let mut entries: Vec<Entry> = Vec::new();

    let mut error = false;
    while parser.token != EOF {
        let entry = parser.parse_expr();

        let entry_str = match entry.node {
            ExprLit(lit) => {
                match lit.node {
                    LitStr(ref s, _) => s.clone(),
                    _ => {
                        cx.span_err(entry.span, "expected string literal");
                        error = true;
                        InternedString::new("")
                    }
                }
            }
            _ => {
                cx.span_err(entry.span, "expected string literal");
                error = true;
                InternedString::new("")
            }
        };

        entries.push(Entry { str: entry_str, expr: entry });

        if !parser.eat(&COMMA) && parser.token != EOF {
            cx.span_err(parser.span, "expected `,`");
            return None;
        }
    }

    if error {
        return None;
    }

    Some(entries)
}

/// A convenience type for macros that return a single item.
pub struct MacItems {
    items: Vec<Gc<ast::Item>>
}

impl MacItems {
    pub fn new(items: Vec<Gc<ast::Item>>) -> Box<MacResult> {
        box MacItems { items: items } as Box<MacResult>
    }
}
impl MacResult for MacItems {
    fn make_items(&self) -> Option<SmallVector<Gc<ast::Item>>> {
        Some(SmallVector::many(self.items.clone()))
    }
}

fn provide_csv_given_labels(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> {
   let mut entries = match parse_entries(cx, tts) {
      Some(entries) => entries,
      None => return DummyResult::expr(sp),
   };

   let name   = entries.shift().expect("should be given a type name").str;
   let path   = entries.shift().expect("should be given a CSV file path").str;
   let labels = entries.shift().expect("should be given column labels").str;

   println!("provide_csv_given_labels: name:   {}", name);
   println!("provide_csv_given_labels: path:   {}", path);
   println!("provide_csv_given_labels: labels: {}", labels);

   // PENDING
   // read the CSV and try to get the type of data in it
   // create a type with
   //  * this name,
   //  * these labeled columns,
   //  * discovered type of data
   //  * a constructor which reads the whole file

/* OLD
   let banana = "YAY! BANANA!";
   return MacExpr::new(quote_expr!(cx, {let x: $banana = 44}));
 */

   fn define_my_csv(cx0: &mut ExtCtxt) -> Option<Gc<syntax::ast::Item>> {
      let item1: Option<Gc<syntax::ast::Item>>  = quote_item!(cx0,
         pub struct MyCSV {
            pub data: Vec<(String)>,
         }
      );
      return item1;
   }

   let item1 = define_my_csv(cx);  // Why is this necessary?

   let item2: Option<Gc<syntax::ast::Item>>  = quote_item!(cx,
      impl $name {
         pub fn new() -> MyCSV {  // BUG: note removing the { leads to goofy error messsages
            println!("HMMMMMM.");
            return MyCSV {
               data: (vec!["zero".to_string(), "one".to_string(), "two".to_string()]),
            };
         }
      }
   );

   let items = vec![item1.expect("Should be able to construct MyCSV."),
                    item2.expect("Should be able to construct MyCSV.")];
   return MacItems::new(items);
}

<commit_msg>cleanup<commit_after>#![crate_id="CSVProvider#0.1-pre"]
#![crate_type="dylib"]

// why doesn't "lib" work?

// rustc 0.11.0-pre (b47f2226a25654c5b781d27a91f2fa5274b3a347 2014-06-28 14:31:37 +0000)

#![feature(globs, macro_rules, quote, managed_boxes, plugin_registrar)]

#![allow(unused_imports)]
#![allow(unused_variable)]

extern crate syntax;
extern crate rustc;

use syntax::ast::{
   Name,
   TokenTree,
   Expr,
   ExprLit,
   ExprVec,
   LitStr,
   DUMMY_NODE_ID,
   MutImmutable
};
use syntax::codemap::Span;
use syntax::ext::base::{
   SyntaxExtension,
   BasicMacroExpander,
   ExtCtxt,
   MacResult,
   MacPat,
   MacItem,
   DummyResult,
   MacExpr,
   NormalTT
};
use std::gc::Gc;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{InternedString, COMMA, EOF};

use std::io;
use std::io::fs;
use std::os;
use std::str;
use std::io::Process;
use syntax::ast;
use syntax::ast::Name;

use syntax::util::small_vector::SmallVector;

use std::collections::HashMap;
use std::gc::{Gc, GC};

use rustc::plugin::Registry;

#[plugin_registrar]
pub fn macro_registrar(reg: &mut Registry) {
   reg.register_macro("ProvideCSV_labels", provide_csv_given_labels);
}

#[deriving(Clone)]
struct Entry {
   str: InternedString,
   expr: Gc<Expr>
}

// see https://github.com/sfackler/syntax-ext-talk/blob/gh-pages/simple-ext/lib.rs
fn parse_entries(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
   let mut parser = parse::new_parser_from_tts(
       cx.parse_sess(),
       cx.cfg(),
       tts.iter()
          .map(|x| (*x).clone())
          .collect()
   );

   let mut entries: Vec<Entry> = Vec::new();

   let mut error = false;
   while parser.token != EOF {
      let entry = parser.parse_expr();

      let entry_str = match entry.node {
         ExprLit(lit) => {
            match lit.node {
               LitStr(ref s, _) => s.clone(),
               _ => {
                  cx.span_err(entry.span, "expected string literal");
                  error = true;
                  InternedString::new("")
                }
            }
         }
         _ => {
                cx.span_err(entry.span, "expected string literal");
                error = true;
                InternedString::new("")
         }
      };

      entries.push(Entry { str: entry_str, expr: entry });

      if !parser.eat(&COMMA) && parser.token != EOF {
         cx.span_err(parser.span, "expected `,`");
         return None;
      }
    }

   if error {
      return None;
   }

   Some(entries)
}

/// A convenience type for macros that return a single item.
pub struct MacItems {
   items: Vec<Gc<ast::Item>>
}

impl MacItems {
   pub fn new(items: Vec<Gc<ast::Item>>) -> Box<MacResult> {
      box MacItems { items: items } as Box<MacResult>
   }
}
impl MacResult for MacItems {
   fn make_items(&self) -> Option<SmallVector<Gc<ast::Item>>> {
      Some(SmallVector::many(self.items.clone()))
   }
}

fn provide_csv_given_labels(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> {
   let mut entries = match parse_entries(cx, tts) {
      Some(entries) => entries,
      None => return DummyResult::expr(sp),
   };

   let name   = entries.shift().expect("should be given a type name").str;
   let path   = entries.shift().expect("should be given a CSV file path").str;
   let labels = entries.shift().expect("should be given column labels").str;

   println!("provide_csv_given_labels: name:   {}", name);
   println!("provide_csv_given_labels: path:   {}", path);
   println!("provide_csv_given_labels: labels: {}", labels);

   // PENDING
   // read the CSV and try to get the type of data in it
   // create a type with
   //  * this name,
   //  * these labeled columns,
   //  * discovered type of data
   //  * a constructor which reads the whole file

/* OLD
   let banana = "YAY! BANANA!";
   return MacExpr::new(quote_expr!(cx, {let x: $banana = 44}));
 */

   fn define_my_csv(cx0: &mut ExtCtxt) -> Option<Gc<syntax::ast::Item>> {
      let item1: Option<Gc<syntax::ast::Item>>  = quote_item!(cx0,
         pub struct MyCSV {
            pub data: Vec<(String)>,
         }
      );
      return item1;
   }

   let item1 = define_my_csv(cx);  // Why is this necessary?

   let item2: Option<Gc<syntax::ast::Item>>  = quote_item!(cx,
      impl $name {
         pub fn new() -> MyCSV {  // BUG: note removing the { leads to goofy error messsages
            println!("HMMMMMM.");
            return MyCSV {
               data: (vec!["zero".to_string(), "one".to_string(), "two".to_string()]),
            };
         }
      }
   );

   let items = vec![item1.expect("Should be able to construct MyCSV."),
                    item2.expect("Should be able to construct MyCSV.")];
   return MacItems::new(items);
}

<|endoftext|>


<commit_before>//! The instruction selector
//!
//! Note: For now this will be the simplest instruction selector possible.
//!       I'll be able to improve it later, but I need something that works
//!       before I can get there.

// TODO: Finish Instruction Selection
// TODO: Write assembly pretty printer
// TODO: Add tests

use std::collections::HashMap;
use ::Ident;
use back::machine::{Address, Instruction, MachineCode, MachineRegister, NativeInt, REGISTER_COUNT};
use middle::ir;

struct InstructionSelector<'a> {
    ir: &'a ir::Program,
    code: MachineCode,
    globals: HashMap<Ident, usize>,
}

impl<'a> InstructionSelector<'a> {
    fn new(ir: &'a ir::Program) -> InstructionSelector<'a> {
        InstructionSelector {
            ir: ir,
            code: MachineCode::new(),
            globals: HashMap::new(),
        }
    }

    fn init_global(&mut self, name: &Ident, value: NativeInt, offset: usize) {
        let address = offset + REGISTER_COUNT - 1;

        // Store the offset
        self.globals.insert(*name, address);

        // Emit initialization
        self.code.emit(instruction!(MOV [Address::Immediate(address as NativeInt)] value))
    }

    fn trans_fn(&mut self, name: &Ident, body: &[ir::Block], args: &[Ident]) {
        //
    }

    fn translate(mut self) -> MachineCode {
        // First, initialize global variables
        for (offset, symbol) in self.ir.iter().enumerate() {
            if let ir::Symbol::Global { ref name, ref value } = *symbol {
                self.init_global(name, value.val() as NativeInt, offset);
            }
        }

        // Then initialize the stack management registers
        let tos = (REGISTER_COUNT + self.globals.len()) as NativeInt - 1;  // Top of stack
        self.code.emit(instruction!(MOV [Address::MachineRegister(MachineRegister::BP)] tos));
        self.code.emit(instruction!(MOV [Address::MachineRegister(MachineRegister::SP)] tos));

        // Emit a JMP to main
        self.code.emit(instruction!(JMP Ident::new("main")));

        // Translate all functions
        for symbol in self.ir {
            if let ir::Symbol::Function { ref name, ref body, ref args } = *symbol {
                self.trans_fn(name, body, args);
            }
        }

        self.code
    }
}

pub fn select_instructions(ir: &ir::Program) -> MachineCode {
    let is = InstructionSelector::new(ir);
    is.translate()
}<commit_msg>Add basic idea for instruction selection<commit_after>//! The instruction selector
//!
//! Note: For now this will be the simplest instruction selector possible.
//!       I'll be able to improve it later, but I need something that works
//!       before I can get there.

// TODO: Finish Instruction Selection
// TODO: Write assembly pretty printer
// TODO: Add tests

use std::collections::HashMap;
use ::Ident;
use back::machine::{Address, Instruction, MachineCode, MachineRegister, NativeInt, REGISTER_COUNT};
use middle::ir;

struct InstructionSelector<'a> {
    ir: &'a ir::Program,
    code: MachineCode,
    globals: HashMap<Ident, usize>,
}

impl<'a> InstructionSelector<'a> {
    fn new(ir: &'a ir::Program) -> InstructionSelector<'a> {
        InstructionSelector {
            ir: ir,
            code: MachineCode::new(),
            globals: HashMap::new(),
        }
    }

    fn init_global(&mut self, name: &Ident, value: NativeInt, offset: usize) {
        let address = offset + REGISTER_COUNT - 1;

        // Store the offset
        self.globals.insert(*name, address);

        // Emit initialization
        self.code.emit(instruction!(MOV [Address::Immediate(address as NativeInt)] value))
    }

    fn trans_fn(&mut self, name: &Ident, body: &[ir::Block], args: &[Ident]) {
        // TODO: Generate the prologue

        for block in body {
            for inst in block.inst {
                // TODO: Translate instruction
                /* IDEA:
                let asm_instr = match *inst {
                    ir::Instruction::BinOp { op, lhs, rhs, dst } => {
                        match op {
                            ir::InfixOp::Add => instruction!(ADD ...),
                            ...
                        }
                    },
                    ir::Instruction::UnOp { op, item, dst } => { ... },
                    ...
                }
                */
            }

            // TODO: Translate closing instruction
        }

        // TODO: Generate the epilogue
    }

    fn translate(mut self) -> MachineCode {
        // First, initialize global variables
        for (offset, symbol) in self.ir.iter().enumerate() {
            if let ir::Symbol::Global { ref name, ref value } = *symbol {
                self.init_global(name, value.val() as NativeInt, offset);
            }
        }

        // Then initialize the stack management registers
        let tos = (REGISTER_COUNT + self.globals.len()) as NativeInt - 1;  // Top of stack
        self.code.emit(instruction!(MOV [Address::MachineRegister(MachineRegister::BP)] tos));
        self.code.emit(instruction!(MOV [Address::MachineRegister(MachineRegister::SP)] tos));

        // Execute the main method and then halt
        self.code.emit(instruction!(JMP Ident::new("main")));
        self.code.emit(instruction!(HALT));

        // Translate all functions
        for symbol in self.ir {
            if let ir::Symbol::Function { ref name, ref body, ref args } = *symbol {
                self.trans_fn(name, body, args);
            }
        }

        self.code
    }
}

pub fn select_instructions(ir: &ir::Program) -> MachineCode {
    let is = InstructionSelector::new(ir);
    is.translate()
}
<|endoftext|>


<commit_before>//! Functions to support processing request/response bodies

use std::path::Path;

use bytes::Bytes;
use log::*;
use maplit::*;
use serde_json::{Map, Value};

use pact_matching::models::{OptionalBody, Request, Response};
use pact_matching::models::generators::{Generator, GeneratorCategory, Generators};
use pact_matching::models::json_utils::{json_to_num, json_to_string};
use pact_matching::models::matchingrules::{MatchingRule, MatchingRuleCategory, RuleLogic};

const CONTENT_TYPE_HEADER: &str = "Content-Type";

/// Process an array with embedded matching rules and generators
pub fn process_array(
  array: &[Value],
  matching_rules: &mut MatchingRuleCategory,
  generators: &mut Generators,
  path: &str,
  type_matcher: bool,
  skip_matchers: bool
) -> Value {
  Value::Array(array.iter().enumerate().map(|(index, val)| {
    let updated_path = if type_matcher {
      format!("{}[*]", path)
    } else {
      format!("{}[{}]", path, index)
    };
    match val {
      Value::Object(ref map) => process_object(map, matching_rules, generators, &updated_path, false, skip_matchers),
      Value::Array(ref array) => process_array(array, matching_rules, generators, &updated_path, false, skip_matchers),
      _ => val.clone()
    }
  }).collect())
}

/// Process an object (map) with embedded matching rules and generators
pub fn process_object(
  obj: &Map<String, Value>,
  matching_rules: &mut MatchingRuleCategory,
  generators: &mut Generators,
  path: &str,
  type_matcher: bool,
  skip_matchers: bool
) -> Value {
  if obj.contains_key("pact:matcher:type") {
    if !skip_matchers {
      let matching_rule = from_integration_json(obj);
      if let Some(rule) = &matching_rule {
        matching_rules.add_rule(&path.to_string(), rule.clone(), &RuleLogic::And);
      }
      if let Some(gen) = obj.get("pact:generator:type") {
        if let Some(generator) = Generator::from_map(&json_to_string(gen), obj) {
          generators.add_generator_with_subcategory(&GeneratorCategory::BODY, path, generator);
        }
      }
      let (value, skip_matchers) = if let Some(rule) = matching_rule {
        match rule {
          MatchingRule::ArrayContains(_) => (obj.get("variants"), true),
          _ => (obj.get("value"), false)
        }
      } else {
        (obj.get("value"), false)
      };
      match value {
        Some(val) => match val {
          Value::Object(ref map) => process_object(map, matching_rules, generators, path, true, skip_matchers),
          Value::Array(array) => process_array(array, matching_rules, generators, path, true, skip_matchers),
          _ => val.clone()
        },
        None => Value::Null
      }
    } else {
      match obj.get("value") {
        Some(val) => match val {
          Value::Object(ref map) => process_object(map, matching_rules, generators, path, false, skip_matchers),
          Value::Array(array) => process_array(array, matching_rules, generators, path, false, skip_matchers),
          _ => val.clone()
        },
        None => Value::Null
      }
    }
  } else {
    Value::Object(obj.iter()
      .filter(|(key, _)| !key.starts_with("pact:"))
      .map(|(key, val)| {
      let updated_path = if type_matcher {
        format!("{}.*", path)
      } else {
        format!("{}.{}", path, key)
      };
      (key.clone(), match val {
        Value::Object(ref map) => process_object(map, matching_rules, generators, &updated_path, false, skip_matchers),
        Value::Array(ref array) => process_array(array, matching_rules, generators, &updated_path, false, skip_matchers),
        _ => val.clone()
      })
    }).collect())
  }
}

/// Builds a `MatchingRule` from a `Value` struct used by language integrations
pub fn from_integration_json(m: &Map<String, Value>) -> Option<MatchingRule> {
  match m.get("pact:matcher:type") {
    Some(value) => {
      let val = json_to_string(value);
      match val.as_str() {
        "regex" => match m.get(&val) {
          Some(s) => Some(MatchingRule::Regex(json_to_string(s))),
          None => None
        },
        "equality" => Some(MatchingRule::Equality),
        "include" => match m.get("value") {
          Some(s) => Some(MatchingRule::Include(json_to_string(s))),
          None => None
        },
        "type" => match (json_to_num(m.get("min").cloned()), json_to_num(m.get("max").cloned())) {
          (Some(min), Some(max)) => Some(MatchingRule::MinMaxType(min, max)),
          (Some(min), None) => Some(MatchingRule::MinType(min)),
          (None, Some(max)) => Some(MatchingRule::MaxType(max)),
          _ => Some(MatchingRule::Type)
        },
        "number" => Some(MatchingRule::Number),
        "integer" => Some(MatchingRule::Integer),
        "decimal" => Some(MatchingRule::Decimal),
        "real" => Some(MatchingRule::Decimal),
        "min" => match json_to_num(m.get(&val).cloned()) {
          Some(min) => Some(MatchingRule::MinType(min)),
          None => None
        },
        "max" => match json_to_num(m.get(&val).cloned()) {
          Some(max) => Some(MatchingRule::MaxType(max)),
          None => None
        },
        "timestamp" => match m.get("format").or_else(|| m.get(&val)) {
          Some(s) => Some(MatchingRule::Timestamp(json_to_string(s))),
          None => None
        },
        "date" => match m.get("format").or_else(|| m.get(&val)) {
          Some(s) => Some(MatchingRule::Date(json_to_string(s))),
          None => None
        },
        "time" => match m.get("format").or_else(|| m.get(&val)) {
          Some(s) => Some(MatchingRule::Time(json_to_string(s))),
          None => None
        },
        "null" => Some(MatchingRule::Null),
        "contentType" => match m.get("value") {
          Some(s) => Some(MatchingRule::ContentType(json_to_string(s))),
          None => None
        }
        "arrayContains" => match m.get("variants") {
          Some(variants) => match variants {
            Value::Array(variants) => {
              let values = variants.iter().enumerate().map(|(index, variant)| {
                let mut category = MatchingRuleCategory::empty("body");
                let mut generators = Generators::default();
                match variant {
                  Value::Object(map) => {
                    process_object(map, &mut category, &mut generators, "$", false, false);
                  }
                  _ => warn!("arrayContains: JSON for variant {} is not correctly formed: {}", index, variant)
                }
                (index, category, generators.categories.get(&GeneratorCategory::BODY).cloned().unwrap_or_default())
              }).collect();
              Some(MatchingRule::ArrayContains(values))
            }
            _ => None
          }
          None => None
        }
        _ => None
      }
    },
    _ => None
  }
}

/// Process a JSON body with embedded matching rules and generators
pub fn process_json(body: String, matching_rules: &mut MatchingRuleCategory, generators: &mut Generators) -> String {
  match serde_json::from_str(&body) {
    Ok(json) => match json {
      Value::Object(ref map) => process_object(map, matching_rules, generators, &"$".to_string(), false, false).to_string(),
      Value::Array(ref array) => process_array(array, matching_rules, generators, &"$".to_string(), false, false).to_string(),
      _ => body
    },
    Err(_) => body
  }
}

/// Process a JSON body with embedded matching rules and generators
pub fn process_json_value(body: &Value, matching_rules: &mut MatchingRuleCategory, generators: &mut Generators) -> String {
  match body {
    Value::Object(ref map) => process_object(map, matching_rules, generators, &"$".to_string(), false, false).to_string(),
    Value::Array(ref array) => process_array(array, matching_rules, generators, &"$".to_string(), false, false).to_string(),
    _ => body.to_string()
  }
}

/// Setup the request as a multipart form upload
pub fn request_multipart(request: &mut Request, boundary: &str, body: OptionalBody, content_type: &str, part_name: &str) {
  request.body = body;
  match request.headers {
    Some(ref mut headers) => {
      headers.insert(CONTENT_TYPE_HEADER.to_string(), vec![format!("multipart/form-data; boundary={}", boundary)]);
    },
    None => {
      request.headers = Some(hashmap! {
        CONTENT_TYPE_HEADER.to_string() => vec![format!("multipart/form-data; boundary={}", boundary)]
      });
    }
  };
  request.matching_rules.add_category("body")
    .add_rule(format!("$['{}']", part_name), MatchingRule::ContentType(content_type.into()), &RuleLogic::And);
  request.matching_rules.add_category("header")
    .add_rule("Content-Type", MatchingRule::Regex(r"multipart/form-data;(\s*charset=[^;]*;)?\s*boundary=.*".into()), &RuleLogic::And);
}

/// Setup the response as a multipart form upload
pub fn response_multipart(response: &mut Response, boundary: &str, body: OptionalBody, content_type: &str, part_name: &str) {
  response.body = body;
  match response.headers {
    Some(ref mut headers) => {
      headers.insert(CONTENT_TYPE_HEADER.to_string(), vec![format!("multipart/form-data; boundary={}", boundary)]);
    },
    None => {
      response.headers = Some(hashmap! {
        CONTENT_TYPE_HEADER.to_string() => vec![format!("multipart/form-data; boundary={}", boundary)]
      });
    }
  }
  response.matching_rules.add_category("body")
    .add_rule(format!("$['{}']", part_name), MatchingRule::ContentType(content_type.into()), &RuleLogic::And);
  response.matching_rules.add_category("header")
    .add_rule("Content-Type", MatchingRule::Regex(r"multipart/form-data;(\s*charset=[^;]*;)?\s*boundary=.*".into()), &RuleLogic::And);
}

/// Representation of a multipart body
pub struct MultipartBody {
  /// The actual body
  pub body: OptionalBody,

  /// The boundary used in the multipart encoding
  pub boundary: String,
}

/// Loads an example file as a MIME Multipart body
pub fn file_as_multipart_body(file: &str, part_name: &str) -> Result<MultipartBody, String> {
  let mut multipart = multipart::client::Multipart::from_request(multipart::mock::ClientRequest::default()).unwrap();

  multipart.write_file(part_name, Path::new(file)).map_err(format_multipart_error)?;
  let http_buffer = multipart.send().map_err(format_multipart_error)?;

  Ok(MultipartBody {
    body: OptionalBody::Present(Bytes::from(http_buffer.buf), Some("multipart/form-data".into())),
    boundary: http_buffer.boundary
  })
}

/// Create an empty MIME Multipart body
pub fn empty_multipart_body() -> Result<MultipartBody, String> {
  let multipart = multipart::client::Multipart::from_request(multipart::mock::ClientRequest::default()).unwrap();
  let http_buffer = multipart.send().map_err(format_multipart_error)?;

  Ok(MultipartBody {
    body: OptionalBody::Present(Bytes::from(http_buffer.buf), Some("multipart/form-data".into())),
    boundary: http_buffer.boundary
  })
}

fn format_multipart_error(e: std::io::Error) -> String {
  format!("convert_ptr_to_mime_part_body: Failed to generate multipart body: {}", e)
}

#[cfg(test)]
mod test {
  use std::str::FromStr;

  use expectest::prelude::{be_equal_to, expect};
  use serde_json::json;

  use pact_matching::{generators, matchingrules_list};
  use pact_matching::models::generators::{Generator, Generators};
  use pact_matching::models::matchingrules::{MatchingRule, MatchingRuleCategory};

  use crate::bodies::process_object;

  #[test]
  fn process_object_with_normal_json_test() {
    let json = json!({
      "a": "b",
      "c": [100, 200, 300]
    });
    let mut matching_rules = MatchingRuleCategory::default();
    let mut generators = Generators::default();
    let result = process_object(json.as_object().unwrap(), &mut matching_rules,
                                &mut generators, "$", false, false);

    expect!(result).to(be_equal_to(json));
  }

  #[test]
  fn process_object_with_matching_rule_test() {
    let json = json!({
      "a": {
        "pact:matcher:type": "regex",
        "regex": "\\w+",
        "value": "b"
      },
      "c": [100, 200, {
        "pact:matcher:type": "integer",
        "pact:generator:type": "RandomInt",
        "value": 300
      }]
    });
    let mut matching_rules = MatchingRuleCategory::empty("body");
    let mut generators = Generators::default();
    let result = process_object(json.as_object().unwrap(), &mut matching_rules,
                                &mut generators, "$", false, false);

    expect!(result).to(be_equal_to(json!({
      "a": "b",
      "c": [100, 200, 300]
    })));
    expect!(matching_rules).to(be_equal_to(matchingrules_list!{
      "body";
      "$.a" => [ MatchingRule::Regex("\\w+".into()) ],
      "$.c[2]" => [ MatchingRule::Integer ]
    }));
    expect!(generators).to(be_equal_to(generators! {
      "BODY" => {
        "$.c[2]" => Generator::RandomInt(0, 10)
      }
    }));
  }

  #[test]
  fn process_object_with_primitive_json_value() {
    let json = json!({
      "pact:matcher:type": "regex",
      "regex": "\\w+",
      "value": "b"
    });
    let mut matching_rules = MatchingRuleCategory::empty("body");
    let mut generators = Generators::default();
    let result = process_object(json.as_object().unwrap(), &mut matching_rules,
                                &mut generators, "$", false, false);

    expect!(result).to(be_equal_to(json!("b")));
    expect!(matching_rules).to(be_equal_to(matchingrules_list!{
      "body";
      "$" => [ MatchingRule::Regex("\\w+".into()) ]
    }));
    expect!(generators).to(be_equal_to(Generators::default()));
  }
}
<commit_msg>feat: enable consumer code to use the new Value matcher<commit_after>//! Functions to support processing request/response bodies

use std::path::Path;

use bytes::Bytes;
use log::*;
use maplit::*;
use serde_json::{Map, Value};

use pact_matching::models::{OptionalBody, Request, Response};
use pact_matching::models::generators::{Generator, GeneratorCategory, Generators};
use pact_matching::models::json_utils::{json_to_num, json_to_string};
use pact_matching::models::matchingrules::{MatchingRule, MatchingRuleCategory, RuleLogic};

const CONTENT_TYPE_HEADER: &str = "Content-Type";

/// Process an array with embedded matching rules and generators
pub fn process_array(
  array: &[Value],
  matching_rules: &mut MatchingRuleCategory,
  generators: &mut Generators,
  path: &str,
  type_matcher: bool,
  skip_matchers: bool
) -> Value {
  Value::Array(array.iter().enumerate().map(|(index, val)| {
    let updated_path = if type_matcher {
      format!("{}[*]", path)
    } else {
      format!("{}[{}]", path, index)
    };
    match val {
      Value::Object(ref map) => process_object(map, matching_rules, generators, &updated_path, false, skip_matchers),
      Value::Array(ref array) => process_array(array, matching_rules, generators, &updated_path, false, skip_matchers),
      _ => val.clone()
    }
  }).collect())
}

/// Process an object (map) with embedded matching rules and generators
pub fn process_object(
  obj: &Map<String, Value>,
  matching_rules: &mut MatchingRuleCategory,
  generators: &mut Generators,
  path: &str,
  type_matcher: bool,
  skip_matchers: bool
) -> Value {
  if obj.contains_key("pact:matcher:type") {
    if !skip_matchers {
      let matching_rule = from_integration_json(obj);
      if let Some(rule) = &matching_rule {
        matching_rules.add_rule(&path.to_string(), rule.clone(), &RuleLogic::And);
      }
      if let Some(gen) = obj.get("pact:generator:type") {
        if let Some(generator) = Generator::from_map(&json_to_string(gen), obj) {
          generators.add_generator_with_subcategory(&GeneratorCategory::BODY, path, generator);
        }
      }
      let (value, skip_matchers) = if let Some(rule) = matching_rule {
        match rule {
          MatchingRule::ArrayContains(_) => (obj.get("variants"), true),
          _ => (obj.get("value"), false)
        }
      } else {
        (obj.get("value"), false)
      };
      match value {
        Some(val) => match val {
          Value::Object(ref map) => process_object(map, matching_rules, generators, path, true, skip_matchers),
          Value::Array(array) => process_array(array, matching_rules, generators, path, true, skip_matchers),
          _ => val.clone()
        },
        None => Value::Null
      }
    } else {
      match obj.get("value") {
        Some(val) => match val {
          Value::Object(ref map) => process_object(map, matching_rules, generators, path, false, skip_matchers),
          Value::Array(array) => process_array(array, matching_rules, generators, path, false, skip_matchers),
          _ => val.clone()
        },
        None => Value::Null
      }
    }
  } else {
    Value::Object(obj.iter()
      .filter(|(key, _)| !key.starts_with("pact:"))
      .map(|(key, val)| {
      let updated_path = if type_matcher {
        format!("{}.*", path)
      } else {
        format!("{}.{}", path, key)
      };
      (key.clone(), match val {
        Value::Object(ref map) => process_object(map, matching_rules, generators, &updated_path, false, skip_matchers),
        Value::Array(ref array) => process_array(array, matching_rules, generators, &updated_path, false, skip_matchers),
        _ => val.clone()
      })
    }).collect())
  }
}

/// Builds a `MatchingRule` from a `Value` struct used by language integrations
pub fn from_integration_json(m: &Map<String, Value>) -> Option<MatchingRule> {
  match m.get("pact:matcher:type") {
    Some(value) => {
      let val = json_to_string(value);
      match val.as_str() {
        "regex" => match m.get(&val) {
          Some(s) => Some(MatchingRule::Regex(json_to_string(s))),
          None => None
        },
        "equality" => Some(MatchingRule::Equality),
        "include" => match m.get("value") {
          Some(s) => Some(MatchingRule::Include(json_to_string(s))),
          None => None
        },
        "type" => match (json_to_num(m.get("min").cloned()), json_to_num(m.get("max").cloned())) {
          (Some(min), Some(max)) => Some(MatchingRule::MinMaxType(min, max)),
          (Some(min), None) => Some(MatchingRule::MinType(min)),
          (None, Some(max)) => Some(MatchingRule::MaxType(max)),
          _ => Some(MatchingRule::Type)
        },
        "number" => Some(MatchingRule::Number),
        "integer" => Some(MatchingRule::Integer),
        "decimal" => Some(MatchingRule::Decimal),
        "real" => Some(MatchingRule::Decimal),
        "min" => match json_to_num(m.get(&val).cloned()) {
          Some(min) => Some(MatchingRule::MinType(min)),
          None => None
        },
        "max" => match json_to_num(m.get(&val).cloned()) {
          Some(max) => Some(MatchingRule::MaxType(max)),
          None => None
        },
        "timestamp" => match m.get("format").or_else(|| m.get(&val)) {
          Some(s) => Some(MatchingRule::Timestamp(json_to_string(s))),
          None => None
        },
        "date" => match m.get("format").or_else(|| m.get(&val)) {
          Some(s) => Some(MatchingRule::Date(json_to_string(s))),
          None => None
        },
        "time" => match m.get("format").or_else(|| m.get(&val)) {
          Some(s) => Some(MatchingRule::Time(json_to_string(s))),
          None => None
        },
        "null" => Some(MatchingRule::Null),
        "values" => Some(MatchingRule::Values),
        "contentType" => match m.get("value") {
          Some(s) => Some(MatchingRule::ContentType(json_to_string(s))),
          None => None
        }
        "arrayContains" => match m.get("variants") {
          Some(variants) => match variants {
            Value::Array(variants) => {
              let values = variants.iter().enumerate().map(|(index, variant)| {
                let mut category = MatchingRuleCategory::empty("body");
                let mut generators = Generators::default();
                match variant {
                  Value::Object(map) => {
                    process_object(map, &mut category, &mut generators, "$", false, false);
                  }
                  _ => warn!("arrayContains: JSON for variant {} is not correctly formed: {}", index, variant)
                }
                (index, category, generators.categories.get(&GeneratorCategory::BODY).cloned().unwrap_or_default())
              }).collect();
              Some(MatchingRule::ArrayContains(values))
            }
            _ => None
          }
          None => None
        }
        _ => None
      }
    },
    _ => None
  }
}

/// Process a JSON body with embedded matching rules and generators
pub fn process_json(body: String, matching_rules: &mut MatchingRuleCategory, generators: &mut Generators) -> String {
  match serde_json::from_str(&body) {
    Ok(json) => match json {
      Value::Object(ref map) => process_object(map, matching_rules, generators, &"$".to_string(), false, false).to_string(),
      Value::Array(ref array) => process_array(array, matching_rules, generators, &"$".to_string(), false, false).to_string(),
      _ => body
    },
    Err(_) => body
  }
}

/// Process a JSON body with embedded matching rules and generators
pub fn process_json_value(body: &Value, matching_rules: &mut MatchingRuleCategory, generators: &mut Generators) -> String {
  match body {
    Value::Object(ref map) => process_object(map, matching_rules, generators, &"$".to_string(), false, false).to_string(),
    Value::Array(ref array) => process_array(array, matching_rules, generators, &"$".to_string(), false, false).to_string(),
    _ => body.to_string()
  }
}

/// Setup the request as a multipart form upload
pub fn request_multipart(request: &mut Request, boundary: &str, body: OptionalBody, content_type: &str, part_name: &str) {
  request.body = body;
  match request.headers {
    Some(ref mut headers) => {
      headers.insert(CONTENT_TYPE_HEADER.to_string(), vec![format!("multipart/form-data; boundary={}", boundary)]);
    },
    None => {
      request.headers = Some(hashmap! {
        CONTENT_TYPE_HEADER.to_string() => vec![format!("multipart/form-data; boundary={}", boundary)]
      });
    }
  };
  request.matching_rules.add_category("body")
    .add_rule(format!("$['{}']", part_name), MatchingRule::ContentType(content_type.into()), &RuleLogic::And);
  request.matching_rules.add_category("header")
    .add_rule("Content-Type", MatchingRule::Regex(r"multipart/form-data;(\s*charset=[^;]*;)?\s*boundary=.*".into()), &RuleLogic::And);
}

/// Setup the response as a multipart form upload
pub fn response_multipart(response: &mut Response, boundary: &str, body: OptionalBody, content_type: &str, part_name: &str) {
  response.body = body;
  match response.headers {
    Some(ref mut headers) => {
      headers.insert(CONTENT_TYPE_HEADER.to_string(), vec![format!("multipart/form-data; boundary={}", boundary)]);
    },
    None => {
      response.headers = Some(hashmap! {
        CONTENT_TYPE_HEADER.to_string() => vec![format!("multipart/form-data; boundary={}", boundary)]
      });
    }
  }
  response.matching_rules.add_category("body")
    .add_rule(format!("$['{}']", part_name), MatchingRule::ContentType(content_type.into()), &RuleLogic::And);
  response.matching_rules.add_category("header")
    .add_rule("Content-Type", MatchingRule::Regex(r"multipart/form-data;(\s*charset=[^;]*;)?\s*boundary=.*".into()), &RuleLogic::And);
}

/// Representation of a multipart body
pub struct MultipartBody {
  /// The actual body
  pub body: OptionalBody,

  /// The boundary used in the multipart encoding
  pub boundary: String,
}

/// Loads an example file as a MIME Multipart body
pub fn file_as_multipart_body(file: &str, part_name: &str) -> Result<MultipartBody, String> {
  let mut multipart = multipart::client::Multipart::from_request(multipart::mock::ClientRequest::default()).unwrap();

  multipart.write_file(part_name, Path::new(file)).map_err(format_multipart_error)?;
  let http_buffer = multipart.send().map_err(format_multipart_error)?;

  Ok(MultipartBody {
    body: OptionalBody::Present(Bytes::from(http_buffer.buf), Some("multipart/form-data".into())),
    boundary: http_buffer.boundary
  })
}

/// Create an empty MIME Multipart body
pub fn empty_multipart_body() -> Result<MultipartBody, String> {
  let multipart = multipart::client::Multipart::from_request(multipart::mock::ClientRequest::default()).unwrap();
  let http_buffer = multipart.send().map_err(format_multipart_error)?;

  Ok(MultipartBody {
    body: OptionalBody::Present(Bytes::from(http_buffer.buf), Some("multipart/form-data".into())),
    boundary: http_buffer.boundary
  })
}

fn format_multipart_error(e: std::io::Error) -> String {
  format!("convert_ptr_to_mime_part_body: Failed to generate multipart body: {}", e)
}

#[cfg(test)]
mod test {
  use std::str::FromStr;

  use expectest::prelude::{be_equal_to, expect};
  use serde_json::json;

  use pact_matching::{generators, matchingrules_list};
  use pact_matching::models::generators::{Generator, Generators};
  use pact_matching::models::matchingrules::{MatchingRule, MatchingRuleCategory};

  use crate::bodies::process_object;

  #[test]
  fn process_object_with_normal_json_test() {
    let json = json!({
      "a": "b",
      "c": [100, 200, 300]
    });
    let mut matching_rules = MatchingRuleCategory::default();
    let mut generators = Generators::default();
    let result = process_object(json.as_object().unwrap(), &mut matching_rules,
                                &mut generators, "$", false, false);

    expect!(result).to(be_equal_to(json));
  }

  #[test]
  fn process_object_with_matching_rule_test() {
    let json = json!({
      "a": {
        "pact:matcher:type": "regex",
        "regex": "\\w+",
        "value": "b"
      },
      "c": [100, 200, {
        "pact:matcher:type": "integer",
        "pact:generator:type": "RandomInt",
        "value": 300
      }]
    });
    let mut matching_rules = MatchingRuleCategory::empty("body");
    let mut generators = Generators::default();
    let result = process_object(json.as_object().unwrap(), &mut matching_rules,
                                &mut generators, "$", false, false);

    expect!(result).to(be_equal_to(json!({
      "a": "b",
      "c": [100, 200, 300]
    })));
    expect!(matching_rules).to(be_equal_to(matchingrules_list!{
      "body";
      "$.a" => [ MatchingRule::Regex("\\w+".into()) ],
      "$.c[2]" => [ MatchingRule::Integer ]
    }));
    expect!(generators).to(be_equal_to(generators! {
      "BODY" => {
        "$.c[2]" => Generator::RandomInt(0, 10)
      }
    }));
  }

  #[test]
  fn process_object_with_primitive_json_value() {
    let json = json!({
      "pact:matcher:type": "regex",
      "regex": "\\w+",
      "value": "b"
    });
    let mut matching_rules = MatchingRuleCategory::empty("body");
    let mut generators = Generators::default();
    let result = process_object(json.as_object().unwrap(), &mut matching_rules,
                                &mut generators, "$", false, false);

    expect!(result).to(be_equal_to(json!("b")));
    expect!(matching_rules).to(be_equal_to(matchingrules_list!{
      "body";
      "$" => [ MatchingRule::Regex("\\w+".into()) ]
    }));
    expect!(generators).to(be_equal_to(Generators::default()));
  }
}
<|endoftext|>


<commit_before>use std::{
    fmt,
    collections::HashMap,
};

use libsyntax2::{
    SyntaxNodeRef, SyntaxNode, SmolStr, AstNode,
    ast::{self, NameOwner, LoopBodyOwner},
    algo::{ancestors, generate, walk::preorder}
};

type ScopeId = usize;

#[derive(Debug)]
pub struct FnScopes {
    pub self_param: Option<SyntaxNode>,
    scopes: Vec<ScopeData>,
    scope_for: HashMap<SyntaxNode, ScopeId>,
}

impl FnScopes {
    pub fn new(fn_def: ast::FnDef) -> FnScopes {
        let mut scopes = FnScopes {
            self_param: fn_def.param_list()
                .and_then(|it| it.self_param())
                .map(|it| it.syntax().owned()),
            scopes: Vec::new(),
            scope_for: HashMap::new()
        };
        let root = scopes.root_scope();
        scopes.add_params_bindings(root, fn_def.param_list());
        if let Some(body) = fn_def.body() {
            compute_block_scopes(body, &mut scopes, root)
        }
        scopes
    }
    pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
        &self.scopes[scope].entries
    }
    pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item=ScopeId> + 'a {
        generate(self.scope_for(node), move |&scope| self.scopes[scope].parent)
    }
    fn root_scope(&mut self) -> ScopeId {
        let res = self.scopes.len();
        self.scopes.push(ScopeData { parent: None, entries: vec![] });
        res
    }
    fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
        let res = self.scopes.len();
        self.scopes.push(ScopeData { parent: Some(parent), entries: vec![] });
        res
    }
    fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) {
        let entries = preorder(pat.syntax())
            .filter_map(ast::BindPat::cast)
            .filter_map(ScopeEntry::new);
        self.scopes[scope].entries.extend(entries);
    }
    fn add_params_bindings(&mut self, scope: ScopeId, params: Option<ast::ParamList>) {
        params.into_iter()
            .flat_map(|it| it.params())
            .filter_map(|it| it.pat())
            .for_each(|it| self.add_bindings(scope, it));
    }
    fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) {
        self.scope_for.insert(node.owned(), scope);
    }
    fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> {
        ancestors(node)
            .filter_map(|it| self.scope_for.get(&it.owned()).map(|&scope| scope))
            .next()
    }
}

pub struct ScopeEntry {
    syntax: SyntaxNode
}

impl ScopeEntry {
    fn new(pat: ast::BindPat) -> Option<ScopeEntry> {
        if pat.name().is_some() {
            Some(ScopeEntry { syntax: pat.syntax().owned() })
        } else {
            None
        }
    }
    pub fn name(&self) -> SmolStr {
        self.ast().name()
            .unwrap()
            .text()
    }
    fn ast(&self) -> ast::BindPat {
        ast::BindPat::cast(self.syntax.borrowed())
            .unwrap()
    }
}

impl fmt::Debug for ScopeEntry {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("ScopeEntry")
         .field("name", &self.name())
         .field("syntax", &self.syntax)
         .finish()
    }
}

fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) {
    for stmt in block.statements() {
        match stmt {
            ast::Stmt::LetStmt(stmt) => {
                if let Some(expr) = stmt.initializer() {
                    scopes.set_scope(expr.syntax(), scope);
                    compute_expr_scopes(expr, scopes, scope);
                }
                scope = scopes.new_scope(scope);
                if let Some(pat) = stmt.pat() {
                    scopes.add_bindings(scope, pat);
                }
            }
            ast::Stmt::ExprStmt(expr_stmt) => {
                if let Some(expr) = expr_stmt.expr() {
                    scopes.set_scope(expr.syntax(), scope);
                    compute_expr_scopes(expr, scopes, scope);
                }
            }
        }
    }
    if let Some(expr) = block.expr() {
        scopes.set_scope(expr.syntax(), scope);
        compute_expr_scopes(expr, scopes, scope);
    }
}

fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) {
    match expr {
        ast::Expr::IfExpr(e) => {
            let cond_scope = e.condition().and_then(|cond| {
                compute_cond_scopes(cond, scopes, scope)
            });
            if let Some(block) = e.then_branch() {
                compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
            }
            if let Some(block) = e.else_branch() {
                compute_block_scopes(block, scopes, scope);
            }
        },
        ast::Expr::BlockExpr(e) => {
            if let Some(block) = e.block() {
                compute_block_scopes(block, scopes, scope);
            }
        }
        ast::Expr::LoopExpr(e) => {
            if let Some(block) = e.loop_body() {
                compute_block_scopes(block, scopes, scope);
            }
        }
        ast::Expr::WhileExpr(e) => {
            let cond_scope = e.condition().and_then(|cond| {
                compute_cond_scopes(cond, scopes, scope)
            });
            if let Some(block) = e.loop_body() {
                compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
            }
        }
        ast::Expr::ForExpr(e) => {
            if let Some(expr) = e.iterable() {
                compute_expr_scopes(expr, scopes, scope);
            }
            let mut scope = scope;
            if let Some(pat) = e.pat() {
                scope = scopes.new_scope(scope);
                scopes.add_bindings(scope, pat);
            }
            if let Some(block) = e.loop_body() {
                compute_block_scopes(block, scopes, scope);
            }
        }
        ast::Expr::LambdaExpr(e) => {
            let mut scope = scopes.new_scope(scope);
            scopes.add_params_bindings(scope, e.param_list());
            if let Some(body) = e.body() {
                scopes.set_scope(body.syntax(), scope);
                compute_expr_scopes(body, scopes, scope);
            }
        }
        ast::Expr::CallExpr(e) => {
            e.arg_list().into_iter()
                .flat_map(|it| it.args())
                .chain(e.expr())
                .for_each(|expr| compute_expr_scopes(expr, scopes, scope));
        }
        ast::Expr::MatchExpr(e) => {
            if let Some(expr) = e.expr() {
                compute_expr_scopes(expr, scopes, scope);
            }
            for arm in e.match_arm_list().into_iter().flat_map(|it| it.arms()) {
                let scope = scopes.new_scope(scope);
                for pat in arm.pats() {
                    scopes.add_bindings(scope, pat);
                }
                if let Some(expr) = arm.expr() {
                    compute_expr_scopes(expr, scopes, scope);
                }
            }
        }
        _ => {
            expr.syntax().children()
                .filter_map(ast::Expr::cast)
                .for_each(|expr| compute_expr_scopes(expr, scopes, scope))
        }
    };

    fn compute_cond_scopes(cond: ast::Condition, scopes: &mut FnScopes, scope: ScopeId) -> Option<ScopeId> {
        if let Some(expr) = cond.expr() {
            compute_expr_scopes(expr, scopes, scope);
        }
        if let Some(pat) = cond.pat() {
            let s = scopes.new_scope(scope);
            scopes.add_bindings(s, pat);
            Some(s)
        } else {
            None
        }
    }
}

#[derive(Debug)]
struct ScopeData {
    parent: Option<ScopeId>,
    entries: Vec<ScopeEntry>
}

#[cfg(test)]
mod tests {
    use super::*;
    use libsyntax2::File;
    use {find_node_at_offset, test_utils::extract_offset};

    fn do_check(code: &str, expected: &[&str]) {
        let (off, code) = extract_offset(code);
        let code = {
            let mut buf = String::new();
            let off = u32::from(off) as usize;
            buf.push_str(&code[..off]);
            buf.push_str("marker");
            buf.push_str(&code[off..]);
            buf
        };
        let file = File::parse(&code);
        let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
        let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
        let scopes = FnScopes::new(fn_def);
        let actual = scopes.scope_chain(marker.syntax())
            .flat_map(|scope| scopes.entries(scope))
            .map(|it| it.name())
            .collect::<Vec<_>>();
        assert_eq!(expected, actual.as_slice());
    }

    #[test]
    fn test_lambda_scope() {
        do_check(r"
            fn quux(foo: i32) {
                let f = |bar| {
                    <|>
                };
            }",
            &["bar", "foo"],
        );
    }

    #[test]
    fn test_call_scope() {
        do_check(r"
            fn quux() {
                f(|x| <|> );
            }",
            &["x"],
        );
    }

    #[test]
    fn test_loop_scope() {
        do_check(r"
            fn quux() {
                loop {
                    let x = ();
                    <|>
                };
            }",
            &["x"],
        );
    }

    #[test]
    fn test_match() {
        do_check(r"
            fn quux() {
                match () {
                    Some(x) => {
                        <|>
                    }
                };
            }",
            &["x"],
        );
    }
}
<commit_msg>improve test<commit_after>use std::{
    fmt,
    collections::HashMap,
};

use libsyntax2::{
    SyntaxNodeRef, SyntaxNode, SmolStr, AstNode,
    ast::{self, NameOwner, LoopBodyOwner},
    algo::{ancestors, generate, walk::preorder}
};

type ScopeId = usize;

#[derive(Debug)]
pub struct FnScopes {
    pub self_param: Option<SyntaxNode>,
    scopes: Vec<ScopeData>,
    scope_for: HashMap<SyntaxNode, ScopeId>,
}

impl FnScopes {
    pub fn new(fn_def: ast::FnDef) -> FnScopes {
        let mut scopes = FnScopes {
            self_param: fn_def.param_list()
                .and_then(|it| it.self_param())
                .map(|it| it.syntax().owned()),
            scopes: Vec::new(),
            scope_for: HashMap::new()
        };
        let root = scopes.root_scope();
        scopes.add_params_bindings(root, fn_def.param_list());
        if let Some(body) = fn_def.body() {
            compute_block_scopes(body, &mut scopes, root)
        }
        scopes
    }
    pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
        &self.scopes[scope].entries
    }
    pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item=ScopeId> + 'a {
        generate(self.scope_for(node), move |&scope| self.scopes[scope].parent)
    }
    fn root_scope(&mut self) -> ScopeId {
        let res = self.scopes.len();
        self.scopes.push(ScopeData { parent: None, entries: vec![] });
        res
    }
    fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
        let res = self.scopes.len();
        self.scopes.push(ScopeData { parent: Some(parent), entries: vec![] });
        res
    }
    fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) {
        let entries = preorder(pat.syntax())
            .filter_map(ast::BindPat::cast)
            .filter_map(ScopeEntry::new);
        self.scopes[scope].entries.extend(entries);
    }
    fn add_params_bindings(&mut self, scope: ScopeId, params: Option<ast::ParamList>) {
        params.into_iter()
            .flat_map(|it| it.params())
            .filter_map(|it| it.pat())
            .for_each(|it| self.add_bindings(scope, it));
    }
    fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) {
        self.scope_for.insert(node.owned(), scope);
    }
    fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> {
        ancestors(node)
            .filter_map(|it| self.scope_for.get(&it.owned()).map(|&scope| scope))
            .next()
    }
}

pub struct ScopeEntry {
    syntax: SyntaxNode
}

impl ScopeEntry {
    fn new(pat: ast::BindPat) -> Option<ScopeEntry> {
        if pat.name().is_some() {
            Some(ScopeEntry { syntax: pat.syntax().owned() })
        } else {
            None
        }
    }
    pub fn name(&self) -> SmolStr {
        self.ast().name()
            .unwrap()
            .text()
    }
    fn ast(&self) -> ast::BindPat {
        ast::BindPat::cast(self.syntax.borrowed())
            .unwrap()
    }
}

impl fmt::Debug for ScopeEntry {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("ScopeEntry")
         .field("name", &self.name())
         .field("syntax", &self.syntax)
         .finish()
    }
}

fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) {
    for stmt in block.statements() {
        match stmt {
            ast::Stmt::LetStmt(stmt) => {
                if let Some(expr) = stmt.initializer() {
                    scopes.set_scope(expr.syntax(), scope);
                    compute_expr_scopes(expr, scopes, scope);
                }
                scope = scopes.new_scope(scope);
                if let Some(pat) = stmt.pat() {
                    scopes.add_bindings(scope, pat);
                }
            }
            ast::Stmt::ExprStmt(expr_stmt) => {
                if let Some(expr) = expr_stmt.expr() {
                    scopes.set_scope(expr.syntax(), scope);
                    compute_expr_scopes(expr, scopes, scope);
                }
            }
        }
    }
    if let Some(expr) = block.expr() {
        scopes.set_scope(expr.syntax(), scope);
        compute_expr_scopes(expr, scopes, scope);
    }
}

fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) {
    match expr {
        ast::Expr::IfExpr(e) => {
            let cond_scope = e.condition().and_then(|cond| {
                compute_cond_scopes(cond, scopes, scope)
            });
            if let Some(block) = e.then_branch() {
                compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
            }
            if let Some(block) = e.else_branch() {
                compute_block_scopes(block, scopes, scope);
            }
        },
        ast::Expr::BlockExpr(e) => {
            if let Some(block) = e.block() {
                compute_block_scopes(block, scopes, scope);
            }
        }
        ast::Expr::LoopExpr(e) => {
            if let Some(block) = e.loop_body() {
                compute_block_scopes(block, scopes, scope);
            }
        }
        ast::Expr::WhileExpr(e) => {
            let cond_scope = e.condition().and_then(|cond| {
                compute_cond_scopes(cond, scopes, scope)
            });
            if let Some(block) = e.loop_body() {
                compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
            }
        }
        ast::Expr::ForExpr(e) => {
            if let Some(expr) = e.iterable() {
                compute_expr_scopes(expr, scopes, scope);
            }
            let mut scope = scope;
            if let Some(pat) = e.pat() {
                scope = scopes.new_scope(scope);
                scopes.add_bindings(scope, pat);
            }
            if let Some(block) = e.loop_body() {
                compute_block_scopes(block, scopes, scope);
            }
        }
        ast::Expr::LambdaExpr(e) => {
            let mut scope = scopes.new_scope(scope);
            scopes.add_params_bindings(scope, e.param_list());
            if let Some(body) = e.body() {
                scopes.set_scope(body.syntax(), scope);
                compute_expr_scopes(body, scopes, scope);
            }
        }
        ast::Expr::CallExpr(e) => {
            e.arg_list().into_iter()
                .flat_map(|it| it.args())
                .chain(e.expr())
                .for_each(|expr| compute_expr_scopes(expr, scopes, scope));
        }
        ast::Expr::MatchExpr(e) => {
            if let Some(expr) = e.expr() {
                compute_expr_scopes(expr, scopes, scope);
            }
            for arm in e.match_arm_list().into_iter().flat_map(|it| it.arms()) {
                let scope = scopes.new_scope(scope);
                for pat in arm.pats() {
                    scopes.add_bindings(scope, pat);
                }
                if let Some(expr) = arm.expr() {
                    compute_expr_scopes(expr, scopes, scope);
                }
            }
        }
        _ => {
            expr.syntax().children()
                .filter_map(ast::Expr::cast)
                .for_each(|expr| compute_expr_scopes(expr, scopes, scope))
        }
    };

    fn compute_cond_scopes(cond: ast::Condition, scopes: &mut FnScopes, scope: ScopeId) -> Option<ScopeId> {
        if let Some(expr) = cond.expr() {
            compute_expr_scopes(expr, scopes, scope);
        }
        if let Some(pat) = cond.pat() {
            let s = scopes.new_scope(scope);
            scopes.add_bindings(s, pat);
            Some(s)
        } else {
            None
        }
    }
}

#[derive(Debug)]
struct ScopeData {
    parent: Option<ScopeId>,
    entries: Vec<ScopeEntry>
}

#[cfg(test)]
mod tests {
    use super::*;
    use libsyntax2::File;
    use {find_node_at_offset, test_utils::extract_offset};

    fn do_check(code: &str, expected: &[&str]) {
        let (off, code) = extract_offset(code);
        let code = {
            let mut buf = String::new();
            let off = u32::from(off) as usize;
            buf.push_str(&code[..off]);
            buf.push_str("marker");
            buf.push_str(&code[off..]);
            buf
        };
        let file = File::parse(&code);
        let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
        let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
        let scopes = FnScopes::new(fn_def);
        let actual = scopes.scope_chain(marker.syntax())
            .flat_map(|scope| scopes.entries(scope))
            .map(|it| it.name())
            .collect::<Vec<_>>();
        assert_eq!(expected, actual.as_slice());
    }

    #[test]
    fn test_lambda_scope() {
        do_check(r"
            fn quux(foo: i32) {
                let f = |bar, baz: i32| {
                    <|>
                };
            }",
            &["bar", "baz", "foo"],
        );
    }

    #[test]
    fn test_call_scope() {
        do_check(r"
            fn quux() {
                f(|x| <|> );
            }",
            &["x"],
        );
    }

    #[test]
    fn test_loop_scope() {
        do_check(r"
            fn quux() {
                loop {
                    let x = ();
                    <|>
                };
            }",
            &["x"],
        );
    }

    #[test]
    fn test_match() {
        do_check(r"
            fn quux() {
                match () {
                    Some(x) => {
                        <|>
                    }
                };
            }",
            &["x"],
        );
    }
}
<|endoftext|>


<commit_before>
#[derive(Debug)]
#[repr(packed)] // repr(C) would add unwanted padding before first_section
pub struct BootLoaderNameTag {
    typ: u32,
    size: u32,
    string: u8,
}

impl BootLoaderNameTag {
    pub fn name(&self) -> &str {
        unsafe { ::core::str::from_utf8_unchecked(::core::slice::from_raw_parts((&self.string) as *const u8, self.size as usize - 8)) }
    }
}
<commit_msg>Fix #11:  BootLoaderNameTag returning null char<commit_after>
#[derive(Debug)]
#[repr(packed)] // repr(C) would add unwanted padding before first_section
pub struct BootLoaderNameTag {
    typ: u32,
    size: u32,
    string: u8,
}

impl BootLoaderNameTag {
    pub fn name(&self) -> &str {
        use core::{mem,str,slice};
        unsafe {
            let strlen = self.size as usize - mem::size_of::<BootLoaderNameTag>();
            str::from_utf8_unchecked(
                slice::from_raw_parts((&self.string) as *const u8, strlen))
        }
    }
}
<|endoftext|>