galvanic-mock 0.1.3

A behaviour-driven mocking framework for generic traits. Create mocks for (multiple) traits in a behaviour-driven development mocking framework. Define the behaviour of mocks and expected method calls using argument patterns. Supports mocking of generic traits and generic methods. Requires: nightly
Documentation
/* Copyright 2017 Christopher Bacher
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
use syn;
use syn::parse::*;

use data::*;
use generate::binding_implementer::implement_initialize_binding;

named!(pub parse_bind -> BindingField,
    do_parse!(
        punct!("bind") >> name: call!(syn::parse::ident) >>
        punct!(":") >> ty: call!(syn::parse::ty) >>
        punct!("=") >> initializer: call!(syn::parse::expr) >>
        (BindingField { name, ty, initializer })
    )
);

named!(pub parse_expect_interaction -> ExpectStatement,
    do_parse!(
        punct!("<") >> mock_var: call!(syn::parse::ident) >> keyword!("as") >> ufc_trait: call!(syn::parse::path) >> punct!(">") >>
        punct!("::") >> method: call!(syn::parse::ident) >>
        args: alt!(
              delimited!(punct!("("), separated_list!(punct!(","), syn::parse::expr), punct!(")")) => {
                    |es: Vec<_>|
                        if es.is_empty() { BehaviourMatcher::Void }
                        else { BehaviourMatcher::PerArgument(es) }
              }
            | call!(syn::parse::expr) => { |e| BehaviourMatcher::Explicit(e) }
        ) >>
        repeat: alt!( preceded!(keyword!("times"), syn::parse::expr) => { |e| ExpectRepeat::Times(e) }
                    | preceded!(keyword!("at_least"), syn::parse::expr) => { |e| ExpectRepeat::AtLeast(e) }
                    | preceded!(keyword!("at_most"), syn::parse::expr) => { |e| ExpectRepeat::AtMost(e) }
                    | preceded!(keyword!("between"), tuple!( call!(syn::parse::expr), preceded!(punct!(","), syn::parse::expr) )) => { |(e1, e2)| ExpectRepeat::Between(e1, e2) }
                    | keyword!("never") => { |_| ExpectRepeat::Times(syn::parse::expr("0").expect("")) }
        ) >>
        (ExpectStatement {
            block_id: 0,
            stmt_id: 0,
            mock_var,
            ufc_trait,
            method,
            matcher: args,
            repeat
        })
    )
);

named!(pub parse_expect_interactions -> (Vec<BindingField>, Vec<ExpectStatement>),
    delimited!(tuple!(keyword!("expect_interactions"), punct!("!"), punct!("{")),
               tuple!(
                   terminated_list!(punct!(";"), parse_bind),
                   terminated_list!(punct!(";"), parse_expect_interaction)
               ),
               punct!("}")
    )
);


pub fn handle_expect_interactions(source: &str, absolute_position: usize) -> (String, String) {
    if let IResult::Done(remainder, (binding_fields, expect_definitions)) = parse_expect_interactions(source) {
        let mut statements = acquire!(EXPECT_STATEMENTS);

        let mut add_statements = Vec::new();
        for (idx, mut stmt) in expect_definitions.into_iter().enumerate() {
            stmt.block_id = absolute_position;
            stmt.stmt_id = absolute_position + idx;
            let stmt_id = stmt.stmt_id;

            {
                let mock_var = &stmt.mock_var;
                let ufc_trait_name = stmt.trait_name();
                let method_name = stmt.method_name();

                let stmt_repr = format!("{}", stmt);
                add_statements.push(match &stmt.repeat {
                    &ExpectRepeat::Times(ref expr) => quote!( #mock_var.add_expect_behaviour(#ufc_trait_name, #method_name, mock::ExpectBehaviour::with_times(#expr, #stmt_id, binding.clone(), #stmt_repr)); ),
                    &ExpectRepeat::AtLeast(ref expr) => quote!( #mock_var.add_expect_behaviour(#ufc_trait_name, #method_name, mock::ExpectBehaviour::with_at_least(#expr, #stmt_id, binding.clone(), #stmt_repr)); ),
                    &ExpectRepeat::AtMost(ref expr) => quote!( #mock_var.add_expect_behaviour(#ufc_trait_name, #method_name, mock::ExpectBehaviour::with_at_most(#expr, #stmt_id, binding.clone(), #stmt_repr)); ),
                    &ExpectRepeat::Between(ref expr_lower, ref expr_upper) => quote!( #mock_var.add_expect_behaviour(#ufc_trait_name, #method_name, mock::ExpectBehaviour::with_between(#expr_lower, #expr_upper, #stmt_id, binding.clone(), #stmt_repr)); ),
                });
            }
            statements.entry(stmt.ufc_trait.clone())
                      .or_insert_with(|| Vec::new())
                      .push(stmt);
        }

        let binding = Binding {
            block_id: absolute_position,
            fields: binding_fields
        };
        let binding_initialization = implement_initialize_binding(&binding);
        acquire!(BINDINGS).push(binding);

        let given_block = quote! {
            let binding = std::rc::Rc::new(#binding_initialization);
            #(#add_statements)*
        };

        return (given_block.to_string(), remainder.to_owned());
    } else { panic!("Expecting a `expect_interactions!` definition: <MOCK_VAR_NAME as MOCKED_TRAIT>::METHOD(MATCHER, ...) REPEAT; ..."); }
}