<commit_before>#![deny(warnings)]
#![feature(vec_push_all, test)]
extern crate hyper;

extern crate test;

use std::fmt;
use std::io::{self, Read, Write, Cursor};
use std::net::SocketAddr;
use std::time::Duration;

use hyper::net;

static README: &'static [u8] = include_bytes!("../README.md");

struct MockStream {
    read: Cursor<Vec<u8>>
}

impl MockStream {
    fn new() -> MockStream {
        let head = b"HTTP/1.1 200 OK\r\nServer: Mock\r\n\r\n";
        let mut res = head.to_vec();
        res.push_all(README);
        MockStream {
            read: Cursor::new(res)
        }
    }
}

impl Clone for MockStream {
    fn clone(&self) -> MockStream {
        MockStream {
            read: Cursor::new(self.read.get_ref().clone())
        }
    }
}

impl Read for MockStream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.read.read(buf)
    }
}

impl Write for MockStream {
    fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
        // we're mocking, what do we care.
        Ok(msg.len())
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

#[derive(Clone, Debug)]
struct Foo;

impl hyper::header::Header for Foo {
    fn header_name() -> &'static str {
        "x-foo"
    }
    fn parse_header(_: &[Vec<u8>]) -> hyper::Result<Foo> {
        Err(hyper::Error::Header)
    }
}

impl hyper::header::HeaderFormat for Foo {
    fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str("Bar")
    }
}

impl net::NetworkStream for MockStream {
    fn peer_addr(&mut self) -> io::Result<SocketAddr> {
        Ok("127.0.0.1:1337".parse().unwrap())
    }
    fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
        // can't time out
        Ok(())
    }
    fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
        // can't time out
        Ok(())
    }
}

struct MockConnector;

impl net::NetworkConnector for MockConnector {
    type Stream = MockStream;
    fn connect(&self, _: &str, _: u16, _: &str) -> hyper::Result<MockStream> {
        Ok(MockStream::new())
    }
}

#[bench]
fn bench_mock_hyper(b: &mut test::Bencher) {
    let url = "http://127.0.0.1:1337/";
    b.iter(|| {
        let mut req = hyper::client::Request::with_connector(
            hyper::Get, hyper::Url::parse(url).unwrap(), &MockConnector
        ).unwrap();
        req.headers_mut().set(Foo);

        let mut s = String::new();
        req
            .start().unwrap()
            .send().unwrap()
            .read_to_string(&mut s).unwrap()
    });
}
<commit_msg>test(bench): switch vec_push_all to extend_from_slice<commit_after>#![deny(warnings)]
#![feature(test)]
extern crate hyper;

extern crate test;

use std::fmt;
use std::io::{self, Read, Write, Cursor};
use std::net::SocketAddr;
use std::time::Duration;

use hyper::net;

static README: &'static [u8] = include_bytes!("../README.md");

struct MockStream {
    read: Cursor<Vec<u8>>
}

impl MockStream {
    fn new() -> MockStream {
        let head = b"HTTP/1.1 200 OK\r\nServer: Mock\r\n\r\n";
        let mut res = head.to_vec();
        res.extend_from_slice(README);
        MockStream {
            read: Cursor::new(res)
        }
    }
}

impl Clone for MockStream {
    fn clone(&self) -> MockStream {
        MockStream {
            read: Cursor::new(self.read.get_ref().clone())
        }
    }
}

impl Read for MockStream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.read.read(buf)
    }
}

impl Write for MockStream {
    fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
        // we're mocking, what do we care.
        Ok(msg.len())
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

#[derive(Clone, Debug)]
struct Foo;

impl hyper::header::Header for Foo {
    fn header_name() -> &'static str {
        "x-foo"
    }
    fn parse_header(_: &[Vec<u8>]) -> hyper::Result<Foo> {
        Err(hyper::Error::Header)
    }
}

impl hyper::header::HeaderFormat for Foo {
    fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str("Bar")
    }
}

impl net::NetworkStream for MockStream {
    fn peer_addr(&mut self) -> io::Result<SocketAddr> {
        Ok("127.0.0.1:1337".parse().unwrap())
    }
    fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
        // can't time out
        Ok(())
    }
    fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
        // can't time out
        Ok(())
    }
}

struct MockConnector;

impl net::NetworkConnector for MockConnector {
    type Stream = MockStream;
    fn connect(&self, _: &str, _: u16, _: &str) -> hyper::Result<MockStream> {
        Ok(MockStream::new())
    }
}

#[bench]
fn bench_mock_hyper(b: &mut test::Bencher) {
    let url = "http://127.0.0.1:1337/";
    b.iter(|| {
        let mut req = hyper::client::Request::with_connector(
            hyper::Get, hyper::Url::parse(url).unwrap(), &MockConnector
        ).unwrap();
        req.headers_mut().set(Foo);

        let mut s = String::new();
        req
            .start().unwrap()
            .send().unwrap()
            .read_to_string(&mut s).unwrap()
    });
}
<|endoftext|>


<commit_before>use std;
use std::fmt;

use bootsector;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FileType {
    GZip,
    Zip,
    Tar,
    BZip2,
    Xz,
    Deb,
    DiskImage,
    Other,
}

impl fmt::Display for FileType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(self, f)
    }
}

fn read_octal(bytes: &[u8]) -> Option<u32> {
    let mut start = 0;
    while start < bytes.len() && b' ' == bytes[start] {
        start += 1;
    }

    let mut end = bytes.len() - 1;
    while end > start && (b' ' == bytes[end] || 0 == bytes[end]) {
        end -= 1;
    }

    if let Ok(string) = std::str::from_utf8(&bytes[start..(end + 1)]) {
        if let Ok(val) = u32::from_str_radix(string, 8) {
            return Some(val);
        }
    }
    None
}

fn is_probably_tar(header: &[u8]) -> bool {
    if header.len() < 512 {
        return false;
    }

    if let Some(checksum) = read_octal(&header[148..156]) {
        let mut sum: u32 = (b' ' as u32) * 8;
        for i in 0..148 {
            sum += header[i] as u32;
        }
        for i in 156..512 {
            sum += header[i] as u32;
        }

        if checksum == sum {
            return true;
        }
    }

    return false;
}

/// As we allow MBR tables, and MBR tables have nearly no magic, no checksums, etc.
/// there is some risk that we'll try and read total nonsense as an MBR; live with it.
fn is_probably_disc_image(header: &[u8]) -> bool {
    if header.len() < 512 + 4 {
        return false;
    }

    if 0x55 != header[510] || 0xAA != header[511] {
        return false;
    }

    use std::io;
    let cursor = io::Cursor::new(header);
    match bootsector::list_partitions(cursor, &bootsector::Options::default()) {
        Err(_) => false,
        Ok(table) => !table.is_empty(),
    }
}

const DEB_PREFIX: &[u8] = b"!<arch>\ndebian-binary ";

impl FileType {
    #[cfg_attr(rustfmt, rustfmt_skip)]
    pub fn identify<'a>(header: &[u8]) -> FileType {
        if header.len() >= 20
            && 0x1f == header[0] && 0x8b == header[1] {
            FileType::GZip
        } else if header.len() >= 152
            && b'P' == header[0] && b'K' == header[1]
            && 0x03 == header[2] && 0x04 == header[3] {
            FileType::Zip
        } else if header.len() > 257 + 10
            && b'u' == header[257] && b's' == header[258]
            && b't' == header[259] && b'a' == header[260]
            && b'r' == header[261]
            && (
            (0 == header[262] && b'0' == header[263] && b'0' == header[264]) ||
                (b' ' == header[262] && b' ' == header[263] && 0 == header[264])
        ) {
            FileType::Tar
        } else if header.len() > 70
            && header[0..DEB_PREFIX.len()] == DEB_PREFIX[..]
            && header[66..70] == b"`\n2."[..] {
            FileType::Deb
        } else if header.len() > 40
            && b'B' == header[0] && b'Z' == header[1]
            && b'h' == header[2] // [3]: compression level
            && 0x31 == header[4] && 0x41 == header[5]
            && 0x59 == header[6] && 0x26 == header[7]
            && 0x53 == header[8] && 0x59 == header[9] {
            FileType::BZip2
        } else if header.len() > 6
            && 0xfd == header[0] && b'7' == header[1]
            && b'z' == header[2] && b'X' == header[3]
            && b'Z' == header[4] && 0 == header[5] {
            FileType::Xz
        } else if is_probably_tar(header) {
            FileType::Tar
        } else if is_probably_disc_image(header) {
            FileType::DiskImage
        } else {
            FileType::Other
        }
    }
}
<commit_msg>don't try and read the partition table during file identification; buf isn't big enough<commit_after>use std;
use std::fmt;

use bootsector;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FileType {
    GZip,
    Zip,
    Tar,
    BZip2,
    Xz,
    Deb,
    DiskImage,
    Other,
}

impl fmt::Display for FileType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(self, f)
    }
}

fn read_octal(bytes: &[u8]) -> Option<u32> {
    let mut start = 0;
    while start < bytes.len() && b' ' == bytes[start] {
        start += 1;
    }

    let mut end = bytes.len() - 1;
    while end > start && (b' ' == bytes[end] || 0 == bytes[end]) {
        end -= 1;
    }

    if let Ok(string) = std::str::from_utf8(&bytes[start..(end + 1)]) {
        if let Ok(val) = u32::from_str_radix(string, 8) {
            return Some(val);
        }
    }
    None
}

fn is_probably_tar(header: &[u8]) -> bool {
    if header.len() < 512 {
        return false;
    }

    if let Some(checksum) = read_octal(&header[148..156]) {
        let mut sum: u32 = (b' ' as u32) * 8;
        for i in 0..148 {
            sum += header[i] as u32;
        }
        for i in 156..512 {
            sum += header[i] as u32;
        }

        if checksum == sum {
            return true;
        }
    }

    return false;
}

/// As we allow MBR tables, and MBR tables have nearly no magic, no checksums, etc.
/// there is some risk that we'll try and read total nonsense as an MBR; live with it.
fn is_probably_disc_image(header: &[u8]) -> bool {
    if header.len() < 512 + 4 {
        return false;
    }

    0x55 == header[510] && 0xAA == header[511]
}

const DEB_PREFIX: &[u8] = b"!<arch>\ndebian-binary ";

impl FileType {
    #[cfg_attr(rustfmt, rustfmt_skip)]
    pub fn identify<'a>(header: &[u8]) -> FileType {
        if header.len() >= 20
            && 0x1f == header[0] && 0x8b == header[1] {
            FileType::GZip
        } else if header.len() >= 152
            && b'P' == header[0] && b'K' == header[1]
            && 0x03 == header[2] && 0x04 == header[3] {
            FileType::Zip
        } else if header.len() > 257 + 10
            && b'u' == header[257] && b's' == header[258]
            && b't' == header[259] && b'a' == header[260]
            && b'r' == header[261]
            && (
            (0 == header[262] && b'0' == header[263] && b'0' == header[264]) ||
                (b' ' == header[262] && b' ' == header[263] && 0 == header[264])
        ) {
            FileType::Tar
        } else if header.len() > 70
            && header[0..DEB_PREFIX.len()] == DEB_PREFIX[..]
            && header[66..70] == b"`\n2."[..] {
            FileType::Deb
        } else if header.len() > 40
            && b'B' == header[0] && b'Z' == header[1]
            && b'h' == header[2] // [3]: compression level
            && 0x31 == header[4] && 0x41 == header[5]
            && 0x59 == header[6] && 0x26 == header[7]
            && 0x53 == header[8] && 0x59 == header[9] {
            FileType::BZip2
        } else if header.len() > 6
            && 0xfd == header[0] && b'7' == header[1]
            && b'z' == header[2] && b'X' == header[3]
            && b'Z' == header[4] && 0 == header[5] {
            FileType::Xz
        } else if is_probably_tar(header) {
            FileType::Tar
        } else if is_probably_disc_image(header) {
            FileType::DiskImage
        } else {
            FileType::Other
        }
    }
}
<|endoftext|>


<commit_before>// vim: tw=80
use super::*;

use quote::ToTokens;

use crate::{
    mockable_item::{MockableItem, MockableModule}
};

/// A Mock item
pub(crate) enum MockItem {
    Module(MockItemModule)
}

impl From<MockableItem> for MockItem {
    fn from(mockable: MockableItem) -> MockItem {
        match mockable {
            MockableItem::Manual(_) => unimplemented!("1"),
            MockableItem::Module(mod_) => MockItem::Module(
                MockItemModule::from(mod_)
            )
        }
    }
}

impl ToTokens for MockItem {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        match self {
            MockItem::Module(mod_) => mod_.to_tokens(tokens),
            _ => unimplemented!("2")
        }
    }
}

struct MockFunction {
    /// Lifetime Generics of the mocked method that relate to the arguments but
    /// not the return value
    alifetimes: Generics,
    /// Names of the method arguments
    argnames: Vec<Pat>,
    /// Types of the method arguments
    argty: Vec<Type>,
    /// Type Generics of the Expectation object
    egenerics: Generics,
    /// The mock function's generic types as a list of types
    fn_params: Punctuated<Ident, Token![,]>,
    /// Is this for a static method or free function?
    is_static: bool,
    /// The mockable version of the function
    item_fn: ItemFn,
    /// name of the function's parent module
    mod_ident: Ident,
    /// Expressions that create the predicate arguments from the call arguments
    predexprs: Vec<TokenStream>,
    /// Types used for Predicates.  Will be almost the same as args, but every
    /// type will be a non-reference type.
    predty: Vec<Type>,
    /// References to every type in `predty`.
    refpredty: Vec<Type>
}

impl MockFunction {
    // TODO: return a Syn object instead of a TokenStream
    fn hrtb(&self) -> TokenStream {
        if self.alifetimes.params.is_empty() {
            TokenStream::default()
        } else {
            let ig = &self.alifetimes;
            quote!(for #ig)
        }
    }

}

impl From<(&Ident, ItemFn)> for MockFunction {
    /// Create a MockFunction from its mockable version and the name of its
    /// parent module
    fn from((ident, f): (&Ident, ItemFn)) -> MockFunction {
        let egenerics = Generics::default();    // TODO
        let mut argnames = Vec::new();
        let mut argty = Vec::new();
        let mut is_static = true;
        let mut predexprs = Vec::new();
        let mut predty = Vec::new();
        let mut refpredty = Vec::new();
        for fa in f.sig.inputs.iter() {
            if let FnArg::Typed(pt) = fa {
                let argname = (*pt.pat).clone();
                let aty = &pt.ty;
                //let aty = supersuperfy(&pt.ty, levels);
                if let Type::Reference(tr) = &**aty {
                    predexprs.push(quote!(#argname));
                    predty.push((*tr.elem).clone());
                    refpredty.push((**aty).clone());
                } else {
                    predexprs.push(quote!(&#argname));
                    predty.push((**aty).clone());
                    let tr = TypeReference {
                        and_token: Token![&](Span::call_site()),
                        lifetime: None,
                        mutability: None,
                        elem: Box::new((**aty).clone())
                    };
                    refpredty.push(Type::Reference(tr));
                };
                argnames.push(argname);
                argty.push((**aty).clone());
            } else {
                is_static = false;
                ()    // Strip out the "&self" argument
            }
        }
        let (mut egenerics, alifetimes, rlifetimes) = split_lifetimes(
            f.sig.generics.clone(),
            &f.sig.inputs,
            // TODO: supersuperfy the output
            &f.sig.output
        );
        let fn_params = Punctuated::<Ident, Token![,]>::from_iter(
            egenerics.type_params().map(|tp| tp.ident.clone())
        );
        MockFunction {
            alifetimes,
            argnames,
            argty,
            egenerics,
            fn_params,
            is_static,
            item_fn: f,
            mod_ident: ident.clone(),
            predexprs,
            predty,
            refpredty,
        }
    }
}

impl ToTokens for MockFunction {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let matcher = &Matcher{f: self};
        let mod_ident = format_ident!("__{}", &self.item_fn.sig.ident);
        let orig_signature = &self.item_fn.sig;
        quote!(
            #[allow(missing_docs)]
            pub mod #mod_ident {
                use super::*;
                use ::mockall::CaseTreeExt;
                use ::std::sync::MutexGuard;
                use ::std::{
                    mem,
                    ops::{DerefMut, Range},
                    sync::Mutex
                };
                enum Rfunc {} // 42 lines
                #matcher
                struct Common{} // 106 lines
                pub struct Expectation{}    // 208 lines
                pub struct Expectations{}   // 43 lines
                pub struct ExpectationGuard {} // 114 lines
                pub struct Context {}   // 44 lines
            }
            #orig_signature {}
            pub fn bar_context() {}
        ).to_tokens(tokens);
    }
}

struct Matcher<'a> {
    f: &'a MockFunction
}

impl<'a> ToTokens for Matcher<'a> {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let (ig, tg, wc) = self.f.egenerics.split_for_impl();
        let argnames = &self.f.argnames;
        let braces = argnames.iter()
            .fold(String::new(), |mut acc, _argname| {
                if acc.is_empty() {
                    acc.push_str("{}");
                } else {
                    acc.push_str(", {}");
                }
                acc
            });
        let fn_params = &self.f.fn_params;
        let hrtb = self.f.hrtb();
        let indices = (0..argnames.len())
            .map(|i| {
                syn::Index::from(i)
            }).collect::<Vec<_>>();
        let lg = &self.f.alifetimes;
        let pred_matches = TokenStream::from_iter(
            argnames.iter().enumerate()
            .map(|(i, argname)| {
                let idx = syn::Index::from(i);
                quote!(__mockall_pred.#idx.eval(#argname),)
            })
        );
        let preds = TokenStream::from_iter(
            self.f.predty.iter().map(|t|
                quote!(Box<dyn #hrtb ::mockall::Predicate<#t> + Send>,)
            )
        );
        let predty = &self.f.predty;
        let refpredty = &self.f.refpredty;
        quote!(
            enum Matcher #ig #wc {
                Always,
                Func(Box<dyn #hrtb Fn(#( #refpredty, )*) -> bool + Send>),
                // Version of Matcher::Func for closures that aren't Send
                FuncST(::mockall::Fragile<Box<dyn #hrtb Fn(#( #refpredty, )*) -> bool>>),
                Pred(Box<(#preds)>),
                // Prevent "unused type parameter" errors
                // Surprisingly, PhantomData<Fn(generics)> is Send even if
                // generics are not, unlike PhantomData<generics>
                _Phantom(Box<dyn Fn(#fn_params) -> () + Send>)
            }
            impl #ig Matcher #tg #wc {
                fn matches #lg (&self, #( #argnames: &#predty, )*) -> bool {
                    match self {
                        Matcher::Always => true,
                        Matcher::Func(__mockall_f) =>
                            __mockall_f(#(#argnames, )*),
                        Matcher::FuncST(__mockall_f) =>
                            (__mockall_f.get())(#(#argnames, )*),
                        Matcher::Pred(__mockall_pred) =>
                            [#pred_matches]
                            .iter()
                            .all(|__mockall_x| *__mockall_x),
                        _ => unreachable!()
                    }
                }
            }

            impl #ig Default for Matcher #tg #wc {
                #[allow(unused_variables)]
                fn default() -> Self {
                    Matcher::Always
                }
            }

            impl #ig ::std::fmt::Display for Matcher #tg #wc {
                fn fmt(&self, __mockall_fmt: &mut ::std::fmt::Formatter<'_>)
                    -> ::std::fmt::Result
                {
                    match self {
                        Matcher::Always => write!(__mockall_fmt, "<anything>"),
                        Matcher::Func(_) => write!(__mockall_fmt, "<function>"),
                        Matcher::FuncST(_) => write!(__mockall_fmt, "<single threaded function>"),
                        Matcher::Pred(__mockall_p) => {
                            write!(__mockall_fmt, #braces,
                                #(__mockall_p.#indices,)*)
                        }
                        _ => unreachable!(),
                    }
                }
            }
        ).to_tokens(tokens);
    }
}enum MockItemContent {
    Fn(MockFunction),
    Tokens(TokenStream)
}

pub(crate) struct MockItemModule {
    vis: Visibility,
    mock_ident: Ident,
    orig_ident: Option<Ident>,
    content: Vec<MockItemContent>
}

fn mock_fn(item_fn: ItemFn) -> ItemMod {
    let ident = format_ident!("__{}", item_fn.sig.ident);
    unimplemented!("5")
}

/// Generate a whole mod module from the mockable sort
impl From<MockableModule> for ItemMod {
    fn from(mod_: MockableModule) -> ItemMod {
        let attrs = Vec::new();
        let vis = mod_.vis;
        let mod_token = mod_.mod_token;
        let ident = mod_.mock_ident;
        let semi = None;
        let brace = token::Brace::default();

        let mut contents = Vec::<Item>::new();
        contents.push(Item::Verbatim(quote!(use super::T;)));
        for item in mod_.content.into_iter() {
            match item {
                Item::Fn(item_fn) => {
                    contents.push(
                        Item::Verbatim(quote!(#[allow(missing_docs)]))
                    );
                    contents.push(Item::Mod(mock_fn(item_fn)));
                },
                _ => {
                    compile_error(item.span(),
                        "Unsupported item type when mocking modules");
                }
            }
        }

        let content = Some((brace, contents));
        ItemMod {
            attrs,
            vis,
            mod_token,
            ident,
            content,
            semi
        }
    }
}

impl From<MockableModule> for MockItemModule {
    fn from(mod_: MockableModule) -> MockItemModule {
        let mock_ident = mod_.mock_ident.clone();
        let orig_ident = mod_.orig_ident;
        let content = mod_.content.into_iter().filter_map(|item| {
            let span = item.span();
            match item {
                Item::ExternCrate(_) | Item::Impl(_) =>
                {
                    // Ignore
                    None
                },
                Item::Static(is) => {
                    Some(MockItemContent::Tokens(is.into_token_stream()))
                },
                Item::Const(ic) => {
                    Some(MockItemContent::Tokens(ic.into_token_stream()))
                },
                Item::Fn(f) => {
                    Some(MockItemContent::Fn(
                        MockFunction::from((&mock_ident, f))
                    ))
                },
                Item::Mod(_) | Item::ForeignMod(_)
                    | Item::Struct(_) | Item::Enum(_)
                    | Item::Union(_) | Item::Trait(_) =>
                {
                    compile_error(span,
                        "Mockall does not yet support deriving nested mocks");
                    None
                },
                Item::Type(ty) => {
                    Some(MockItemContent::Tokens(ty.into_token_stream()))
                },
                Item::TraitAlias(ta) => {
                    Some(MockItemContent::Tokens(ta.into_token_stream()))
                },
                Item::Use(u) => {
                    Some(MockItemContent::Tokens(u.into_token_stream()))
                },
                _ => {
                    compile_error(span, "Unsupported item");
                    None
                }
            }
        }).collect::<Vec<_>>();

        MockItemModule {
            vis: mod_.vis,
            mock_ident: mod_.mock_ident,
            orig_ident,
            content
        }
    }
}

impl ToTokens for MockItemModule {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let mut body = TokenStream::new();
        let mut cp_body = TokenStream::new();
        let modname = &self.mock_ident;

        for item in self.content.iter() {
            match item {
                MockItemContent::Tokens(ts) => ts.to_tokens(&mut body),
                MockItemContent::Fn(f) => {
                    //let mod_ident = format_ident!("__{}", &f.sig.ident);
                    //quote!(
                        //let __mockall_timeses = #mod_ident::EXPECTATIONS.lock()
                            //.unwrap()
                            //.checkpoint()
                            //.collect::<Vec<_>>();
                    //).to_tokens(&mut cp_body);
                    f.to_tokens(&mut body);
                },
            }
        }

        quote!(
            /// Verify that all current expectations for this function are
            /// satisfied and clear them.
            pub fn checkpoint() { #cp_body }
        ).to_tokens(&mut body);
        let docstr = {
            let inner_ds = if let Some(ident) = &self.orig_ident {
                format!("Mock version of the `{}` module", ident)
            } else {
                format!("TODO: write doc string")
            };
            quote!( #[doc = #inner_ds])
        };
        quote!(
            #docstr
            pub mod #modname { #body }
        ).to_tokens(tokens);
    }
}
<commit_msg>Now generating correct code for Rfunc, for funcs with static output<commit_after>// vim: tw=80
use super::*;

use quote::ToTokens;

use crate::{
    mockable_item::{MockableItem, MockableModule}
};

/// A Mock item
pub(crate) enum MockItem {
    Module(MockItemModule)
}

impl From<MockableItem> for MockItem {
    fn from(mockable: MockableItem) -> MockItem {
        match mockable {
            MockableItem::Manual(_) => unimplemented!("1"),
            MockableItem::Module(mod_) => MockItem::Module(
                MockItemModule::from(mod_)
            )
        }
    }
}

impl ToTokens for MockItem {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        match self {
            MockItem::Module(mod_) => mod_.to_tokens(tokens),
            _ => unimplemented!("2")
        }
    }
}

struct MockFunction {
    /// Lifetime Generics of the mocked method that relate to the arguments but
    /// not the return value
    alifetimes: Generics,
    /// Names of the method arguments
    argnames: Vec<Pat>,
    /// Types of the method arguments
    argty: Vec<Type>,
    /// Type Generics of the Expectation object
    egenerics: Generics,
    /// The mock function's generic types as a list of types
    fn_params: Punctuated<Ident, Token![,]>,
    /// Is this for a static method or free function?
    is_static: bool,
    /// The mockable version of the function
    item_fn: ItemFn,
    /// name of the function's parent module
    mod_ident: Ident,
    /// Output type of the Method, supersuperfied.
    output: Type,
    /// Expressions that create the predicate arguments from the call arguments
    predexprs: Vec<TokenStream>,
    /// Types used for Predicates.  Will be almost the same as args, but every
    /// type will be a non-reference type.
    predty: Vec<Type>,
    /// References to every type in `predty`.
    refpredty: Vec<Type>
}

impl MockFunction {
    // TODO: return a Syn object instead of a TokenStream
    fn hrtb(&self) -> TokenStream {
        if self.alifetimes.params.is_empty() {
            TokenStream::default()
        } else {
            let ig = &self.alifetimes;
            quote!(for #ig)
        }
    }

}

impl From<(&Ident, ItemFn)> for MockFunction {
    /// Create a MockFunction from its mockable version and the name of its
    /// parent module
    fn from((ident, f): (&Ident, ItemFn)) -> MockFunction {
        let egenerics = Generics::default();    // TODO
        let mut argnames = Vec::new();
        let mut argty = Vec::new();
        let mut is_static = true;
        let mut predexprs = Vec::new();
        let mut predty = Vec::new();
        let mut refpredty = Vec::new();
        for fa in f.sig.inputs.iter() {
            if let FnArg::Typed(pt) = fa {
                let argname = (*pt.pat).clone();
                let aty = &pt.ty;
                //let aty = supersuperfy(&pt.ty, levels);
                if let Type::Reference(tr) = &**aty {
                    predexprs.push(quote!(#argname));
                    predty.push((*tr.elem).clone());
                    refpredty.push((**aty).clone());
                } else {
                    predexprs.push(quote!(&#argname));
                    predty.push((**aty).clone());
                    let tr = TypeReference {
                        and_token: Token![&](Span::call_site()),
                        lifetime: None,
                        mutability: None,
                        elem: Box::new((**aty).clone())
                    };
                    refpredty.push(Type::Reference(tr));
                };
                argnames.push(argname);
                argty.push((**aty).clone());
            } else {
                is_static = false;
                ()    // Strip out the "&self" argument
            }
        }
        // TODO: supersuperfy the output
        let output = match f.sig.output.clone() {
            ReturnType::Default => Type::Tuple(TypeTuple{
                paren_token: token::Paren(Span::call_site()),
                elems: Punctuated::new()
            }),
            ReturnType::Type(_, ty) => (*ty).clone()
        };
        let (mut egenerics, alifetimes, rlifetimes) = split_lifetimes(
            f.sig.generics.clone(),
            &f.sig.inputs,
            &f.sig.output
        );
        let fn_params = Punctuated::<Ident, Token![,]>::from_iter(
            egenerics.type_params().map(|tp| tp.ident.clone())
        );
        MockFunction {
            alifetimes,
            argnames,
            argty,
            egenerics,
            fn_params,
            is_static,
            item_fn: f,
            mod_ident: ident.clone(),
            output,
            predexprs,
            predty,
            refpredty,
        }
    }
}

impl ToTokens for MockFunction {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let matcher = &Matcher{f: self};
        let rfunc = &Rfunc{f: self};
        let mod_ident = format_ident!("__{}", &self.item_fn.sig.ident);
        let orig_signature = &self.item_fn.sig;
        quote!(
            #[allow(missing_docs)]
            pub mod #mod_ident {
                use super::*;
                use ::mockall::CaseTreeExt;
                use ::std::sync::MutexGuard;
                use ::std::{
                    mem,
                    ops::{DerefMut, Range},
                    sync::Mutex
                };
                #rfunc
                #matcher
                struct Common{} // 106 lines
                pub struct Expectation{}    // 208 lines
                pub struct Expectations{}   // 43 lines
                pub struct ExpectationGuard {} // 114 lines
                pub struct Context {}   // 44 lines
            }
            #orig_signature {}
            pub fn bar_context() {}
        ).to_tokens(tokens);
    }
}

struct Matcher<'a> {
    f: &'a MockFunction
}

impl<'a> ToTokens for Matcher<'a> {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let (ig, tg, wc) = self.f.egenerics.split_for_impl();
        let argnames = &self.f.argnames;
        let braces = argnames.iter()
            .fold(String::new(), |mut acc, _argname| {
                if acc.is_empty() {
                    acc.push_str("{}");
                } else {
                    acc.push_str(", {}");
                }
                acc
            });
        let fn_params = &self.f.fn_params;
        let hrtb = self.f.hrtb();
        let indices = (0..argnames.len())
            .map(|i| {
                syn::Index::from(i)
            }).collect::<Vec<_>>();
        let lg = &self.f.alifetimes;
        let pred_matches = TokenStream::from_iter(
            argnames.iter().enumerate()
            .map(|(i, argname)| {
                let idx = syn::Index::from(i);
                quote!(__mockall_pred.#idx.eval(#argname),)
            })
        );
        let preds = TokenStream::from_iter(
            self.f.predty.iter().map(|t|
                quote!(Box<dyn #hrtb ::mockall::Predicate<#t> + Send>,)
            )
        );
        let predty = &self.f.predty;
        let refpredty = &self.f.refpredty;
        quote!(
            enum Matcher #ig #wc {
                Always,
                Func(Box<dyn #hrtb Fn(#( #refpredty, )*) -> bool + Send>),
                // Version of Matcher::Func for closures that aren't Send
                FuncST(::mockall::Fragile<Box<dyn #hrtb Fn(#( #refpredty, )*) -> bool>>),
                Pred(Box<(#preds)>),
                // Prevent "unused type parameter" errors
                // Surprisingly, PhantomData<Fn(generics)> is Send even if
                // generics are not, unlike PhantomData<generics>
                _Phantom(Box<dyn Fn(#fn_params) -> () + Send>)
            }
            impl #ig Matcher #tg #wc {
                fn matches #lg (&self, #( #argnames: &#predty, )*) -> bool {
                    match self {
                        Matcher::Always => true,
                        Matcher::Func(__mockall_f) =>
                            __mockall_f(#(#argnames, )*),
                        Matcher::FuncST(__mockall_f) =>
                            (__mockall_f.get())(#(#argnames, )*),
                        Matcher::Pred(__mockall_pred) =>
                            [#pred_matches]
                            .iter()
                            .all(|__mockall_x| *__mockall_x),
                        _ => unreachable!()
                    }
                }
            }

            impl #ig Default for Matcher #tg #wc {
                #[allow(unused_variables)]
                fn default() -> Self {
                    Matcher::Always
                }
            }

            impl #ig ::std::fmt::Display for Matcher #tg #wc {
                fn fmt(&self, __mockall_fmt: &mut ::std::fmt::Formatter<'_>)
                    -> ::std::fmt::Result
                {
                    match self {
                        Matcher::Always => write!(__mockall_fmt, "<anything>"),
                        Matcher::Func(_) => write!(__mockall_fmt, "<function>"),
                        Matcher::FuncST(_) => write!(__mockall_fmt, "<single threaded function>"),
                        Matcher::Pred(__mockall_p) => {
                            write!(__mockall_fmt, #braces,
                                #(__mockall_p.#indices,)*)
                        }
                        _ => unreachable!(),
                    }
                }
            }
        ).to_tokens(tokens);
    }
}

struct Rfunc<'a> {
    f: &'a MockFunction
}

impl<'a> ToTokens for Rfunc<'a> {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let argnames = &self.f.argnames;
        let argty = &self.f.argty;
        let fn_params = &self.f.fn_params;
        let (ig, tg, wc) = self.f.egenerics.split_for_impl();
        let hrtb = self.f.hrtb();
        let lg = &self.f.alifetimes;
        let output = &self.f.output;
        quote!(
            enum Rfunc #ig #wc {
                Default,
                // Indicates that a `return_once` expectation has already
                // returned
                Expired,
                Mut(Box<dyn #hrtb FnMut(#(#argty, )*) -> #output + Send>),
                // Version of Rfunc::Mut for closures that aren't Send
                MutST(::mockall::Fragile<
                    Box<dyn #hrtb FnMut(#(#argty, )*) -> #output >>
                ),
                Once(Box<dyn #hrtb FnOnce(#(#argty, )*) -> #output + Send>),
                // Version of Rfunc::Once for closure that aren't Send
                OnceST(::mockall::Fragile<
                    Box<dyn #hrtb FnOnce(#(#argty, )*) -> #output>>
                ),
                // Prevent "unused type parameter" errors Surprisingly,
                // PhantomData<Fn(generics)> is Send even if generics are not,
                // unlike PhantomData<generics>
                _Phantom(Box<dyn Fn(#fn_params) -> () + Send>)
            }

            impl #ig  Rfunc #tg #wc {
                fn call_mut #lg (&mut self, #( #argnames: #argty, )* )
                    -> std::result::Result<#output, &'static str>
                {
                    match self {
                        Rfunc::Default => {
                            use ::mockall::ReturnDefault;
                            ::mockall::DefaultReturner::<#output>
                                ::return_default()
                        },
                        Rfunc::Expired => {
                            Err("called twice, but it returns by move")
                        },
                        Rfunc::Mut(__mockall_f) => {
                            Ok(__mockall_f( #(#argnames, )* ))
                        },
                        Rfunc::MutST(__mockall_f) => {
                            Ok((__mockall_f.get_mut())(#(#argnames,)*))
                        },
                        Rfunc::Once(_) => {
                            if let Rfunc::Once(mut __mockall_f) =
                                mem::replace(self, Rfunc::Expired) {
                                Ok(__mockall_f( #(#argnames, )* ))
                            } else {
                                unreachable!()
                            }
                        },
                        Rfunc::OnceST(_) => {
                            if let Rfunc::OnceST(mut __mockall_f) =
                                mem::replace(self, Rfunc::Expired) {
                                Ok((__mockall_f.into_inner())(#(#argnames,)*))
                            } else {
                                unreachable!()
                            }
                        },
                        Rfunc::_Phantom(_) => unreachable!()
                    }
                }
            }

            impl #ig std::default::Default for Rfunc #tg #wc
            {
                fn default() -> Self {
                    Rfunc::Default
                }
            }
        ).to_tokens(tokens);
    }
}

enum MockItemContent {
    Fn(MockFunction),
    Tokens(TokenStream)
}

pub(crate) struct MockItemModule {
    vis: Visibility,
    mock_ident: Ident,
    orig_ident: Option<Ident>,
    content: Vec<MockItemContent>
}

fn mock_fn(item_fn: ItemFn) -> ItemMod {
    let ident = format_ident!("__{}", item_fn.sig.ident);
    unimplemented!("5")
}

/// Generate a whole mod module from the mockable sort
impl From<MockableModule> for ItemMod {
    fn from(mod_: MockableModule) -> ItemMod {
        let attrs = Vec::new();
        let vis = mod_.vis;
        let mod_token = mod_.mod_token;
        let ident = mod_.mock_ident;
        let semi = None;
        let brace = token::Brace::default();

        let mut contents = Vec::<Item>::new();
        contents.push(Item::Verbatim(quote!(use super::T;)));
        for item in mod_.content.into_iter() {
            match item {
                Item::Fn(item_fn) => {
                    contents.push(
                        Item::Verbatim(quote!(#[allow(missing_docs)]))
                    );
                    contents.push(Item::Mod(mock_fn(item_fn)));
                },
                _ => {
                    compile_error(item.span(),
                        "Unsupported item type when mocking modules");
                }
            }
        }

        let content = Some((brace, contents));
        ItemMod {
            attrs,
            vis,
            mod_token,
            ident,
            content,
            semi
        }
    }
}

impl From<MockableModule> for MockItemModule {
    fn from(mod_: MockableModule) -> MockItemModule {
        let mock_ident = mod_.mock_ident.clone();
        let orig_ident = mod_.orig_ident;
        let content = mod_.content.into_iter().filter_map(|item| {
            let span = item.span();
            match item {
                Item::ExternCrate(_) | Item::Impl(_) =>
                {
                    // Ignore
                    None
                },
                Item::Static(is) => {
                    Some(MockItemContent::Tokens(is.into_token_stream()))
                },
                Item::Const(ic) => {
                    Some(MockItemContent::Tokens(ic.into_token_stream()))
                },
                Item::Fn(f) => {
                    Some(MockItemContent::Fn(
                        MockFunction::from((&mock_ident, f))
                    ))
                },
                Item::Mod(_) | Item::ForeignMod(_)
                    | Item::Struct(_) | Item::Enum(_)
                    | Item::Union(_) | Item::Trait(_) =>
                {
                    compile_error(span,
                        "Mockall does not yet support deriving nested mocks");
                    None
                },
                Item::Type(ty) => {
                    Some(MockItemContent::Tokens(ty.into_token_stream()))
                },
                Item::TraitAlias(ta) => {
                    Some(MockItemContent::Tokens(ta.into_token_stream()))
                },
                Item::Use(u) => {
                    Some(MockItemContent::Tokens(u.into_token_stream()))
                },
                _ => {
                    compile_error(span, "Unsupported item");
                    None
                }
            }
        }).collect::<Vec<_>>();

        MockItemModule {
            vis: mod_.vis,
            mock_ident: mod_.mock_ident,
            orig_ident,
            content
        }
    }
}

impl ToTokens for MockItemModule {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let mut body = TokenStream::new();
        let mut cp_body = TokenStream::new();
        let modname = &self.mock_ident;

        for item in self.content.iter() {
            match item {
                MockItemContent::Tokens(ts) => ts.to_tokens(&mut body),
                MockItemContent::Fn(f) => {
                    //let mod_ident = format_ident!("__{}", &f.sig.ident);
                    //quote!(
                        //let __mockall_timeses = #mod_ident::EXPECTATIONS.lock()
                            //.unwrap()
                            //.checkpoint()
                            //.collect::<Vec<_>>();
                    //).to_tokens(&mut cp_body);
                    f.to_tokens(&mut body);
                },
            }
        }

        quote!(
            /// Verify that all current expectations for this function are
            /// satisfied and clear them.
            pub fn checkpoint() { #cp_body }
        ).to_tokens(&mut body);
        let docstr = {
            let inner_ds = if let Some(ident) = &self.orig_ident {
                format!("Mock version of the `{}` module", ident)
            } else {
                format!("TODO: write doc string")
            };
            quote!( #[doc = #inner_ds])
        };
        quote!(
            #docstr
            pub mod #modname { #body }
        ).to_tokens(tokens);
    }
}
<|endoftext|>


<commit_before>//
//   Copyright 2016 Andrew Hunter
//
//   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 std::rc::*;

use super::component::*;
use super::super::tree::*;

struct FunctionComponent;

impl Component for FunctionComponent {
}

impl Drop for FunctionComponent {
    fn drop(&mut self) {
    }
}

///
/// Simplest form of 'component function': a function that receives a `TreeChange` indicating how the
/// input tree has changed, and returns a new change indicating how the output has changed.
///
impl ConvertToComponent for Box<Fn(&TreeChange) -> TreeChange> {
    ///
    /// Creates a component that consumes from a particular tree and publishes to a different tree
    ///
    fn into_component(self, consumer: ConsumerRef, publisher: PublisherRef) -> ComponentRef {
        let mut our_consumer    = consumer;
        let mut our_publisher   = publisher;
        let action              = self;

        our_consumer.subscribe(TreeAddress::Here, TreeExtent::SubTree, Box::new(move |change| {
            let change_result = action(change);
            our_publisher.publish(change_result);
        }));

        return Rc::new(FunctionComponent);
    }
}

///
/// Simplest form of 'component function': a function that receives a `TreeChange` indicating how the
/// input tree has changed, and returns a new change indicating how the output has changed.
///
/// This variant allows for mutable state.
///
impl ConvertToComponent for Box<FnMut(&TreeChange) -> TreeChange> {
    ///
    /// Creates a component that consumes from a particular tree and publishes to a different tree
    ///
    fn into_component(self, consumer: ConsumerRef, publisher: PublisherRef) -> ComponentRef {
        let mut our_consumer    = consumer;
        let mut our_publisher   = publisher;
        let mut action          = self;

        our_consumer.subscribe(TreeAddress::Here, TreeExtent::SubTree, Box::new(move |change| {
            let change_result = action(change);
            our_publisher.publish(change_result);
        }));

        return Rc::new(FunctionComponent);
    }
}

///
/// Provides a component function that converts an input tree to an output tree
///
impl ConvertToComponent for Box<Fn(&TreeRef) -> TreeRef> {
    ///
    /// Creates a component that consumes from a particular tree and publishes to a different tree
    ///
    fn into_component(self, consumer: ConsumerRef, publisher: PublisherRef) -> ComponentRef {
        let mut our_consumer    = consumer;
        let mut our_publisher   = publisher;
        let action              = self;

        let mut tree = "empty".to_tree_node();

        our_consumer.subscribe(TreeAddress::Here, TreeExtent::SubTree, Box::new(move |change| {
            tree = change.apply(&tree);

            let new_tree = action(&tree);

            our_publisher.publish(TreeChange::new(&TreeAddress::Here, TreeChangeType::Child, Some(&new_tree)));
        }));

        return Rc::new(FunctionComponent);
    }
}

///
/// Provides a component function that converts an input tree to an output tree
///
/// This variant allows for mutable state
///
impl ConvertToComponent for Box<FnMut(&TreeRef) -> TreeRef> {
    ///
    /// Creates a component that consumes from a particular tree and publishes to a different tree
    ///
    fn into_component(self, consumer: ConsumerRef, publisher: PublisherRef) -> ComponentRef {
        let mut our_consumer    = consumer;
        let mut our_publisher   = publisher;
        let mut action          = self;

        let mut tree = "empty".to_tree_node();

        our_consumer.subscribe(TreeAddress::Here, TreeExtent::SubTree, Box::new(move |change| {
            tree = change.apply(&tree);

            let new_tree = action(&tree);

            our_publisher.publish(TreeChange::new(&TreeAddress::Here, TreeChangeType::Child, Some(&new_tree)));
        }));

        return Rc::new(FunctionComponent);
    }
}

///
/// Makes a function into a variant that can be used with a suitable `into_component()` call.
///
/// Short for 'make component'
///
/// For example:
///
/// ```
/// # use tametree::component::*;
/// # use tametree::component::immediate_publisher::*;
/// #
/// # let input_publisher   = ImmediatePublisher::new();
/// # let consumer          = input_publisher.create_consumer();
/// # let publisher         = ImmediatePublisher::new();
/// let component = mk_com(|tree: &TreeRef| { tree.clone() }).into_component(consumer, publisher);
/// ```
///
/// This exists to get around some limitations in rust's type inference.
///
/// What would be neat is if we could do `(|change: &TreeRef| -> TreeRef { ... }).into_component()`
/// and have rust figure out that there's an implementation on a `Fn(&TreeRef) -> TreeRef` that we
/// can use to generate the result. Even `Box::new(...).into_component()` would be OK.
///
/// However, closures and Fns are different types and don't get coerced implicitly so that doesn't work.
/// That is, `Box::new(...).into_component()` will produce an error as the box has the closure type and rust
/// isn't smart enough to realise there's a cast to `Box<Fn()>`
///
/// Here's what you have to do as a result:
///
/// ```
/// # use tametree::component::*;
/// # use tametree::component::immediate_publisher::*;
/// #
/// # let input_publisher   = ImmediatePublisher::new();
/// # let consumer          = input_publisher.create_consumer();
/// # let publisher         = ImmediatePublisher::new();
/// let component_fn: Box<Fn(&TreeRef) -> TreeRef> = Box::new(|tree: &TreeRef| { tree.clone() });
/// let component = component_fn.into_component(consumer, publisher);
/// ```
///
/// Type inference: utterly defeated. (`Box::<Fn blah blah>::new` doesn't work either because it coerces the
/// closure to the Fn trait too early and thus produces an error)
///
/// To make this less of a nightmare to use, the mk_com function tells rust that a function can be boxed and
/// helps out by inferring the various parameters.
///
#[inline]
pub fn mk_com<TIn, TOut, F>(func: F) -> Box<Fn(&TIn) -> TOut> where F: Fn(&TIn) -> TOut + 'static {
    Box::new(func)
}

#[cfg(test)]
mod component_function_tests {
    use super::super::super::component::*;
    use super::super::immediate_publisher::*;
    use super::super::output_tree_publisher::*;

    #[test]
    pub fn can_create_tree_change_component() {
        let mut input_publisher = ImmediatePublisher::new();
        let consumer            = input_publisher.create_consumer();

        let output_publisher    = OutputTreePublisher::new();
        let result_reader       = output_publisher.get_tree_reader();

        let _component = mk_com(|_change: &TreeChange| {
            TreeChange::new(&TreeAddress::Here, TreeChangeType::Child, Some(&"passed".to_tree_node()))
        }).into_component(consumer, output_publisher);

        // Publish something to our function
        input_publisher.publish(TreeChange::new(&TreeAddress::Here, TreeChangeType::Child, Some(&"test".to_tree_node())));

        // Check that the output was 'passed'
        let result = result_reader();
        assert!(result.get_tag() == "passed")
    }
}
<commit_msg>Add component() to remove the need to call into_component separately<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   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 std::rc::*;

use super::component::*;
use super::super::tree::*;

struct FunctionComponent;

impl Component for FunctionComponent {
}

impl Drop for FunctionComponent {
    fn drop(&mut self) {
    }
}

///
/// Simplest form of 'component function': a function that receives a `TreeChange` indicating how the
/// input tree has changed, and returns a new change indicating how the output has changed.
///
impl ConvertToComponent for Box<Fn(&TreeChange) -> TreeChange> {
    ///
    /// Creates a component that consumes from a particular tree and publishes to a different tree
    ///
    fn into_component(self, consumer: ConsumerRef, publisher: PublisherRef) -> ComponentRef {
        let mut our_consumer    = consumer;
        let mut our_publisher   = publisher;
        let action              = self;

        our_consumer.subscribe(TreeAddress::Here, TreeExtent::SubTree, Box::new(move |change| {
            let change_result = action(change);
            our_publisher.publish(change_result);
        }));

        return Rc::new(FunctionComponent);
    }
}

///
/// Simplest form of 'component function': a function that receives a `TreeChange` indicating how the
/// input tree has changed, and returns a new change indicating how the output has changed.
///
/// This variant allows for mutable state.
///
impl ConvertToComponent for Box<FnMut(&TreeChange) -> TreeChange> {
    ///
    /// Creates a component that consumes from a particular tree and publishes to a different tree
    ///
    fn into_component(self, consumer: ConsumerRef, publisher: PublisherRef) -> ComponentRef {
        let mut our_consumer    = consumer;
        let mut our_publisher   = publisher;
        let mut action          = self;

        our_consumer.subscribe(TreeAddress::Here, TreeExtent::SubTree, Box::new(move |change| {
            let change_result = action(change);
            our_publisher.publish(change_result);
        }));

        return Rc::new(FunctionComponent);
    }
}

///
/// Provides a component function that converts an input tree to an output tree
///
impl ConvertToComponent for Box<Fn(&TreeRef) -> TreeRef> {
    ///
    /// Creates a component that consumes from a particular tree and publishes to a different tree
    ///
    fn into_component(self, consumer: ConsumerRef, publisher: PublisherRef) -> ComponentRef {
        let mut our_consumer    = consumer;
        let mut our_publisher   = publisher;
        let action              = self;

        let mut tree = "empty".to_tree_node();

        our_consumer.subscribe(TreeAddress::Here, TreeExtent::SubTree, Box::new(move |change| {
            tree = change.apply(&tree);

            let new_tree = action(&tree);

            our_publisher.publish(TreeChange::new(&TreeAddress::Here, TreeChangeType::Child, Some(&new_tree)));
        }));

        return Rc::new(FunctionComponent);
    }
}

///
/// Provides a component function that converts an input tree to an output tree
///
/// This variant allows for mutable state
///
impl ConvertToComponent for Box<FnMut(&TreeRef) -> TreeRef> {
    ///
    /// Creates a component that consumes from a particular tree and publishes to a different tree
    ///
    fn into_component(self, consumer: ConsumerRef, publisher: PublisherRef) -> ComponentRef {
        let mut our_consumer    = consumer;
        let mut our_publisher   = publisher;
        let mut action          = self;

        let mut tree = "empty".to_tree_node();

        our_consumer.subscribe(TreeAddress::Here, TreeExtent::SubTree, Box::new(move |change| {
            tree = change.apply(&tree);

            let new_tree = action(&tree);

            our_publisher.publish(TreeChange::new(&TreeAddress::Here, TreeChangeType::Child, Some(&new_tree)));
        }));

        return Rc::new(FunctionComponent);
    }
}

///
/// Makes a function into a variant that can be used with a suitable `into_component()` call.
///
/// Short for 'make component'
///
/// For example:
///
/// ```
/// # use tametree::component::*;
/// # use tametree::component::immediate_publisher::*;
/// #
/// # let input_publisher   = ImmediatePublisher::new();
/// # let consumer          = input_publisher.create_consumer();
/// # let publisher         = ImmediatePublisher::new();
/// let component = mk_com(|tree: &TreeRef| { tree.clone() }).into_component(consumer, publisher);
/// ```
///
/// This exists to get around some limitations in rust's type inference.
///
/// What would be neat is if we could do `(|change: &TreeRef| -> TreeRef { ... }).into_component()`
/// and have rust figure out that there's an implementation on a `Fn(&TreeRef) -> TreeRef` that we
/// can use to generate the result. Even `Box::new(...).into_component()` would be OK.
///
/// However, closures and Fns are different types and don't get coerced implicitly so that doesn't work.
/// That is, `Box::new(...).into_component()` will produce an error as the box has the closure type and rust
/// isn't smart enough to realise there's a cast to `Box<Fn()>`
///
/// Here's what you have to do as a result:
///
/// ```
/// # use tametree::component::*;
/// # use tametree::component::immediate_publisher::*;
/// #
/// # let input_publisher   = ImmediatePublisher::new();
/// # let consumer          = input_publisher.create_consumer();
/// # let publisher         = ImmediatePublisher::new();
/// let component_fn: Box<Fn(&TreeRef) -> TreeRef> = Box::new(|tree: &TreeRef| { tree.clone() });
/// let component = component_fn.into_component(consumer, publisher);
/// ```
///
/// Type inference: utterly defeated. (`Box::<Fn blah blah>::new` doesn't work either because it coerces the
/// closure to the Fn trait too early and thus produces an error)
///
/// To make this less of a nightmare to use, the mk_com function tells rust that a function can be boxed and
/// helps out by inferring the various parameters.
///
#[inline]
pub fn mk_com<TIn, TOut, F>(func: F) -> Box<Fn(&TIn) -> TOut> where F: Fn(&TIn) -> TOut + 'static {
    Box::new(func)
}

///
/// Starts running a function as a component
///
/// # Exmaple
///
/// ```
/// # use tametree::component::*;
/// # use tametree::component::immediate_publisher::*;
/// #
/// # let input_publisher   = ImmediatePublisher::new();
/// # let consumer          = input_publisher.create_consumer();
/// # let publisher         = ImmediatePublisher::new();
/// let pass_through_component = component(consumer, publisher, |tree: &TreeRef| { tree.clone() });
/// ```
///
#[inline]
pub fn component<TIn, TOut, F>(consumer: ConsumerRef, publisher: PublisherRef, func: F) -> ComponentRef
    where   F: Fn(&TIn) -> TOut + 'static,
            Box<Fn(&TIn) -> TOut> : ConvertToComponent {
    mk_com(func).into_component(consumer, publisher)
}

#[cfg(test)]
mod component_function_tests {
    use super::super::super::component::*;
    use super::super::immediate_publisher::*;
    use super::super::output_tree_publisher::*;

    #[test]
    pub fn can_create_tree_change_component() {
        let mut input_publisher = ImmediatePublisher::new();
        let consumer            = input_publisher.create_consumer();

        let output_publisher    = OutputTreePublisher::new();
        let result_reader       = output_publisher.get_tree_reader();

        let _component = component(|_change: &TreeChange| {
            TreeChange::new(&TreeAddress::Here, TreeChangeType::Child, Some(&"passed".to_tree_node()))
        });

        // Publish something to our function
        input_publisher.publish(TreeChange::new(&TreeAddress::Here, TreeChangeType::Child, Some(&"test".to_tree_node())));

        // Check that the output was 'passed'
        let result = result_reader();
        assert!(result.get_tag() == "passed")
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

use quote::Tokens;
use syn::{Body, Ident, MacroInput, Path, VariantData};
use syn::Body::Struct;
use syn::Ty;

use string::snake_to_camel;

/// Expand the required traits for the derive Settings attribute.
pub fn expand_settings_enum(mut ast: MacroInput) -> Tokens {
    let original_name = ast.ident.clone();
    let variant_name = Ident::new(format!("{}Variant", original_name));
    ast.ident = Ident::new(format!("_{}", original_name));
    let name = ast.ident.clone();
    let variant_enum = to_enums(&original_name, &ast.ident, &variant_name, &ast.body);
    let settings_impl = to_settings_impl(&original_name, &name, &variant_name, &ast.body);
    quote! {
        #[derive(Default)]
        #ast

        #variant_enum

        #settings_impl
    }
}

/// Create the variant enums for getters and setters.
fn to_enums(original_name: &Ident, new_name: &Ident, variant_name: &Ident, settings_struct: &Body) -> Tokens {
    if let &Struct(VariantData::Struct(ref strct)) = settings_struct {
        let mut field_names = vec![];
        let mut names = vec![];
        let mut types = vec![];
        for field in strct {
            if let Some(ref ident) = field.ident {
                field_names.push(ident);
                let ident = Ident::new(snake_to_camel(&ident.to_string()));
                names.push(ident);
                types.push(field.ty.clone());
            }
        }
        let string_names = field_names.iter()
            .map(|ident| ident.to_string().replace("_", "-"));
        let names1 = &names;
        let names2 = &names;
        let qualified_names = names.iter()
            .map(|ident| quote! {
                #original_name::#ident
            });
        quote! {
            #[derive(Clone)]
            enum #variant_name {
                #(#names1(#types)),*
            }

            enum #original_name {
                #(#names2),*
            }

            impl ::std::fmt::Display for #original_name {
                fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
                    match *self {
                        #(#qualified_names => write!(formatter, #string_names)),*
                    }
                }
            }

            impl #original_name {
                pub fn new() -> #new_name {
                    #new_name::default()
                }
            }
        }
    }
    else {
        panic!("Not a struct");
    }
}

/// Create the impl Settings.
fn to_settings_impl(original_name: &Ident, name: &Ident, variant_name: &Ident, settings_struct: &Body) -> Tokens {
    if let &Struct(VariantData::Struct(ref strct)) = settings_struct {
        let mut names = vec![];
        let mut capitalized_names = vec![];
        let mut types = vec![];
        for field in strct {
            if let Some(ref ident) = field.ident {
                let ident_string = ident.to_string();
                let ident = Ident::new(ident_string.clone());
                names.push(ident);
                let ident = Ident::new(snake_to_camel(&ident_string));
                capitalized_names.push(ident);

                if let Ty::Path(_, Path { ref segments, .. }) = field.ty {
                    types.push(to_value_type(&segments[0].ident));
                }
            }
        }
        let string_names: Vec<_> = names.iter()
            .map(|ident| ident.to_string().replace("_", "-"))
            .collect();
        let string_names = &string_names;
        let capitalized_names = &capitalized_names;
        let names1 = &names;
        let names2 = &names;
        let names3 = &names;
        let types1 = &types;
        let type_names = types.iter()
            .map(|ident| value_type_to_type(&ident));

        let to_variant_fn_variant = quote! {
            #(#string_names => {
                if let ::mg_settings::Value::#types1(#names1) = value {
                    Ok(#capitalized_names(#names2))
                }
                else {
                    Err(::mg_settings::error::SettingError::WrongType {
                        actual: value.to_type().to_string(),
                        expected: #type_names.to_string(),
                    })
                }
            },)*
        };

        let to_variant_fn = quote! {
            fn to_variant(name: &str, value: ::mg_settings::Value) -> Result<Self::VariantSet, ::mg_settings::error::SettingError> {
                match name {
                    #to_variant_fn_variant
                    _ => Err(::mg_settings::error::SettingError::UnknownSetting(name.to_string())),
                }
            }
        };

        quote! {
            impl ::mg_settings::settings::Settings for #name {
                type VariantGet = #original_name;
                type VariantSet = #variant_name;

                fn get(&self, name: &str) -> Option<::mg_settings::Value> {
                    match name {
                        #(#string_names => Some(::mg_settings::Value::#types1(self.#names2.clone())),)*
                        _ => None,
                    }
                }

                #to_variant_fn

                fn set_value(&mut self, value: Self::VariantSet) {
                    match value {
                        #(#capitalized_names(#names1) => {
                            self.#names2 = #names3
                        }),*
                    }
                }
            }
        }
    }
    else {
        panic!("Not a struct");
    }
}

/// Convert a Rust type to a `Value` type.
fn to_value_type(ident: &Ident) -> Ident {
    let value_type =
        match ident.to_string().as_ref() {
            "bool" => "Bool",
            "f64" => "Float",
            "i64" => "Int",
            "String" => "Str",
            ty => panic!("Unexpected type {}", ty),
        };
    Ident::new(value_type)
}

/// Convert a `Value` type to a string representation of the type.
fn value_type_to_type(ident: &Ident) -> &str {
    match ident.to_string().as_ref() {
        "Bool" => "bool",
        "Float" => "float",
        "Int" => "integer",
        "Str" => "string",
        ty => panic!("Unknown Value type {}", ty),
    }
}
<commit_msg>Made the generated enum public.<commit_after>/*
 * Copyright (c) 2016 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

use quote::Tokens;
use syn::{Body, Ident, MacroInput, Path, VariantData};
use syn::Body::Struct;
use syn::Ty;

use string::snake_to_camel;

/// Expand the required traits for the derive Settings attribute.
pub fn expand_settings_enum(mut ast: MacroInput) -> Tokens {
    let original_name = ast.ident.clone();
    let variant_name = Ident::new(format!("{}Variant", original_name));
    ast.ident = Ident::new(format!("_{}", original_name));
    let name = ast.ident.clone();
    let variant_enum = to_enums(&original_name, &ast.ident, &variant_name, &ast.body);
    let settings_impl = to_settings_impl(&original_name, &name, &variant_name, &ast.body);
    quote! {
        #[derive(Default)]
        #ast

        #variant_enum

        #settings_impl
    }
}

/// Create the variant enums for getters and setters.
fn to_enums(original_name: &Ident, new_name: &Ident, variant_name: &Ident, settings_struct: &Body) -> Tokens {
    if let &Struct(VariantData::Struct(ref strct)) = settings_struct {
        let mut field_names = vec![];
        let mut names = vec![];
        let mut types = vec![];
        for field in strct {
            if let Some(ref ident) = field.ident {
                field_names.push(ident);
                let ident = Ident::new(snake_to_camel(&ident.to_string()));
                names.push(ident);
                types.push(field.ty.clone());
            }
        }
        let string_names = field_names.iter()
            .map(|ident| ident.to_string().replace("_", "-"));
        let names1 = &names;
        let names2 = &names;
        let qualified_names = names.iter()
            .map(|ident| quote! {
                #original_name::#ident
            });
        quote! {
            #[derive(Clone)]
            pub enum #variant_name {
                #(#names1(#types)),*
            }

            pub enum #original_name {
                #(#names2),*
            }

            impl ::std::fmt::Display for #original_name {
                fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
                    match *self {
                        #(#qualified_names => write!(formatter, #string_names)),*
                    }
                }
            }

            impl #original_name {
                pub fn new() -> #new_name {
                    #new_name::default()
                }
            }
        }
    }
    else {
        panic!("Not a struct");
    }
}

/// Create the impl Settings.
fn to_settings_impl(original_name: &Ident, name: &Ident, variant_name: &Ident, settings_struct: &Body) -> Tokens {
    if let &Struct(VariantData::Struct(ref strct)) = settings_struct {
        let mut names = vec![];
        let mut capitalized_names = vec![];
        let mut types = vec![];
        for field in strct {
            if let Some(ref ident) = field.ident {
                let ident_string = ident.to_string();
                let ident = Ident::new(ident_string.clone());
                names.push(ident);
                let ident = Ident::new(snake_to_camel(&ident_string));
                capitalized_names.push(
                    quote! {
                        #variant_name::#ident
                    });

                if let Ty::Path(_, Path { ref segments, .. }) = field.ty {
                    types.push(to_value_type(&segments[0].ident));
                }
            }
        }
        let string_names: Vec<_> = names.iter()
            .map(|ident| ident.to_string().replace("_", "-"))
            .collect();
        let string_names = &string_names;
        let capitalized_names = &capitalized_names;
        let names1 = &names;
        let names2 = &names;
        let names3 = &names;
        let types1 = &types;
        let type_names = types.iter()
            .map(|ident| value_type_to_type(&ident));

        let to_variant_fn_variant = quote! {
            #(#string_names => {
                if let ::mg_settings::Value::#types1(#names1) = value {
                    Ok(#capitalized_names(#names2))
                }
                else {
                    Err(::mg_settings::error::SettingError::WrongType {
                        actual: value.to_type().to_string(),
                        expected: #type_names.to_string(),
                    })
                }
            },)*
        };

        let to_variant_fn = quote! {
            fn to_variant(name: &str, value: ::mg_settings::Value) -> Result<Self::VariantSet, ::mg_settings::error::SettingError> {
                match name {
                    #to_variant_fn_variant
                    _ => Err(::mg_settings::error::SettingError::UnknownSetting(name.to_string())),
                }
            }
        };

        quote! {
            impl ::mg_settings::settings::Settings for #name {
                type VariantGet = #original_name;
                type VariantSet = #variant_name;

                fn get(&self, name: &str) -> Option<::mg_settings::Value> {
                    match name {
                        #(#string_names => Some(::mg_settings::Value::#types1(self.#names2.clone())),)*
                        _ => None,
                    }
                }

                #to_variant_fn

                fn set_value(&mut self, value: Self::VariantSet) {
                    match value {
                        #(#capitalized_names(#names1) => {
                            self.#names2 = #names3
                        }),*
                    }
                }
            }
        }
    }
    else {
        panic!("Not a struct");
    }
}

/// Convert a Rust type to a `Value` type.
fn to_value_type(ident: &Ident) -> Ident {
    let value_type =
        match ident.to_string().as_ref() {
            "bool" => "Bool",
            "f64" => "Float",
            "i64" => "Int",
            "String" => "Str",
            ty => panic!("Unexpected type {}", ty),
        };
    Ident::new(value_type)
}

/// Convert a `Value` type to a string representation of the type.
fn value_type_to_type(ident: &Ident) -> &str {
    match ident.to_string().as_ref() {
        "Bool" => "bool",
        "Float" => "float",
        "Int" => "integer",
        "Str" => "string",
        ty => panic!("Unknown Value type {}", ty),
    }
}
<|endoftext|>


<commit_before>fn main() {
    println!("Hello, world!");
}
<commit_msg>add use and deal with 0 arg<commit_after>use std::io::BufReader;
use std::io::prelude::*;
use std::fs::File;
use std::env;

fn main() {
    let mut tab: Vec<_> = env::args().collect();

    if tab.len()==1 {
        tab[1] = "./".to_string();
    }
}
<|endoftext|>


<commit_before>use crate::error;
use crate::error::Result;
use lazy_static::lazy_static;
use regex::Regex;
use std::fs;
use tree_sitter::{Language, Parser, Point};

lazy_static! {
    static ref CAPTURE_NAME_REGEX: Regex = Regex::new("[\\w_\\-.]+").unwrap();
}

#[derive(Debug, Eq, PartialEq)]
pub struct CaptureInfo {
    pub name: String,
    pub start: Point,
    pub end: Point,
}

#[derive(Debug, PartialEq, Eq)]
pub struct Assertion {
    pub position: Point,
    pub expected_capture_name: String,
}

/// Parse the given source code, finding all of the comments that contain
/// highlighting assertions. Return a vector of (position, expected highlight name)
/// pairs.
pub fn parse_position_comments(
    parser: &mut Parser,
    language: Language,
    source: &[u8],
) -> Result<Vec<Assertion>> {
    let mut result = Vec::new();
    let mut assertion_ranges = Vec::new();

    // Parse the code.
    parser.set_included_ranges(&[]).unwrap();
    parser.set_language(language).unwrap();
    let tree = parser.parse(source, None).unwrap();

    // Walk the tree, finding comment nodes that contain assertions.
    let mut ascending = false;
    let mut cursor = tree.root_node().walk();
    loop {
        if ascending {
            let node = cursor.node();

            // Find every comment node.
            if node.kind().contains("comment") {
                if let Ok(text) = node.utf8_text(source) {
                    let mut position = node.start_position();
                    if position.row == 0 {
                        continue;
                    }

                    // Find the arrow character ("^" or '<-") in the comment. A left arrow
                    // refers to the column where the comment node starts. An up arrow refers
                    // to its own column.
                    let mut has_left_caret = false;
                    let mut has_arrow = false;
                    let mut arrow_end = 0;
                    for (i, c) in text.char_indices() {
                        arrow_end = i + 1;
                        if c == '-' && has_left_caret {
                            has_arrow = true;
                            break;
                        }
                        if c == '^' {
                            has_arrow = true;
                            position.column += i;
                            break;
                        }
                        has_left_caret = c == '<';
                    }

                    // If the comment node contains an arrow and a highlight name, record the
                    // highlight name and the position.
                    if let (true, Some(mat)) =
                        (has_arrow, CAPTURE_NAME_REGEX.find(&text[arrow_end..]))
                    {
                        assertion_ranges.push((node.start_position(), node.end_position()));
                        result.push(Assertion {
                            position: position,
                            expected_capture_name: mat.as_str().to_string(),
                        });
                    }
                }
            }

            // Continue walking the tree.
            if cursor.goto_next_sibling() {
                ascending = false;
            } else if !cursor.goto_parent() {
                break;
            }
        } else if !cursor.goto_first_child() {
            ascending = true;
        }
    }

    // Adjust the row number in each assertion's position to refer to the line of
    // code *above* the assertion. There can be multiple lines of assertion comments,
    // so the positions may have to be decremented by more than one row.
    let mut i = 0;
    for assertion in result.iter_mut() {
        loop {
            let on_assertion_line = assertion_ranges[i..]
                .iter()
                .any(|(start, _)| start.row == assertion.position.row);
            if on_assertion_line {
                assertion.position.row -= 1;
            } else {
                while i < assertion_ranges.len()
                    && assertion_ranges[i].0.row < assertion.position.row
                {
                    i += 1;
                }
                break;
            }
        }
    }

    // The assertions can end up out of order due to the line adjustments.
    result.sort_unstable_by_key(|a| a.position);

    Ok(result)
}

pub fn assert_expected_captures(
    infos: Vec<CaptureInfo>,
    path: String,
    parser: &mut Parser,
    language: Language,
) -> Result<()> {
    let contents = fs::read_to_string(path)?;
    let pairs = parse_position_comments(parser, language, contents.as_bytes())?;
    for info in &infos {
        let found = pairs.iter().find(|p| {
            p.position.row == info.start.row && p.position >= info.start && p.position < info.end
        });

        if let Some(found) = found {
            if found.expected_capture_name != info.name && info.name != "name" {
                Err(error::Error::new(format!(
                    "Assertion failed: at {}, found {}, expected {}",
                    info.start, found.expected_capture_name, info.name
                )))?
            }
        }
    }
    Ok(())
}
<commit_msg>inline this lambda<commit_after>use crate::error;
use crate::error::Result;
use lazy_static::lazy_static;
use regex::Regex;
use std::fs;
use tree_sitter::{Language, Parser, Point};

lazy_static! {
    static ref CAPTURE_NAME_REGEX: Regex = Regex::new("[\\w_\\-.]+").unwrap();
}

#[derive(Debug, Eq, PartialEq)]
pub struct CaptureInfo {
    pub name: String,
    pub start: Point,
    pub end: Point,
}

#[derive(Debug, PartialEq, Eq)]
pub struct Assertion {
    pub position: Point,
    pub expected_capture_name: String,
}

/// Parse the given source code, finding all of the comments that contain
/// highlighting assertions. Return a vector of (position, expected highlight name)
/// pairs.
pub fn parse_position_comments(
    parser: &mut Parser,
    language: Language,
    source: &[u8],
) -> Result<Vec<Assertion>> {
    let mut result = Vec::new();
    let mut assertion_ranges = Vec::new();

    // Parse the code.
    parser.set_included_ranges(&[]).unwrap();
    parser.set_language(language).unwrap();
    let tree = parser.parse(source, None).unwrap();

    // Walk the tree, finding comment nodes that contain assertions.
    let mut ascending = false;
    let mut cursor = tree.root_node().walk();
    loop {
        if ascending {
            let node = cursor.node();

            // Find every comment node.
            if node.kind().contains("comment") {
                if let Ok(text) = node.utf8_text(source) {
                    let mut position = node.start_position();
                    if position.row == 0 {
                        continue;
                    }

                    // Find the arrow character ("^" or '<-") in the comment. A left arrow
                    // refers to the column where the comment node starts. An up arrow refers
                    // to its own column.
                    let mut has_left_caret = false;
                    let mut has_arrow = false;
                    let mut arrow_end = 0;
                    for (i, c) in text.char_indices() {
                        arrow_end = i + 1;
                        if c == '-' && has_left_caret {
                            has_arrow = true;
                            break;
                        }
                        if c == '^' {
                            has_arrow = true;
                            position.column += i;
                            break;
                        }
                        has_left_caret = c == '<';
                    }

                    // If the comment node contains an arrow and a highlight name, record the
                    // highlight name and the position.
                    if let (true, Some(mat)) =
                        (has_arrow, CAPTURE_NAME_REGEX.find(&text[arrow_end..]))
                    {
                        assertion_ranges.push((node.start_position(), node.end_position()));
                        result.push(Assertion {
                            position: position,
                            expected_capture_name: mat.as_str().to_string(),
                        });
                    }
                }
            }

            // Continue walking the tree.
            if cursor.goto_next_sibling() {
                ascending = false;
            } else if !cursor.goto_parent() {
                break;
            }
        } else if !cursor.goto_first_child() {
            ascending = true;
        }
    }

    // Adjust the row number in each assertion's position to refer to the line of
    // code *above* the assertion. There can be multiple lines of assertion comments,
    // so the positions may have to be decremented by more than one row.
    let mut i = 0;
    for assertion in result.iter_mut() {
        loop {
            let on_assertion_line = assertion_ranges[i..]
                .iter()
                .any(|(start, _)| start.row == assertion.position.row);
            if on_assertion_line {
                assertion.position.row -= 1;
            } else {
                while i < assertion_ranges.len()
                    && assertion_ranges[i].0.row < assertion.position.row
                {
                    i += 1;
                }
                break;
            }
        }
    }

    // The assertions can end up out of order due to the line adjustments.
    result.sort_unstable_by_key(|a| a.position);

    Ok(result)
}

pub fn assert_expected_captures(
    infos: Vec<CaptureInfo>,
    path: String,
    parser: &mut Parser,
    language: Language,
) -> Result<()> {
    let contents = fs::read_to_string(path)?;
    let pairs = parse_position_comments(parser, language, contents.as_bytes())?;
    for info in &infos {
        if let Some(found) = pairs.iter().find(|p| {
            p.position.row == info.start.row && p.position >= info.start && p.position < info.end
        }) {
            if found.expected_capture_name != info.name && info.name != "name" {
                Err(error::Error::new(format!(
                    "Assertion failed: at {}, found {}, expected {}",
                    info.start, found.expected_capture_name, info.name
                )))?
            }
        }
    }
    Ok(())
}
<|endoftext|>