<commit_before>/// External linking is a complex implementation to be able to serve a clean and easy-to-use
/// interface.
///
/// Internally, there are no such things as "external links" (plural). Each Entry in the store can
/// only have _one_ external link.
///
/// This library does the following therefor: It allows you to have several external links with one
/// entry, which are internally one file in the store for each link, linked with "internal
/// linking".
///
/// This helps us greatly with deduplication of URLs.
///

use std::convert::Into;
use std::ops::Deref;
use std::ops::DerefMut;
use std::collections::BTreeMap;

use libimagstore::store::Entry;
use libimagstore::store::EntryHeader;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Store;
use libimagstore::storeid::StoreId;
use libimagstore::storeid::IntoStoreId;

use error::LinkError as LE;
use error::LinkErrorKind as LEK;
use result::Result;
use internal::InternalLinker;
use module_path::ModuleEntryPath;

use toml::Value;
use toml::Table;
use url::Url;
use sodiumoxide::crypto::hash;
use sodiumoxide::crypto::hash::sha512::Digest;

/// "Link" Type, just an abstraction over FileLockEntry to have some convenience internally.
struct Link<'a> {
    link: FileLockEntry<'a>
}

impl<'a> Link<'a> {

    pub fn new(fle: FileLockEntry<'a>) -> Link<'a> {
        Link { link: fle }
    }

    /// For interal use only. Load an Link from a store id, if this is actually a Link
    fn retrieve(store: &'a Store, id: StoreId) -> Result<Option<Link<'a>>> {
        store.retrieve(id)
            .map(|fle| {
                if let Some(_) = Link::get_link_uri_from_filelockentry(&fle) {
                    Some(Link {
                        link: fle
                    })
                } else {
                    None
                }
            })
            .map_err(|e| LE::new(LEK::StoreReadError, Some(Box::new(e))))
    }

    /// Get a link Url object from a FileLockEntry, ignore errors.
    fn get_link_uri_from_filelockentry(file: &FileLockEntry<'a>) -> Option<Url> {
        file.deref()
            .get_header()
            .read("imag.content.uri")
            .ok()
            .and_then(|opt| {
                match opt {
                    Some(Value::String(s)) => Url::parse(&s[..]).ok(),
                    _ => None
                }
            })
    }

    pub fn get_url(&self) -> Result<Option<Url>> {
        let opt = self.link
            .deref()
            .get_header()
            .read("imag.content.uri");

        match opt {
            Ok(Some(Value::String(s))) => {
                Url::parse(&s[..])
                     .map(|s| Some(s))
                     .map_err(|e| LE::new(LEK::EntryHeaderReadError, Some(Box::new(e))))
            },
            Ok(None) => Ok(None),
            _ => Err(LE::new(LEK::EntryHeaderReadError, None))
        }
    }

}

pub trait ExternalLinker : InternalLinker {

    /// Get the external links from the implementor object
    fn get_external_links(&self, store: &Store) -> Result<Vec<Url>>;

    /// Set the external links for the implementor object
    fn set_external_links(&mut self, store: &Store, links: Vec<Url>) -> Result<()>;

    /// Add an external link to the implementor object
    fn add_external_link(&mut self, store: &Store, link: Url) -> Result<()>;

    /// Remove an external link from the implementor object
    fn remove_external_link(&mut self, store: &Store, link: Url) -> Result<()>;

}

/// Check whether the StoreId starts with `/link/external/`
fn is_link_store_id(id: &StoreId) -> bool {
    id.starts_with("/link/external/")
}

fn get_external_link_from_file(entry: &FileLockEntry) -> Result<Url> {
    Link::get_link_uri_from_filelockentry(entry) // TODO: Do not hide error by using this function
        .ok_or(LE::new(LEK::StoreReadError, None))
}

/// Implement ExternalLinker for Entry, hiding the fact that there is no such thing as an external
/// link in an entry, but internal links to other entries which serve as external links, as one
/// entry in the store can only have one external link.
impl ExternalLinker for Entry {

    /// Get the external links from the implementor object
    fn get_external_links(&self, store: &Store) -> Result<Vec<Url>> {
        // Iterate through all internal links and filter for FileLockEntries which live in
        // /link/external/<SHA> -> load these files and get the external link from their headers,
        // put them into the return vector.
        self.get_internal_links()
            .map(|vect| {
                vect.into_iter()
                    .filter(is_link_store_id)
                    .map(|id| {
                        match store.retrieve(id) {
                            Ok(f) => get_external_link_from_file(&f),
                            Err(e) => Err(LE::new(LEK::StoreReadError, Some(Box::new(e)))),
                        }
                    })
                    .filter_map(|x| x.ok()) // TODO: Do not ignore error here
                    .collect()
            })
            .map_err(|e| LE::new(LEK::StoreReadError, Some(Box::new(e))))
    }

    /// Set the external links for the implementor object
    fn set_external_links(&mut self, store: &Store, links: Vec<Url>) -> Result<()> {
        // Take all the links, generate a SHA sum out of each one, filter out the already existing
        // store entries and store the other URIs in the header of one FileLockEntry each, in
        // the path /link/external/<SHA of the URL>

        /// Convert a hash::sha512::Digest from sodiumoxide into a StoreId
        fn hash_to_storeid(d: Digest) -> StoreId {
            let hash = &d[..];
            let v = Vec::from(hash);
            let s = String::from_utf8(v).unwrap(); // TODO: Uncaught unwrap()
            ModuleEntryPath::new(format!("external/{}", s)).into_storeid()
        }

        for link in links { // for all links
            let hash     = hash::hash(link.serialize().as_bytes());
            let file_id  = hash_to_storeid(hash);

            let mut file = {
                if let Ok(mut file) = store.retrieve(file_id.clone()) { // retrieve the file from the store
                    file
                } else { // or
                    let res = store.create(file_id) // create it
                        .and_then(|mut file| {
                            {
                                let mut hdr = file.deref_mut().get_header_mut();

                                // Write the URI into the header
                                match hdr.set("imag.content", Value::Table(BTreeMap::new())) {
                                    Ok(_) => {
                                        let v = Value::String(link.serialize());
                                        hdr.set("imag.content.uri", v);
                                        Ok(())
                                    },
                                    Err(e) => Err(e),
                                }
                            }.map(|_| file)
                        })
                        .map_err(|e| LE::new(LEK::StoreWriteError, Some(Box::new(e))));

                    // And if that fails we can error
                    if let Err(e) = res {
                        return Err(e);
                    }
                    res.unwrap()
                }
            };

            // then add an internal link to the new file or return an error if this fails
            if let Err(e) = self.add_internal_link(file.deref_mut()) {
                return Err(LE::new(LEK::StoreWriteError, Some(Box::new(e))));
            }
        }
        Ok(())
    }

    /// Add an external link to the implementor object
    fn add_external_link(&mut self, store: &Store, link: Url) -> Result<()> {
        // get external links, add this one, save them
        self.get_external_links(store)
            .and_then(|mut links| {
                links.push(link);
                self.set_external_links(store, links)
            })
    }

    /// Remove an external link from the implementor object
    fn remove_external_link(&mut self, store: &Store, link: Url) -> Result<()> {
        // get external links, remove this one, save them
        self.get_external_links(store)
            .and_then(|mut links| {
                let links = links.into_iter()
                    .filter(|l| l.serialize() != link.serialize())
                    .collect();
                self.set_external_links(store, links)
            })
    }

}

<commit_msg>Add debug!() calls for external linking source<commit_after>/// External linking is a complex implementation to be able to serve a clean and easy-to-use
/// interface.
///
/// Internally, there are no such things as "external links" (plural). Each Entry in the store can
/// only have _one_ external link.
///
/// This library does the following therefor: It allows you to have several external links with one
/// entry, which are internally one file in the store for each link, linked with "internal
/// linking".
///
/// This helps us greatly with deduplication of URLs.
///

use std::convert::Into;
use std::ops::Deref;
use std::ops::DerefMut;
use std::collections::BTreeMap;

use libimagstore::store::Entry;
use libimagstore::store::EntryHeader;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Store;
use libimagstore::storeid::StoreId;
use libimagstore::storeid::IntoStoreId;

use error::LinkError as LE;
use error::LinkErrorKind as LEK;
use result::Result;
use internal::InternalLinker;
use module_path::ModuleEntryPath;

use toml::Value;
use toml::Table;
use url::Url;
use sodiumoxide::crypto::hash;
use sodiumoxide::crypto::hash::sha512::Digest;

/// "Link" Type, just an abstraction over FileLockEntry to have some convenience internally.
struct Link<'a> {
    link: FileLockEntry<'a>
}

impl<'a> Link<'a> {

    pub fn new(fle: FileLockEntry<'a>) -> Link<'a> {
        Link { link: fle }
    }

    /// For interal use only. Load an Link from a store id, if this is actually a Link
    fn retrieve(store: &'a Store, id: StoreId) -> Result<Option<Link<'a>>> {
        store.retrieve(id)
            .map(|fle| {
                if let Some(_) = Link::get_link_uri_from_filelockentry(&fle) {
                    Some(Link {
                        link: fle
                    })
                } else {
                    None
                }
            })
            .map_err(|e| LE::new(LEK::StoreReadError, Some(Box::new(e))))
    }

    /// Get a link Url object from a FileLockEntry, ignore errors.
    fn get_link_uri_from_filelockentry(file: &FileLockEntry<'a>) -> Option<Url> {
        file.deref()
            .get_header()
            .read("imag.content.uri")
            .ok()
            .and_then(|opt| {
                match opt {
                    Some(Value::String(s)) => Url::parse(&s[..]).ok(),
                    _ => None
                }
            })
    }

    pub fn get_url(&self) -> Result<Option<Url>> {
        let opt = self.link
            .deref()
            .get_header()
            .read("imag.content.uri");

        match opt {
            Ok(Some(Value::String(s))) => {
                Url::parse(&s[..])
                     .map(|s| Some(s))
                     .map_err(|e| LE::new(LEK::EntryHeaderReadError, Some(Box::new(e))))
            },
            Ok(None) => Ok(None),
            _ => Err(LE::new(LEK::EntryHeaderReadError, None))
        }
    }

}

pub trait ExternalLinker : InternalLinker {

    /// Get the external links from the implementor object
    fn get_external_links(&self, store: &Store) -> Result<Vec<Url>>;

    /// Set the external links for the implementor object
    fn set_external_links(&mut self, store: &Store, links: Vec<Url>) -> Result<()>;

    /// Add an external link to the implementor object
    fn add_external_link(&mut self, store: &Store, link: Url) -> Result<()>;

    /// Remove an external link from the implementor object
    fn remove_external_link(&mut self, store: &Store, link: Url) -> Result<()>;

}

/// Check whether the StoreId starts with `/link/external/`
fn is_link_store_id(id: &StoreId) -> bool {
    debug!("Checking whether this is a /link/external/*: '{:?}'", id);
    id.starts_with("/link/external/")
}

fn get_external_link_from_file(entry: &FileLockEntry) -> Result<Url> {
    Link::get_link_uri_from_filelockentry(entry) // TODO: Do not hide error by using this function
        .ok_or(LE::new(LEK::StoreReadError, None))
}

/// Implement ExternalLinker for Entry, hiding the fact that there is no such thing as an external
/// link in an entry, but internal links to other entries which serve as external links, as one
/// entry in the store can only have one external link.
impl ExternalLinker for Entry {

    /// Get the external links from the implementor object
    fn get_external_links(&self, store: &Store) -> Result<Vec<Url>> {
        // Iterate through all internal links and filter for FileLockEntries which live in
        // /link/external/<SHA> -> load these files and get the external link from their headers,
        // put them into the return vector.
        self.get_internal_links()
            .map(|vect| {
                debug!("Getting external links");
                vect.into_iter()
                    .filter(is_link_store_id)
                    .map(|id| {
                        debug!("Retrieving entry for id: '{:?}'", id);
                        match store.retrieve(id.clone()) {
                            Ok(f) => get_external_link_from_file(&f),
                            Err(e) => {
                                debug!("Retrieving entry for id: '{:?}' failed", id);
                                Err(LE::new(LEK::StoreReadError, Some(Box::new(e))))
                            }
                        }
                    })
                    .filter_map(|x| x.ok()) // TODO: Do not ignore error here
                    .collect()
            })
            .map_err(|e| LE::new(LEK::StoreReadError, Some(Box::new(e))))
    }

    /// Set the external links for the implementor object
    fn set_external_links(&mut self, store: &Store, links: Vec<Url>) -> Result<()> {
        // Take all the links, generate a SHA sum out of each one, filter out the already existing
        // store entries and store the other URIs in the header of one FileLockEntry each, in
        // the path /link/external/<SHA of the URL>

        /// Convert a hash::sha512::Digest from sodiumoxide into a StoreId
        fn hash_to_storeid(d: Digest) -> StoreId {
            let hash = &d[..];
            let v = Vec::from(hash);
            let s = String::from_utf8(v).unwrap(); // TODO: Uncaught unwrap()
            debug!("Generating store id for digest: '{:?}' == {}", d, s);

            let id = ModuleEntryPath::new(format!("external/{}", s)).into_storeid();
            debug!("Generted store id: '{:?}'", id);

            id
        }

        debug!("Iterating {} links = {:?}", links.len(), links);
        for link in links { // for all links
            let hash     = hash::hash(link.serialize().as_bytes());
            let file_id  = hash_to_storeid(hash);

            debug!("Link    = '{:?}'", link);
            debug!("Hash    = '{:?}'", hash);
            debug!("StoreId = '{:?}'", file_id);

            let mut file = {
                if let Ok(mut file) = store.retrieve(file_id.clone()) { // retrieve the file from the store
                    debug!("Woha, there is already a file for this link: '{:?}'", link);
                    file
                } else { // or
                    debug!("Generating file for link = '{:?}' on id = {:?}", link, file_id);
                    let res = store.create(file_id) // create it
                        .and_then(|mut file| {
                            {
                                debug!("Generating header content!");
                                let mut hdr = file.deref_mut().get_header_mut();

                                // Write the URI into the header
                                match hdr.set("imag.content", Value::Table(BTreeMap::new())) {
                                    Ok(_) => {
                                        let v = Value::String(link.serialize());
                                        debug!("setting URL = '{:?}", v);
                                        hdr.set("imag.content.uri", v);
                                        Ok(())
                                    },
                                    Err(e) => {
                                        debug!("Failed to generate a table in header at 'imag.content'");
                                        Err(e)
                                    },
                                }
                            }.map(|_| file)
                        })
                        .map_err(|e| LE::new(LEK::StoreWriteError, Some(Box::new(e))));

                    // And if that fails we can error
                    if let Err(e) = res {
                        debug!("Failed to create or retrieve an file for this link '{:?}'", link);
                        return Err(e);
                    }
                    debug!("Success creating or retrieving an file for this link '{:?}'", link);
                    res.unwrap()
                }
            };

            // then add an internal link to the new file or return an error if this fails
            if let Err(e) = self.add_internal_link(file.deref_mut()) {
                debug!("Error adding internal link");
                return Err(LE::new(LEK::StoreWriteError, Some(Box::new(e))));
            }
        }
        debug!("Ready iterating");
        Ok(())
    }

    /// Add an external link to the implementor object
    fn add_external_link(&mut self, store: &Store, link: Url) -> Result<()> {
        // get external links, add this one, save them
        debug!("Getting links");
        self.get_external_links(store)
            .and_then(|mut links| {
                debug!("Adding link = '{:?}' to links = {:?}", link, links);
                links.push(link);
                debug!("Setting {} links = {:?}", links.len(), links);
                self.set_external_links(store, links)
            })
    }

    /// Remove an external link from the implementor object
    fn remove_external_link(&mut self, store: &Store, link: Url) -> Result<()> {
        // get external links, remove this one, save them
        self.get_external_links(store)
            .and_then(|mut links| {
                debug!("Removing link = '{:?}' from links = {:?}", link, links);
                let links = links.into_iter()
                    .filter(|l| l.serialize() != link.serialize())
                    .collect();
                self.set_external_links(store, links)
            })
    }

}

<|endoftext|>


<commit_before>use std::path::PathBuf;

use std::fmt::{Display, Debug, Formatter};
use std::fmt::Error as FmtError;
use std::result::Result as RResult;
use std::path::Components;

use libimagerror::into::IntoError;

use error::StoreErrorKind as SEK;
use store::Result;
use store::Store;

/// The Index into the Store
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
pub struct StoreId {
    base: Option<PathBuf>,
    id:   PathBuf,
}

impl StoreId {

    pub fn new(base: Option<PathBuf>, id: PathBuf) -> Result<StoreId> {
        StoreId::new_baseless(id).map(|mut sid| { sid.base = base; sid })
    }

    pub fn new_baseless(id: PathBuf) -> Result<StoreId> {
        if id.is_absolute() {
            Err(SEK::StoreIdLocalPartAbsoluteError.into_error())
        } else {
            Ok(StoreId {
                base: None,
                id: id
            })
        }
    }

    pub fn without_base(mut self) -> StoreId {
        self.base = None;
        self
    }

    pub fn with_base(mut self, base: PathBuf) -> Self {
        self.base = Some(base);
        self
    }

    pub fn storified(self, store: &Store) -> StoreId {
        StoreId {
            base: Some(store.path().clone()),
            id: self.id
        }
    }

    pub fn exists(&self) -> bool {
        let pb : PathBuf = self.clone().into();
        pb.exists()
    }

    pub fn is_file(&self) -> bool {
        true
    }

    pub fn is_dir(&self) -> bool {
        false
    }

    pub fn to_str(&self) -> Result<String> {
        if self.base.is_some() {
            let mut base = self.base.as_ref().cloned().unwrap();
            base.push(self.id.clone());
            base
        } else {
            self.id.clone()
        }
        .to_str()
        .map(String::from)
        .ok_or(SEK::StoreIdHandlingError.into_error())
    }

    /// Returns the components of the `id` part of the StoreId object.
    ///
    /// Can be used to check whether a StoreId points to an entry in a specific collection of
    /// StoreIds.
    pub fn components(&self) -> Components {
        self.id.components()
    }

    /// Get the _local_ part of a StoreId object, as in "the part from the store root to the entry".
    pub fn local(&self) -> &PathBuf {
        &self.id
    }

}

impl Into<PathBuf> for StoreId {

    fn into(self) -> PathBuf {
        let mut base = self.base.unwrap_or(PathBuf::from("/"));
        base.push(self.id);
        base
    }

}

impl Display for StoreId {

    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        match self.id.to_str() {
            Some(s) => write!(fmt, "{}", s),
            None    => write!(fmt, "{}", self.id.to_string_lossy()),
        }
    }

}

/// This Trait allows you to convert various representations to a single one
/// suitable for usage in the Store
pub trait IntoStoreId {
    fn into_storeid(self) -> Result<StoreId>;
}

impl IntoStoreId for StoreId {
    fn into_storeid(self) -> Result<StoreId> {
        Ok(self)
    }
}

impl IntoStoreId for PathBuf {
    fn into_storeid(self) -> Result<StoreId> {
        StoreId::new_baseless(self)
    }
}

#[macro_export]
macro_rules! module_entry_path_mod {
    ($name:expr) => (
        #[deny(missing_docs,
                missing_copy_implementations,
                trivial_casts, trivial_numeric_casts,
                unsafe_code,
                unstable_features,
                unused_import_braces, unused_qualifications,
                unused_imports)]
        /// A helper module to create valid module entry paths
        pub mod module_path {
            use std::convert::AsRef;
            use std::path::Path;
            use std::path::PathBuf;

            use $crate::storeid::StoreId;
            use $crate::store::Result;

            /// A Struct giving you the ability to choose store entries assigned
            /// to it.
            ///
            /// It is created through a call to `new`.
            pub struct ModuleEntryPath(PathBuf);

            impl ModuleEntryPath {
                /// Path has to be a valid UTF-8 string or this will panic!
                pub fn new<P: AsRef<Path>>(pa: P) -> ModuleEntryPath {
                    let mut path = PathBuf::new();
                    path.push(format!("{}", $name));
                    path.push(pa.as_ref().clone());
                    let name = pa.as_ref().file_name().unwrap()
                        .to_str().unwrap();
                    path.set_file_name(name);
                    ModuleEntryPath(path)
                }
            }

            impl $crate::storeid::IntoStoreId for ModuleEntryPath {
                fn into_storeid(self) -> Result<$crate::storeid::StoreId> {
                    StoreId::new(None, self.0)
                }
            }
        }
    )
}

pub struct StoreIdIterator {
    iter: Box<Iterator<Item = StoreId>>,
}

impl Debug for StoreIdIterator {

    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "StoreIdIterator")
    }

}

impl StoreIdIterator {

    pub fn new(iter: Box<Iterator<Item = StoreId>>) -> StoreIdIterator {
        StoreIdIterator {
            iter: iter,
        }
    }

}

impl Iterator for StoreIdIterator {
    type Item = StoreId;

    fn next(&mut self) -> Option<StoreId> {
        self.iter.next()
    }

}

#[cfg(test)]
mod test {

    use storeid::IntoStoreId;

    module_entry_path_mod!("test");

    #[test]
    fn correct_path() {
        let p = module_path::ModuleEntryPath::new("test");

        assert_eq!(p.into_storeid().unwrap().to_str().unwrap(), "test/test");
    }

}
<commit_msg>Remove StoreId::storified()<commit_after>use std::path::PathBuf;

use std::fmt::{Display, Debug, Formatter};
use std::fmt::Error as FmtError;
use std::result::Result as RResult;
use std::path::Components;

use libimagerror::into::IntoError;

use error::StoreErrorKind as SEK;
use store::Result;

/// The Index into the Store
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
pub struct StoreId {
    base: Option<PathBuf>,
    id:   PathBuf,
}

impl StoreId {

    pub fn new(base: Option<PathBuf>, id: PathBuf) -> Result<StoreId> {
        StoreId::new_baseless(id).map(|mut sid| { sid.base = base; sid })
    }

    pub fn new_baseless(id: PathBuf) -> Result<StoreId> {
        if id.is_absolute() {
            Err(SEK::StoreIdLocalPartAbsoluteError.into_error())
        } else {
            Ok(StoreId {
                base: None,
                id: id
            })
        }
    }

    pub fn without_base(mut self) -> StoreId {
        self.base = None;
        self
    }

    pub fn with_base(mut self, base: PathBuf) -> Self {
        self.base = Some(base);
        self
    }

    pub fn exists(&self) -> bool {
        let pb : PathBuf = self.clone().into();
        pb.exists()
    }

    pub fn is_file(&self) -> bool {
        true
    }

    pub fn is_dir(&self) -> bool {
        false
    }

    pub fn to_str(&self) -> Result<String> {
        if self.base.is_some() {
            let mut base = self.base.as_ref().cloned().unwrap();
            base.push(self.id.clone());
            base
        } else {
            self.id.clone()
        }
        .to_str()
        .map(String::from)
        .ok_or(SEK::StoreIdHandlingError.into_error())
    }

    /// Returns the components of the `id` part of the StoreId object.
    ///
    /// Can be used to check whether a StoreId points to an entry in a specific collection of
    /// StoreIds.
    pub fn components(&self) -> Components {
        self.id.components()
    }

    /// Get the _local_ part of a StoreId object, as in "the part from the store root to the entry".
    pub fn local(&self) -> &PathBuf {
        &self.id
    }

}

impl Into<PathBuf> for StoreId {

    fn into(self) -> PathBuf {
        let mut base = self.base.unwrap_or(PathBuf::from("/"));
        base.push(self.id);
        base
    }

}

impl Display for StoreId {

    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        match self.id.to_str() {
            Some(s) => write!(fmt, "{}", s),
            None    => write!(fmt, "{}", self.id.to_string_lossy()),
        }
    }

}

/// This Trait allows you to convert various representations to a single one
/// suitable for usage in the Store
pub trait IntoStoreId {
    fn into_storeid(self) -> Result<StoreId>;
}

impl IntoStoreId for StoreId {
    fn into_storeid(self) -> Result<StoreId> {
        Ok(self)
    }
}

impl IntoStoreId for PathBuf {
    fn into_storeid(self) -> Result<StoreId> {
        StoreId::new_baseless(self)
    }
}

#[macro_export]
macro_rules! module_entry_path_mod {
    ($name:expr) => (
        #[deny(missing_docs,
                missing_copy_implementations,
                trivial_casts, trivial_numeric_casts,
                unsafe_code,
                unstable_features,
                unused_import_braces, unused_qualifications,
                unused_imports)]
        /// A helper module to create valid module entry paths
        pub mod module_path {
            use std::convert::AsRef;
            use std::path::Path;
            use std::path::PathBuf;

            use $crate::storeid::StoreId;
            use $crate::store::Result;

            /// A Struct giving you the ability to choose store entries assigned
            /// to it.
            ///
            /// It is created through a call to `new`.
            pub struct ModuleEntryPath(PathBuf);

            impl ModuleEntryPath {
                /// Path has to be a valid UTF-8 string or this will panic!
                pub fn new<P: AsRef<Path>>(pa: P) -> ModuleEntryPath {
                    let mut path = PathBuf::new();
                    path.push(format!("{}", $name));
                    path.push(pa.as_ref().clone());
                    let name = pa.as_ref().file_name().unwrap()
                        .to_str().unwrap();
                    path.set_file_name(name);
                    ModuleEntryPath(path)
                }
            }

            impl $crate::storeid::IntoStoreId for ModuleEntryPath {
                fn into_storeid(self) -> Result<$crate::storeid::StoreId> {
                    StoreId::new(None, self.0)
                }
            }
        }
    )
}

pub struct StoreIdIterator {
    iter: Box<Iterator<Item = StoreId>>,
}

impl Debug for StoreIdIterator {

    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "StoreIdIterator")
    }

}

impl StoreIdIterator {

    pub fn new(iter: Box<Iterator<Item = StoreId>>) -> StoreIdIterator {
        StoreIdIterator {
            iter: iter,
        }
    }

}

impl Iterator for StoreIdIterator {
    type Item = StoreId;

    fn next(&mut self) -> Option<StoreId> {
        self.iter.next()
    }

}

#[cfg(test)]
mod test {

    use storeid::IntoStoreId;

    module_entry_path_mod!("test");

    #[test]
    fn correct_path() {
        let p = module_path::ModuleEntryPath::new("test");

        assert_eq!(p.into_storeid().unwrap().to_str().unwrap(), "test/test");
    }

}
<|endoftext|>


<commit_before><commit_msg>hypercall: Add definition of hypercalls ids<commit_after>pub enum HyperCalls {
    SetTrapTable = 0,
    MmuUpdate = 1,
    SetGdt = 2,
    StackSwitch = 3,
    SetCallbacks = 4,
    FpuTaskSwitch = 5,
    SchedOpCompat = 6,
    PlatformOp = 7,
    SetDebugReg = 8,
    GetDebugReg = 9,
    UpdateDescriptor = 10,
    MemoryOp = 12,
    MultiCall = 13,
    UpdateVaMapping = 14,
    SetTimerOp = 15,
    EventChannelOpCompat = 16,
    XenVersion = 17,
    ConsoleIo = 18,
    PhysdevOpCompat = 19,
    GrantTableOp = 20,
    VmAssist = 21,
    UpdateVaMappingOtherdomain = 22,
    Iret = 23,
    VcpuOp = 24,
    SetSegmentBase = 25,
    MmuextOp = 26,
    XsmOp = 27,
    NmiOp = 28,
    SchedOp = 29,
    CallbackOp = 30,
    XenoprofOp = 31,
    EventChannelOp = 32,
    PhysdevOp = 33,
    HvmOp = 34,
    Sysctl = 35,
    Domctl = 36,
    KexecOp = 37,
    TmemOp = 38,
    XcReservedOp = 39,
    XenpmuOp = 40,
}
<|endoftext|>


<commit_before>use std::any::TypeId;

pub type LoadFunc = fn() -> Result<Box<Plugin>, ()>;
pub type UnloadFunc = fn() -> bool;
pub type HookFunc = Box<FnMut(&mut Plugin, &[u8], usize, &[Vec<u8>]) -> Result<Option<Vec<Vec<u8>>>, HookError>>;

pub struct HookFuncWrapper(pub HookFunc);
pub const MAGIC: &'static str = "COOKIES";

#[derive(Debug)]
pub struct HookError {
    pub message: String,
}

#[derive(Debug)]
pub struct Bot {
    pub nick: String,
    pub ident: String,
    pub hostname: String,
    pub gecos: String,
}

impl ::std::fmt::Debug for HookFuncWrapper {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f, "HookFunc")
    }
}

#[derive(Debug)]
pub struct IrcEvent {
    pub plugin_ptr: *const Plugin,
    pub name: String,
    pub f: HookFuncWrapper,
}

pub trait Plugin: 'static {
    fn name(&mut self) -> String;
    fn description(&mut self) -> String;
    fn register_hooks(&mut self) -> Option<Vec<IrcEvent>>;
    unsafe fn get_type_id(&self) -> TypeId { TypeId::of::<Self>() }
    // fn bot_privmsg(&mut self, target: &[u8], message: &[u8]);
}

impl Plugin {
    pub fn downcast_mut<T: Plugin>(&mut self) -> Option<&mut T> {
        if self.is::<T>() {
            unsafe {
                Some(&mut *(self as *mut Plugin as *mut T))
            }
        } else {
            None
        }
    }

    #[inline]
    pub fn is<T: Plugin>(&self) -> bool {
        let t = TypeId::of::<T>();
        let boxed = unsafe { self.get_type_id() };
        t == boxed
    }
}
<commit_msg>plugin: Add missing trait method<commit_after>use std::any::TypeId;

pub type LoadFunc = fn() -> Result<Box<Plugin>, ()>;
pub type UnloadFunc = fn() -> bool;
pub type HookFunc = Box<FnMut(&mut Plugin, &[u8], usize, &[Vec<u8>]) -> Result<Option<Vec<Vec<u8>>>, HookError>>;

pub struct HookFuncWrapper(pub HookFunc);
pub const MAGIC: &'static str = "COOKIES";

#[derive(Debug)]
pub struct HookError {
    pub message: String,
}

#[derive(Debug)]
pub struct Bot {
    pub nick: String,
    pub ident: String,
    pub hostname: String,
    pub gecos: String,
}

impl ::std::fmt::Debug for HookFuncWrapper {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f, "HookFunc")
    }
}

#[derive(Debug)]
pub struct IrcEvent {
    pub plugin_ptr: *const Plugin,
    pub name: String,
    pub f: HookFuncWrapper,
}

pub trait Plugin: 'static {
    fn name(&mut self) -> String;
    fn description(&mut self) -> String;
    fn register_hooks(&mut self) -> Option<Vec<IrcEvent>>;
    unsafe fn get_type_id(&self) -> TypeId { TypeId::of::<Self>() }
    fn register_bots(&mut self) -> Option<Vec<Bot>>;
    // fn bot_privmsg(&mut self, target: &[u8], message: &[u8]);
}

impl Plugin {
    pub fn downcast_mut<T: Plugin>(&mut self) -> Option<&mut T> {
        if self.is::<T>() {
            unsafe {
                Some(&mut *(self as *mut Plugin as *mut T))
            }
        } else {
            None
        }
    }

    #[inline]
    pub fn is<T: Plugin>(&self) -> bool {
        let t = TypeId::of::<T>();
        let boxed = unsafe { self.get_type_id() };
        t == boxed
    }
}
<|endoftext|>


<commit_before>use rustc_serialize::base64::FromBase64;
use std::io::Read;
use std::str::FromStr;
use xml::reader::ParserConfig;
use xml::reader::EventReader as XmlEventReader;
use xml::reader::events::XmlEvent;

#[derive(PartialEq, Debug, Clone)]
pub enum PlistEvent {
	StartArray,
	EndArray,

	StartDictionary,
	EndDictionary,
	DictionaryKey(String),

	BooleanValue(bool),
	DataValue(Vec<u8>),
	DateValue(String),
	FloatValue(f64),
	IntegerValue(i64),
	StringValue(String),

	Error(())
}

pub struct EventReader<R: Read> {
	xml_reader: XmlEventReader<R>
}

impl<R: Read> EventReader<R> {
	pub fn new(reader: R) -> EventReader<R> {
		let config = ParserConfig {
			trim_whitespace: false,
			whitespace_to_characters: true,
			cdata_to_characters: true,
			ignore_comments: true,
			coalesce_characters: true,
		};

		EventReader {
			xml_reader: XmlEventReader::with_config(reader, config)
		}
	}

	fn read_string<F>(&mut self, f: F) -> PlistEvent where F:FnOnce(String) -> PlistEvent {
		match self.xml_reader.next() {
			XmlEvent::Characters(s) => f(s),
			_ => PlistEvent::Error(())
		}
	}

	pub fn next(&mut self) -> Option<PlistEvent> {
		loop {
			let first_event = self.xml_reader.next();
			match first_event {
				XmlEvent::StartElement { name, .. } => match &name.local_name[..] {
					"plist" => (),
					"array" => return Some(PlistEvent::StartArray),
					"dict" => return Some(PlistEvent::StartDictionary),
					"key" => return Some(self.read_string(|s| PlistEvent::DictionaryKey(s))),
					"true" => return Some(PlistEvent::BooleanValue(true)),
					"false" => return Some(PlistEvent::BooleanValue(false)),
					"data" => return Some(self.read_string(|s| {
						match FromBase64::from_base64(&s[..]) {
							Ok(b) => PlistEvent::DataValue(b),
							Err(_) => PlistEvent::Error(())
						}
					})),
					"date" => return Some(self.read_string(|s| PlistEvent::DateValue(s))),
					"integer" => return Some(self.read_string(|s| {
						match FromStr::from_str(&s)	{
							Ok(i) => PlistEvent::IntegerValue(i),
							Err(_) => PlistEvent::Error(())
						}
					})),
					"real" => return Some(self.read_string(|s| {
						match FromStr::from_str(&s)	{
							Ok(f) => PlistEvent::FloatValue(f),
							Err(_) => PlistEvent::Error(())
						}
					})),
					"string" => return Some(self.read_string(|s| PlistEvent::StringValue(s))),
					_ => return Some(PlistEvent::Error(()))
				},
				XmlEvent::EndElement { name, .. } => match &name.local_name[..] {
					"array" => return Some(PlistEvent::EndArray),
					"dict" => return Some(PlistEvent::EndDictionary),
					_ => ()
				},
				XmlEvent::EndDocument => return None,
				_ => ()
			}
		}
	}
}

#[cfg(test)]
mod tests {
	use std::fs::File;
	use std::path::Path;

	use reader::*;

	#[test]
	fn simple() {
		use reader::PlistEvent::*;

		let reader = File::open(&Path::new("./tests/data/simple.plist")).unwrap();
		let mut event_reader = EventReader::new(reader);
		let mut events = Vec::new();
		loop {
			if let Some(event) = event_reader.next() {
				events.push(event);
			} else {
				break;
			}
		}

		let comparison = &[
			StartDictionary,
			DictionaryKey("Author".to_owned()),
			StringValue("William Shakespeare".to_owned()),
			DictionaryKey("Lines".to_owned()),
			StartArray,
			StringValue("It is a tale told by an idiot,".to_owned()),
			StringValue("Full of sound and fury, signifying nothing.".to_owned()),
			EndArray,
			DictionaryKey("Birthdate".to_owned()),
			IntegerValue(1564),
			EndDictionary
		];

		assert_eq!(events, comparison);
	}
}<commit_msg>Implement Iterator trait<commit_after>use rustc_serialize::base64::FromBase64;
use std::io::Read;
use std::str::FromStr;
use xml::reader::ParserConfig;
use xml::reader::EventReader as XmlEventReader;
use xml::reader::events::XmlEvent;

#[derive(PartialEq, Debug, Clone)]
pub enum PlistEvent {
	StartArray,
	EndArray,

	StartDictionary,
	EndDictionary,
	DictionaryKey(String),

	BooleanValue(bool),
	DataValue(Vec<u8>),
	DateValue(String),
	FloatValue(f64),
	IntegerValue(i64),
	StringValue(String),

	Error(())
}

pub struct EventReader<R: Read> {
	xml_reader: XmlEventReader<R>
}

impl<R: Read> EventReader<R> {
	pub fn new(reader: R) -> EventReader<R> {
		let config = ParserConfig {
			trim_whitespace: false,
			whitespace_to_characters: true,
			cdata_to_characters: true,
			ignore_comments: true,
			coalesce_characters: true,
		};

		EventReader {
			xml_reader: XmlEventReader::with_config(reader, config)
		}
	}

	fn read_string<F>(&mut self, f: F) -> PlistEvent where F:FnOnce(String) -> PlistEvent {
		match self.xml_reader.next() {
			XmlEvent::Characters(s) => f(s),
			_ => PlistEvent::Error(())
		}
	}
}

impl<R: Read> Iterator for EventReader<R> {
	type Item = PlistEvent;

	fn next(&mut self) -> Option<PlistEvent> {
		loop {
			let first_event = self.xml_reader.next();
			match first_event {
				XmlEvent::StartElement { name, .. } => match &name.local_name[..] {
					"plist" => (),
					"array" => return Some(PlistEvent::StartArray),
					"dict" => return Some(PlistEvent::StartDictionary),
					"key" => return Some(self.read_string(|s| PlistEvent::DictionaryKey(s))),
					"true" => return Some(PlistEvent::BooleanValue(true)),
					"false" => return Some(PlistEvent::BooleanValue(false)),
					"data" => return Some(self.read_string(|s| {
						match FromBase64::from_base64(&s[..]) {
							Ok(b) => PlistEvent::DataValue(b),
							Err(_) => PlistEvent::Error(())
						}
					})),
					"date" => return Some(self.read_string(|s| PlistEvent::DateValue(s))),
					"integer" => return Some(self.read_string(|s| {
						match FromStr::from_str(&s)	{
							Ok(i) => PlistEvent::IntegerValue(i),
							Err(_) => PlistEvent::Error(())
						}
					})),
					"real" => return Some(self.read_string(|s| {
						match FromStr::from_str(&s)	{
							Ok(f) => PlistEvent::FloatValue(f),
							Err(_) => PlistEvent::Error(())
						}
					})),
					"string" => return Some(self.read_string(|s| PlistEvent::StringValue(s))),
					_ => return Some(PlistEvent::Error(()))
				},
				XmlEvent::EndElement { name, .. } => match &name.local_name[..] {
					"array" => return Some(PlistEvent::EndArray),
					"dict" => return Some(PlistEvent::EndDictionary),
					_ => ()
				},
				XmlEvent::EndDocument => return None,
				_ => ()
			}
		}
	}
}

#[cfg(test)]
mod tests {
	use std::fs::File;
	use std::path::Path;

	use reader::*;

	#[test]
	fn simple() {
		use reader::PlistEvent::*;

		let reader = File::open(&Path::new("./tests/data/simple.plist")).unwrap();
		let event_reader = EventReader::new(reader);
		let events: Vec<PlistEvent> = event_reader.collect();

		let comparison = &[
			StartDictionary,
			DictionaryKey("Author".to_owned()),
			StringValue("William Shakespeare".to_owned()),
			DictionaryKey("Lines".to_owned()),
			StartArray,
			StringValue("It is a tale told by an idiot,".to_owned()),
			StringValue("Full of sound and fury, signifying nothing.".to_owned()),
			EndArray,
			DictionaryKey("Birthdate".to_owned()),
			IntegerValue(1564),
			EndDictionary
		];

		assert_eq!(events, comparison);
	}
}<|endoftext|>


<commit_before>use errors::ParsingError;
use errors::RedisError;
use results::RedisResult;
use std::io::BufReader;
use std::io::prelude::*;
use std::net::TcpStream;

pub struct Reader;

impl Reader {
    pub fn read(buffer: &mut BufReader<TcpStream>) -> Result<RedisResult, RedisError> {

        let mut head_line = String::new();
        try!(buffer.read_line(&mut head_line));

        let identifier = head_line.remove(0);

        match identifier{
                '$' => Reader::read_bulk_string(&head_line, buffer),
                '*' => Reader::read_array(&head_line, buffer),
                '+' => Reader::read_string(&head_line),
                ':' => Reader::read_integer(&head_line),
                '-' => Reader::read_error(&head_line),
                _ => Err(RedisError::Parse(ParsingError::BadIdentifier(identifier.to_string()))),
            }
    }

    fn read_bulk_string(head_line: & String, buffer: &mut BufReader<TcpStream>) -> Result<RedisResult, RedisError> {
        let read_byte_nb: i64 = try!(head_line.trim().parse());

        if read_byte_nb < 0 {
            Ok(RedisResult::Nil)
        } else {
            let mut result: Vec<u8> = Vec::with_capacity((read_byte_nb + 2) as usize);
            loop {
                let length = {
                    let buf = try!(buffer.fill_buf());
                    result.extend(buf.iter().cloned());

                    buf.len()
                };

                if result.len() >= (read_byte_nb + 2) as usize {
                    buffer.consume(length - (result.len() - (read_byte_nb + 2) as usize));
                    break;
                } else {
                    buffer.consume(length);
                }
            }
            result.truncate(read_byte_nb as usize);

            Ok(RedisResult::Bytes(result))
        }
    }

    fn read_string(simple_str: & String) -> Result<RedisResult, RedisError> {
        Ok(RedisResult::String(simple_str.trim().to_string()))
    }

    fn read_integer(integer_str: & String) -> Result<RedisResult, RedisError> {
        Ok(RedisResult::Int(try!(integer_str.trim().parse::<i64>())))
    }

    fn read_error(error_str: & String) -> Result<RedisResult, RedisError> {
        Err(RedisError::Response(error_str.to_string()))
    }

    fn read_array(array_str: & String, buffer: &mut BufReader<TcpStream>) -> Result<RedisResult, RedisError> {
        let read_byte_nb: i64 = try!(array_str.trim().parse());

        if read_byte_nb < 0 {
            Ok(RedisResult::Nil)
        } else {
            let mut result: Vec<RedisResult> = Vec::with_capacity(read_byte_nb as usize);

            loop {
                match Reader::read(buffer) {
                    Ok(value) => result.push(value),
                    Err(RedisError::Response(err)) => result.push(RedisResult::String(err)),
                    Err(err) => return Err(err),
                };

                --read_byte_nb;
                if read_byte_nb == 0 {
                    break;
                }
            }

            Ok(RedisResult::Array(result))
        }
    }

}<commit_msg>fix array reading<commit_after>use errors::ParsingError;
use errors::RedisError;
use results::RedisResult;
use std::io::BufReader;
use std::io::prelude::*;
use std::net::TcpStream;

pub struct Reader;

impl Reader {
    pub fn read(buffer: &mut BufReader<TcpStream>) -> Result<RedisResult, RedisError> {

        let mut head_line = String::new();
        try!(buffer.read_line(&mut head_line));

        println!("{:?}", head_line);

        let identifier = head_line.remove(0);

        match identifier{
                '$' => Reader::read_bulk_string(&head_line, buffer),
                '*' => Reader::read_array(&head_line, buffer),
                '+' => Reader::read_string(&head_line),
                ':' => Reader::read_integer(&head_line),
                '-' => Reader::read_error(&head_line),
                _ => Err(RedisError::Parse(ParsingError::BadIdentifier(identifier.to_string()))),
            }
    }

    fn read_bulk_string(head_line: & String, buffer: &mut BufReader<TcpStream>) -> Result<RedisResult, RedisError> {
        let read_byte_nb: i64 = try!(head_line.trim().parse());

        if read_byte_nb < 0 {
            Ok(RedisResult::Nil)
        } else {
            let mut result: Vec<u8> = Vec::with_capacity((read_byte_nb + 2) as usize);
            loop {
                let length = {
                    let buf = try!(buffer.fill_buf());
                    result.extend(buf.iter().cloned());

                    buf.len()
                };

                if result.len() >= (read_byte_nb + 2) as usize {
                    buffer.consume(length - (result.len() - (read_byte_nb + 2) as usize));
                    break;
                } else {
                    buffer.consume(length);
                }
            }
            result.truncate(read_byte_nb as usize);

            Ok(RedisResult::Bytes(result))
        }
    }

    fn read_string(simple_str: & String) -> Result<RedisResult, RedisError> {
        Ok(RedisResult::String(simple_str.trim().to_string()))
    }

    fn read_integer(integer_str: & String) -> Result<RedisResult, RedisError> {
        Ok(RedisResult::Int(try!(integer_str.trim().parse::<i64>())))
    }

    fn read_error(error_str: & String) -> Result<RedisResult, RedisError> {
        Err(RedisError::Response(error_str.to_string()))
    }

    fn read_array(array_str: & String, buffer: &mut BufReader<TcpStream>) -> Result<RedisResult, RedisError> {
        let mut read_elmt_nb: i64 = try!(array_str.trim().parse());

        if read_elmt_nb < 0 {
            Ok(RedisResult::Nil)
        } else {
            let mut result: Vec<RedisResult> = Vec::with_capacity(read_elmt_nb as usize);

            loop {
                match Reader::read(buffer) {
                    Ok(value) => result.push(value),
                    Err(RedisError::Response(err)) => result.push(RedisResult::String(err)),
                    Err(err) => return Err(err),
                };

                read_elmt_nb = read_elmt_nb - 1;
                if read_elmt_nb == 0 {
                    break;
                }
            }

            Ok(RedisResult::Array(result))
        }
    }

}<|endoftext|>


<commit_before>// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate gltf as gltf_crate;

use gltf_crate::v1 as gltf;

fn main() {
    let path = std::env::args().nth(1).unwrap();
    match gltf::import::<_, gltf::extras::Any>(path) {
        Ok(root) => {
            println!("glTF version 1.0");
            println!("{:#?}", root);
        },
        Err(err) => println!("Error: {:?}", err),
    }
}
<commit_msg>Updated gltf_display example<commit_after>// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate gltf;

use gltf::{v1, v2};

fn main() {
    let path = std::env::args().nth(1).unwrap();
    match v1::import::<_, v1::extras::Any>(&path) {
        Ok(root) => {
            println!("glTF version 1.0");
            println!("{:#?}", root);
        },
        Err(_) => match v2::import::<_, v2::extras::Any>(&path) {
            Ok(root) => {
                println!("glTF version 2.0");
                println!("{:?}", root);
            },
            Err(err) => {
                println!("Error: {:#?}", err);
            },
        },
    }
}
<|endoftext|>


<commit_before>// This code is heavily inspired by hoedown.

//
// Performance observations:
//
// Note that buf.head() == Some(&ch) is much faster than buf[0] == ch.
//

extern crate extra;
use extra::test::BenchHarness;

static SP: u8 = ' ' as u8;
static NL: u8 = '\n' as u8;

/// Returns Some(`rem`) if the line is a horizontal rule, with `rem` being the
/// buf after the hrule. Otherwise returns None.
fn is_hrule<'a>(buf: &'a[u8]) -> Option<&'a[u8]> {
    let mut buf = buf;

    if buf.len() < 3 { return None }

    // Skip up to three leading spaces.
    //
    // We don't need to care about tabs here as in this position they are
    // equivalent to 4 spaces, which means we would not parse this as a
    // hrule.
    if buf.head() == Some(&SP) { buf = buf.tail(); }
    if buf.head() == Some(&SP) { buf = buf.tail(); }
    if buf.head() == Some(&SP) { buf = buf.tail(); }

    // We need at least 3 items
    if buf.len() < 3 { return None }

    let item = buf[0];

    if !(item == ('*' as u8) || item == ('-' as u8) || item == ('_' as u8)) {
        return None;
    }

    // The count of '*', '-' or '_'
    let mut cnt: uint = 0;

    // Counts the consumed spaces (and the final NL)
    let mut spc: uint = 0;

    for &ch in buf.iter() {
        if      ch == item { cnt += 1; }
        else if ch == NL   { spc += 1; break; }
        else if ch == SP   { spc += 1; }
        else               { return None; }
    }

    if cnt >= 3 {
        Some(buf.slice_from(cnt + spc))
    } else {
        None
    }
}

#[test]
fn test_is_hrule() {
    // examples as given on the markdown homepage
    assert!(is_hrule(bytes!("* * *\n")).is_some());
    assert!(is_hrule(bytes!("***\n")).is_some());
    assert!(is_hrule(bytes!("*****\n")).is_some());
    assert!(is_hrule(bytes!("- - -\n")).is_some());
    assert!(is_hrule(bytes!("---------------------------------------\n")).is_some());

    // up to three spaces ignored
    assert!(is_hrule(bytes!(" ***\n")).is_some());
    assert!(is_hrule(bytes!("  ***\n")).is_some());
    assert!(is_hrule(bytes!("   ***\n")).is_some());

    // but not four, or a tab which is equivalent to four spaces

    assert!(is_hrule(bytes!("    ***\n")).is_none());
    assert!(is_hrule(bytes!("\t***\n")).is_none());

    // need at least three
    assert!(is_hrule(bytes!("*\n")).is_none());
    assert!(is_hrule(bytes!("**\n")).is_none());
    assert!(is_hrule(bytes!("* *\n")).is_none());
    assert!(is_hrule(bytes!("   * *\n")).is_none());

    // Also works without newline at the end
    assert!(is_hrule(bytes!("* * *")).is_some());

    // And underscores also supported
    assert!(is_hrule(bytes!("___")).is_some());
    assert!(is_hrule(bytes!("______________")).is_some());
    assert!(is_hrule(bytes!(" ______________")).is_some());

    // Test if the remaining buf actually works.
    let s = bytes!("   * * *\nremaining");
    let res = is_hrule(s);
    assert!(res.is_some());
    assert_eq!(res.unwrap(), bytes!("remaining"));
}

#[bench]
fn bench_is_hrule(b: &mut BenchHarness) {
    let s = bytes!("   * * * * * * * * * * * * * * * *\n");
    b.iter(|| is_hrule(s));
}
<commit_msg>Make is_hrule failsafe by not depending on buf.len() checks.<commit_after>// This code is heavily inspired by hoedown.

//
// Performance observations:
//
// Note that buf.head() == Some(&ch) is much faster than buf[0] == ch.
//

extern crate extra;
use extra::test::BenchHarness;

static SP: u8 = ' ' as u8;
static NL: u8 = '\n' as u8;
static STAR: u8 = '*' as u8;
static DASH: u8 = '-' as u8;
static UNDERSCORE: u8 = '_' as u8;

/// Returns Some(`rem`) if the line is a horizontal rule, with `rem` being the
/// buf after the hrule. Otherwise returns None.
fn is_hrule<'a>(buf: &'a[u8]) -> Option<&'a[u8]> {
    let mut buf = buf;

    // Skip up to three leading spaces.
    //
    // We don't need to care about tabs here as in this position they are
    // equivalent to 4 spaces, which means we would not parse this as a
    // hrule.
    if buf.head() == Some(&SP) { buf = buf.tail(); }
    if buf.head() == Some(&SP) { buf = buf.tail(); }
    if buf.head() == Some(&SP) { buf = buf.tail(); }

    let item = match buf.head() {
        Some(&c) if c == STAR ||
                    c == DASH ||
                    c == UNDERSCORE => c,
        _                           => return None
    };

    // The count of '*', '-' or '_'
    let mut cnt: uint = 0;

    // Counts the consumed spaces (and the final NL)
    let mut spc: uint = 0;

    for &ch in buf.iter() {
        if      ch == item { cnt += 1; }
        else if ch == NL   { spc += 1; break; }
        else if ch == SP   { spc += 1; }
        else               { return None; }
    }

    if cnt >= 3 {
        Some(buf.slice_from(cnt + spc))
    } else {
        None
    }
}

#[test]
fn test_is_hrule() {
    // examples as given on the markdown homepage
    assert!(is_hrule(bytes!("* * *\n")).is_some());
    assert!(is_hrule(bytes!("***\n")).is_some());
    assert!(is_hrule(bytes!("*****\n")).is_some());
    assert!(is_hrule(bytes!("- - -\n")).is_some());
    assert!(is_hrule(bytes!("---------------------------------------\n")).is_some());

    // up to three spaces ignored
    assert!(is_hrule(bytes!(" ***\n")).is_some());
    assert!(is_hrule(bytes!("  ***\n")).is_some());
    assert!(is_hrule(bytes!("   ***\n")).is_some());

    // but not four, or a tab which is equivalent to four spaces

    assert!(is_hrule(bytes!("    ***\n")).is_none());
    assert!(is_hrule(bytes!("\t***\n")).is_none());

    // need at least three
    assert!(is_hrule(bytes!("*\n")).is_none());
    assert!(is_hrule(bytes!("**\n")).is_none());
    assert!(is_hrule(bytes!("* *\n")).is_none());
    assert!(is_hrule(bytes!("   * *\n")).is_none());

    // Also works without newline at the end
    assert!(is_hrule(bytes!("* * *")).is_some());

    // And underscores also supported
    assert!(is_hrule(bytes!("___")).is_some());
    assert!(is_hrule(bytes!("______________")).is_some());
    assert!(is_hrule(bytes!(" ______________")).is_some());

    // Test if the remaining buf actually works.
    let s = bytes!("   * * *\nremaining");
    let res = is_hrule(s);
    assert!(res.is_some());
    assert_eq!(res.unwrap(), bytes!("remaining"));
}

#[bench]
fn bench_is_hrule(b: &mut BenchHarness) {
    let s = bytes!("   * * * * * * * * * * * * * * * *\n");
    b.iter(|| is_hrule(s));
}
<|endoftext|>


<commit_before>use crate::header_builder::FnHeaderBuilder;
use proc_macro2::{TokenStream, TokenTree};
use quote::{format_ident, quote, quote_spanned, ToTokens};
use std::iter::FromIterator;
use syn::punctuated::Punctuated;
use syn::token::Comma;
use syn::{
    parse_quote, Attribute, Block, FnArg, GenericParam, Generics, Ident, ImplItem, ImplItemMethod,
    Item, ItemFn, ItemImpl, ItemMod, ItemTrait, Pat, PatIdent, PatType, Receiver, ReturnType,
    Signature, TraitItem, TraitItemMethod, Type, Visibility, WhereClause,
};

#[derive(Clone, Copy)]
enum Context<'a> {
    Trait,
    Impl {
        receiver: &'a Type,
        impl_generics: &'a Generics,
    },
    Fn,
}

pub fn inject_item(item: &mut Item) {
    match *item {
        Item::Fn(ref mut item_fn) => inject_fn(item_fn),
        Item::Mod(ref mut item_mod) => inject_mod(item_mod),
        Item::Trait(ref mut item_trait) => inject_trait(item_trait),
        Item::Impl(ref mut item_impl) => inject_impl(item_impl),
        _ => (),
    }
}

fn inject_fn(item_fn: &mut ItemFn) {
    inject_any_fn(
        Context::Fn,
        &FnHeaderBuilder::StaticFn,
        &item_fn.attrs,
        &mut item_fn.sig,
        &mut *item_fn.block,
    );
}

fn inject_mod(item_mod: &mut ItemMod) {
    if is_not_mockable(&item_mod.attrs) {
        return;
    }
    item_mod
        .content
        .iter_mut()
        .flat_map(|c| &mut c.1)
        .for_each(inject_item)
}

fn inject_trait(item_trait: &mut ItemTrait) {
    if is_not_mockable(&item_trait.attrs) {
        return;
    }

    let context = Context::Trait;
    for item in &mut item_trait.items {
        if let TraitItem::Method(TraitItemMethod {
            ref attrs,
            ref mut sig,
            default: Some(ref mut block),
            ..
        }) = *item
        {
            inject_any_fn(context, &FnHeaderBuilder::TraitDefault, attrs, sig, block);
        }
    }
}

fn inject_impl(item_impl: &mut ItemImpl) {
    if is_not_mockable(&item_impl.attrs) {
        return;
    }
    let builder = match item_impl.trait_ {
        Some((_, ref path, _)) => FnHeaderBuilder::TraitImpl(&path.segments),
        None => FnHeaderBuilder::StructImpl,
    };

    let context = Context::Impl {
        receiver: &item_impl.self_ty,
        impl_generics: &item_impl.generics,
    };

    for impl_item in &mut item_impl.items {
        if let ImplItem::Method(ref mut item_method) = *impl_item {
            if is_impl_fn_mockabile(&builder, item_method) {
                inject_any_fn(
                    context,
                    &builder,
                    &item_method.attrs,
                    &mut item_method.sig,
                    &mut item_method.block,
                );
            }
        }
    }
}

fn is_impl_fn_mockabile(builder: &FnHeaderBuilder, item_method: &ImplItemMethod) -> bool {
    if let FnHeaderBuilder::TraitImpl(ref segments) = *builder {
        if let Some(segment) = segments.last() {
            if segment.arguments.is_empty() && segment.ident == "Drop" {
                if item_method.sig.ident == "drop" {
                    return false;
                }
            }
        }
    }
    true
}

fn inject_any_fn(
    context: Context,
    builder: &FnHeaderBuilder,
    attrs: &Vec<Attribute>,
    fn_decl: &mut Signature,
    block: &mut Block,
) {
    if fn_decl.constness.is_some()
        || fn_decl.unsafety.is_some()
        || fn_decl.variadic.is_some()
        || is_not_mockable(attrs)
    {
        return;
    }

    // Transform async functions as `async-trait`
    // See: https://github.com/dtolnay/async-trait
    if let Some(_) = fn_decl.asyncness {
        let inner = format_ident!("__{}", fn_decl.ident);
        let args = fn_decl.inputs.iter().enumerate().map(|(i, arg)| match arg {
            FnArg::Receiver(Receiver { self_token, .. }) => quote!(#self_token),
            FnArg::Typed(arg) => {
                if let Pat::Ident(PatIdent { ident, .. }) = &*arg.pat {
                    quote!(#ident)
                } else {
                    positional_arg(i).into_token_stream()
                }
            }
        });
        let mut generics = fn_decl
            .generics
            .type_params()
            .map(|param| param.ident.clone())
            .collect::<Vec<_>>();

        for stmt in &mut block.stmts {
            replace_self_in_stmt(stmt);
        }

        let mut fn_inner = fn_decl.clone();
        fn_inner.ident = inner.clone();

        if let Context::Impl { impl_generics, .. } = context {
            // declare impl generics on inner fn
            fn_inner
                .generics
                .params
                .extend(impl_generics.params.clone());

            // add impl generics to inner call
            generics.extend(impl_generics.type_params().map(|param| param.ident.clone()))
        }

        match fn_inner.inputs.iter_mut().next() {
            Some(
                arg
                @
                FnArg::Receiver(Receiver {
                    reference: Some(_), ..
                }),
            ) => {
                let (self_token, mutability, lifetime) = match arg {
                    FnArg::Receiver(Receiver {
                        self_token,
                        mutability,
                        reference: Some((_, lifetime)),
                        ..
                    }) => (self_token, mutability, lifetime),
                    _ => unreachable!(),
                };
                let under_self = Ident::new("_self", self_token.span);
                match context {
                    Context::Impl { receiver, .. } => {
                        // TODO: parse reference
                        *arg = parse_quote! {
                            #under_self: &#lifetime #mutability #receiver
                        };
                    }
                    _ => (),
                };
            }
            Some(arg @ FnArg::Receiver(_)) => {
                let (self_token, mutability) = match arg {
                    FnArg::Receiver(Receiver {
                        self_token,
                        mutability,
                        ..
                    }) => (self_token, mutability),
                    _ => unreachable!(),
                };
                let under_self = Ident::new("_self", self_token.span);
                match context {
                    Context::Impl { receiver, .. } => {
                        *arg = parse_quote! {
                            #under_self: #mutability #receiver
                        };
                    }
                    _ => (),
                };
            }
            _ => {}
        };

        // this is the standalone async fn
        let func = ItemFn {
            attrs: attrs.clone(),
            vis: Visibility::Inherited,
            sig: fn_inner,
            block: Box::new(block.clone()),
        };

        let brace = block.brace_token;
        let box_pin = quote_spanned!(brace.span=> {
            Box::pin(#inner::<#(#generics),*>(#(#args),*))
        });
        *block = parse_quote!(#box_pin);
        block.brace_token = brace;

        block.stmts.insert(0, syn::Stmt::Item(Item::Fn(func)));

        fn_decl.asyncness = None;

        let where_clause = fn_decl
            .generics
            .where_clause
            .get_or_insert_with(|| WhereClause {
                where_token: Default::default(),
                predicates: Punctuated::new(),
            });

        match fn_decl.inputs.iter_mut().next() {
            Some(
                arg
                @
                FnArg::Receiver(Receiver {
                    reference: Some(_), ..
                }),
            ) => {
                let (self_token, mutability, lifetime) = match arg {
                    FnArg::Receiver(Receiver {
                        self_token,
                        mutability,
                        reference: Some((_, lifetime)),
                        ..
                    }) => (self_token, mutability, lifetime),
                    _ => unreachable!(),
                };
                *arg = parse_quote! {
                    &'life_self #lifetime #mutability #self_token
                };
            }
            Some(arg @ FnArg::Receiver(_)) => {
                let (self_token, mutability) = match arg {
                    FnArg::Receiver(Receiver {
                        self_token,
                        mutability,
                        ..
                    }) => (self_token, mutability),
                    _ => unreachable!(),
                };
                *arg = parse_quote! {
                    #mutability #self_token
                };
            }
            _ => {}
        };

        fn_decl.generics.params.push(parse_quote!('life_self));

        for param in fn_decl.generics.params.iter() {
            match param {
                GenericParam::Type(param) => {
                    let param = &param.ident;
                    where_clause
                        .predicates
                        .push(parse_quote!(#param: 'mocktopus));
                }
                GenericParam::Lifetime(param) => {
                    let param = &param.lifetime;
                    where_clause
                        .predicates
                        .push(parse_quote!(#param: 'mocktopus));
                }
                GenericParam::Const(_) => {}
            }
        }
        fn_decl.generics.params.push(parse_quote!('mocktopus));

        if let Context::Impl { .. } = context {
            where_clause.predicates.push(parse_quote!(Self: 'mocktopus));
        }

        let ret = match &fn_decl.output {
            ReturnType::Default => quote!(()),
            ReturnType::Type(_, ret) => quote!(#ret),
        };
        let bounds = quote!(::core::marker::Send + 'mocktopus);
        fn_decl.output = parse_quote! {
            -> ::core::pin::Pin<Box<
                dyn ::core::future::Future<Output = #ret> + #bounds
            >>
        };
    }

    unignore_fn_args(&mut fn_decl.inputs);
    let header_stmt = builder.build(fn_decl, block.brace_token.span);
    block.stmts.insert(0, header_stmt);
}

fn unignore_fn_args(inputs: &mut Punctuated<FnArg, Comma>) {
    for (i, fn_arg) in inputs.iter_mut().enumerate() {
        if let FnArg::Typed(PatType { ref mut pat, .. }) = *fn_arg {
            let (span, attrs) = match **pat {
                Pat::Wild(ref pat_wild) => {
                    (pat_wild.underscore_token.spans[0], pat_wild.attrs.clone())
                }
                _ => continue,
            };
            *pat = Box::new(Pat::Ident(PatIdent {
                by_ref: None,
                mutability: None,
                ident: Ident::new(&format!("__mocktopus_unignored_argument_{}__", i), span),
                subpat: None,
                attrs,
            }));
        }
    }
}

const INJECTOR_STOPPER_ATTRS: [&str; 2] = ["mockable", "not_mockable"];

fn is_not_mockable(attrs: &Vec<Attribute>) -> bool {
    attrs
        .iter()
        .filter_map(|a| a.path.segments.last())
        .map(|segment| segment.ident.to_string())
        .any(|i| INJECTOR_STOPPER_ATTRS.contains(&&*i))
}

fn positional_arg(i: usize) -> Ident {
    format_ident!("__arg{}", i)
}

fn replace_self_in_stmt(stmt: &mut syn::Stmt) {
    match stmt {
        syn::Stmt::Semi(expr, _) => replace_self_in_expr(expr),
        syn::Stmt::Expr(expr) => replace_self_in_expr(expr),
        _ => (),
    }
}

fn replace_self_in_expr(expr: &mut syn::Expr) {
    match expr {
        syn::Expr::Assign(expr) => {
            replace_self_in_expr(&mut expr.left);
            replace_self_in_expr(&mut expr.right);
        }
        syn::Expr::AssignOp(expr) => {
            replace_self_in_expr(&mut expr.left);
            replace_self_in_expr(&mut expr.right);
        }
        syn::Expr::Await(expr) => {
            replace_self_in_expr(&mut expr.base);
        }
        syn::Expr::Block(expr) => {
            for stmt in &mut expr.block.stmts {
                replace_self_in_stmt(stmt);
            }
        }
        syn::Expr::Call(expr) => {
            replace_self_in_expr(&mut expr.func);
            for arg in &mut expr.args {
                replace_self_in_expr(arg);
            }
        }
        syn::Expr::Field(expr) => {
            replace_self_in_expr(&mut expr.base);
        }
        syn::Expr::Let(expr) => {
            replace_self_in_expr(&mut expr.expr);
        }
        syn::Expr::Macro(expr) => {
            replace_self_in_token_stream(&mut expr.mac.tokens);
            replace_self_in_path(&mut expr.mac.path);
            for attr in &mut expr.attrs {
                replace_self_in_path(&mut attr.path);
            }
        }
        syn::Expr::Path(expr) => replace_self_in_path(&mut expr.path),
        _ => (),
    }
}

fn replace_self_in_path(path: &mut syn::Path) {
    for segment in &mut path.segments {
        if segment.ident == "self" {
            let span = segment.ident.span();
            segment.ident = Ident::new("_self", span);
        }
    }
}

fn replace_self_in_token_stream(tokens: &mut TokenStream) {
    let mut out = Vec::new();
    let mut iter = tokens.clone().into_iter().peekable();
    while let Some(tt) = iter.next() {
        match tt {
            TokenTree::Ident(mut ident) => {
                if ident == "self" {
                    let span = ident.span();
                    ident = Ident::new("_self", span);
                    out.push(TokenTree::Ident(ident));
                } else {
                    out.push(TokenTree::Ident(ident));
                }
            }
            other => out.push(other),
        }
    }
    *tokens = TokenStream::from_iter(out);
}
<commit_msg>cleanup and match more expressions<commit_after>use crate::header_builder::FnHeaderBuilder;
use proc_macro2::{TokenStream, TokenTree};
use quote::{format_ident, quote, quote_spanned, ToTokens};
use std::iter::FromIterator;
use syn::punctuated::Punctuated;
use syn::token::Comma;
use syn::{
    parse_quote, Attribute, Block, FnArg, GenericParam, Generics, Ident, ImplItem, ImplItemMethod,
    Item, ItemFn, ItemImpl, ItemMod, ItemTrait, Pat, PatIdent, PatType, Receiver, ReturnType,
    Signature, TraitItem, TraitItemMethod, Type, Visibility, WhereClause,
};

#[derive(Clone, Copy)]
enum Context<'a> {
    Trait,
    Impl {
        receiver: &'a Type,
        impl_generics: &'a Generics,
    },
    Fn,
}

pub fn inject_item(item: &mut Item) {
    match *item {
        Item::Fn(ref mut item_fn) => inject_fn(item_fn),
        Item::Mod(ref mut item_mod) => inject_mod(item_mod),
        Item::Trait(ref mut item_trait) => inject_trait(item_trait),
        Item::Impl(ref mut item_impl) => inject_impl(item_impl),
        _ => (),
    }
}

fn inject_fn(item_fn: &mut ItemFn) {
    inject_any_fn(
        Context::Fn,
        &FnHeaderBuilder::StaticFn,
        &item_fn.attrs,
        &mut item_fn.sig,
        &mut *item_fn.block,
    );
}

fn inject_mod(item_mod: &mut ItemMod) {
    if is_not_mockable(&item_mod.attrs) {
        return;
    }
    item_mod
        .content
        .iter_mut()
        .flat_map(|c| &mut c.1)
        .for_each(inject_item)
}

fn inject_trait(item_trait: &mut ItemTrait) {
    if is_not_mockable(&item_trait.attrs) {
        return;
    }

    let context = Context::Trait;
    for item in &mut item_trait.items {
        if let TraitItem::Method(TraitItemMethod {
            ref attrs,
            ref mut sig,
            default: Some(ref mut block),
            ..
        }) = *item
        {
            inject_any_fn(context, &FnHeaderBuilder::TraitDefault, attrs, sig, block);
        }
    }
}

fn inject_impl(item_impl: &mut ItemImpl) {
    if is_not_mockable(&item_impl.attrs) {
        return;
    }
    let builder = match item_impl.trait_ {
        Some((_, ref path, _)) => FnHeaderBuilder::TraitImpl(&path.segments),
        None => FnHeaderBuilder::StructImpl,
    };

    let context = Context::Impl {
        receiver: &item_impl.self_ty,
        impl_generics: &item_impl.generics,
    };

    for impl_item in &mut item_impl.items {
        if let ImplItem::Method(ref mut item_method) = *impl_item {
            if is_impl_fn_mockabile(&builder, item_method) {
                inject_any_fn(
                    context,
                    &builder,
                    &item_method.attrs,
                    &mut item_method.sig,
                    &mut item_method.block,
                );
            }
        }
    }
}

fn is_impl_fn_mockabile(builder: &FnHeaderBuilder, item_method: &ImplItemMethod) -> bool {
    if let FnHeaderBuilder::TraitImpl(ref segments) = *builder {
        if let Some(segment) = segments.last() {
            if segment.arguments.is_empty() && segment.ident == "Drop" {
                if item_method.sig.ident == "drop" {
                    return false;
                }
            }
        }
    }
    true
}

fn inject_any_fn(
    context: Context,
    builder: &FnHeaderBuilder,
    attrs: &Vec<Attribute>,
    fn_decl: &mut Signature,
    block: &mut Block,
) {
    if fn_decl.constness.is_some()
        || fn_decl.unsafety.is_some()
        || fn_decl.variadic.is_some()
        || is_not_mockable(attrs)
    {
        return;
    }

    if let Some(_) = fn_decl.asyncness {
        inject_async_fn(context, attrs, fn_decl, block);
    }

    unignore_fn_args(&mut fn_decl.inputs);
    let header_stmt = builder.build(fn_decl, block.brace_token.span);
    block.stmts.insert(0, header_stmt);
}

// Transform async functions as `async-trait`
// See: https://github.com/dtolnay/async-trait
fn inject_async_fn(
    context: Context,
    attrs: &Vec<Attribute>,
    outer_sig: &mut Signature,
    block: &mut Block,
) {
    let args = outer_sig
        .inputs
        .iter()
        .enumerate()
        .map(|(i, arg)| match arg {
            FnArg::Receiver(Receiver { self_token, .. }) => quote!(#self_token),
            FnArg::Typed(arg) => {
                if let Pat::Ident(PatIdent { ident, .. }) = &*arg.pat {
                    quote!(#ident)
                } else {
                    positional_arg(i).into_token_stream()
                }
            }
        });

    let mut generics = outer_sig
        .generics
        .type_params()
        .map(|param| param.ident.clone())
        .collect::<Vec<_>>();

    let mut inner_sig = outer_sig.clone();

    let inner_ident = format_ident!("__{}", outer_sig.ident);
    inner_sig.ident = inner_ident.clone();

    if let Context::Impl { impl_generics, .. } = context {
        // declare impl generics on inner fn
        inner_sig
            .generics
            .params
            .extend(impl_generics.params.clone());

        // add impl generics to inner call
        generics.extend(impl_generics.type_params().map(|param| param.ident.clone()))
    }

    match inner_sig.inputs.iter_mut().next() {
        Some(
            arg @ FnArg::Receiver(Receiver {
                reference: Some(_), ..
            }),
        ) => {
            let (self_token, mutability, lifetime) = match arg {
                FnArg::Receiver(Receiver {
                    self_token,
                    mutability,
                    reference: Some((_, lifetime)),
                    ..
                }) => (self_token, mutability, lifetime),
                _ => unreachable!(),
            };
            let under_self = Ident::new("_self", self_token.span);
            match context {
                Context::Impl { receiver, .. } => {
                    *arg = parse_quote! {
                        #under_self: &#lifetime #mutability #receiver
                    };
                }
                _ => (),
            };
        }
        Some(arg @ FnArg::Receiver(_)) => {
            let (self_token, mutability) = match arg {
                FnArg::Receiver(Receiver {
                    self_token,
                    mutability,
                    ..
                }) => (self_token, mutability),
                _ => unreachable!(),
            };
            let under_self = Ident::new("_self", self_token.span);
            match context {
                Context::Impl { receiver, .. } => {
                    *arg = parse_quote! {
                        #under_self: #mutability #receiver
                    };
                }
                _ => (),
            };
        }
        _ => {}
    };

    for stmt in &mut block.stmts {
        replace_self_in_stmt(stmt);
    }

    // this is the standalone async fn
    let inner_fn = ItemFn {
        attrs: attrs.clone(),
        vis: Visibility::Inherited,
        sig: inner_sig,
        block: Box::new(block.clone()),
    };

    // delegate call to async fn
    let brace = block.brace_token;
    let box_pin = quote_spanned!(brace.span=> {
        Box::pin(#inner_ident::<#(#generics),*>(#(#args),*))
    });
    *block = parse_quote!(#box_pin);
    block.brace_token = brace;

    // insert standalone function at start
    block.stmts.insert(0, syn::Stmt::Item(Item::Fn(inner_fn)));

    let where_clause = outer_sig
        .generics
        .where_clause
        .get_or_insert_with(|| WhereClause {
            where_token: Default::default(),
            predicates: Punctuated::new(),
        });

    match outer_sig.inputs.iter_mut().next() {
        Some(
            arg @ FnArg::Receiver(Receiver {
                reference: Some(_), ..
            }),
        ) => {
            let (self_token, mutability, lifetime) = match arg {
                FnArg::Receiver(Receiver {
                    self_token,
                    mutability,
                    reference: Some((_, lifetime)),
                    ..
                }) => (self_token, mutability, lifetime),
                _ => unreachable!(),
            };
            *arg = parse_quote! {
                &'life_self #lifetime #mutability #self_token
            };
        }
        Some(arg @ FnArg::Receiver(_)) => {
            let (self_token, mutability) = match arg {
                FnArg::Receiver(Receiver {
                    self_token,
                    mutability,
                    ..
                }) => (self_token, mutability),
                _ => unreachable!(),
            };
            *arg = parse_quote! {
                #mutability #self_token
            };
        }
        _ => {}
    };

    outer_sig.generics.params.push(parse_quote!('life_self));
    for param in outer_sig.generics.params.iter() {
        match param {
            GenericParam::Type(param) => {
                let param = &param.ident;
                where_clause
                    .predicates
                    .push(parse_quote!(#param: 'mocktopus));
            }
            GenericParam::Lifetime(param) => {
                let param = &param.lifetime;
                where_clause
                    .predicates
                    .push(parse_quote!(#param: 'mocktopus));
            }
            GenericParam::Const(_) => {}
        }
    }
    outer_sig.generics.params.push(parse_quote!('mocktopus));

    if let Context::Impl { .. } = context {
        where_clause.predicates.push(parse_quote!(Self: 'mocktopus));
    }

    outer_sig.asyncness = None;

    let ret = match &outer_sig.output {
        ReturnType::Default => quote!(()),
        ReturnType::Type(_, ret) => quote!(#ret),
    };
    let bounds = quote!(::core::marker::Send + 'mocktopus);
    outer_sig.output = parse_quote! {
        -> ::core::pin::Pin<Box<
            dyn ::core::future::Future<Output = #ret> + #bounds
        >>
    };
}

fn unignore_fn_args(inputs: &mut Punctuated<FnArg, Comma>) {
    for (i, fn_arg) in inputs.iter_mut().enumerate() {
        if let FnArg::Typed(PatType { ref mut pat, .. }) = *fn_arg {
            let (span, attrs) = match **pat {
                Pat::Wild(ref pat_wild) => {
                    (pat_wild.underscore_token.spans[0], pat_wild.attrs.clone())
                }
                _ => continue,
            };
            *pat = Box::new(Pat::Ident(PatIdent {
                by_ref: None,
                mutability: None,
                ident: Ident::new(&format!("__mocktopus_unignored_argument_{}__", i), span),
                subpat: None,
                attrs,
            }));
        }
    }
}

const INJECTOR_STOPPER_ATTRS: [&str; 2] = ["mockable", "not_mockable"];

fn is_not_mockable(attrs: &Vec<Attribute>) -> bool {
    attrs
        .iter()
        .filter_map(|a| a.path.segments.last())
        .map(|segment| segment.ident.to_string())
        .any(|i| INJECTOR_STOPPER_ATTRS.contains(&&*i))
}

fn positional_arg(i: usize) -> Ident {
    format_ident!("__arg{}", i)
}

fn replace_self_in_stmt(stmt: &mut syn::Stmt) {
    match stmt {
        syn::Stmt::Semi(expr, _) => replace_self_in_expr(expr),
        syn::Stmt::Expr(expr) => replace_self_in_expr(expr),
        _ => (),
    }
}

fn replace_self_in_expr(expr: &mut syn::Expr) {
    match expr {
        syn::Expr::Array(expr) => {
            for elem in &mut expr.elems {
                replace_self_in_expr(elem);
            }
        }
        syn::Expr::Assign(expr) => {
            replace_self_in_expr(&mut expr.left);
            replace_self_in_expr(&mut expr.right);
        }
        syn::Expr::AssignOp(expr) => {
            replace_self_in_expr(&mut expr.left);
            replace_self_in_expr(&mut expr.right);
        }
        syn::Expr::Async(expr) => {
            for stmt in &mut expr.block.stmts {
                replace_self_in_stmt(stmt);
            }
        }
        syn::Expr::Await(expr) => {
            replace_self_in_expr(&mut expr.base);
        }
        syn::Expr::Binary(expr) => {
            replace_self_in_expr(&mut expr.left);
            replace_self_in_expr(&mut expr.right);
        }
        syn::Expr::Block(expr) => {
            for stmt in &mut expr.block.stmts {
                replace_self_in_stmt(stmt);
            }
        }
        syn::Expr::Box(expr) => {
            replace_self_in_expr(&mut expr.expr);
        }
        syn::Expr::Call(expr) => {
            replace_self_in_expr(&mut expr.func);
            for arg in &mut expr.args {
                replace_self_in_expr(arg);
            }
        }
        syn::Expr::Cast(expr) => {
            replace_self_in_expr(&mut expr.expr);
        }
        syn::Expr::Closure(expr) => {
            replace_self_in_expr(&mut expr.body);
        }
        syn::Expr::Field(expr) => {
            replace_self_in_expr(&mut expr.base);
        }
        syn::Expr::ForLoop(expr) => {
            replace_self_in_expr(&mut expr.expr);
        }
        syn::Expr::Group(expr) => {
            replace_self_in_expr(&mut expr.expr);
        }
        syn::Expr::If(expr) => {
            replace_self_in_expr(&mut expr.cond);
            for stmt in &mut expr.then_branch.stmts {
                replace_self_in_stmt(stmt);
            }
            if let Some((_, expr)) = &mut expr.else_branch {
                replace_self_in_expr(expr);
            }
        }
        syn::Expr::Let(expr) => {
            replace_self_in_expr(&mut expr.expr);
        }
        syn::Expr::Loop(expr) => {
            for stmt in &mut expr.body.stmts {
                replace_self_in_stmt(stmt);
            }
        }
        syn::Expr::Macro(expr) => {
            replace_self_in_token_stream(&mut expr.mac.tokens);
            replace_self_in_path(&mut expr.mac.path);
            for attr in &mut expr.attrs {
                replace_self_in_path(&mut attr.path);
            }
        }
        syn::Expr::Match(expr) => {
            replace_self_in_expr(&mut expr.expr);
        }
        syn::Expr::MethodCall(expr) => {
            replace_self_in_expr(&mut expr.receiver);
        }
        syn::Expr::Paren(expr) => {
            replace_self_in_expr(&mut expr.expr);
        }
        syn::Expr::Path(expr) => replace_self_in_path(&mut expr.path),
        syn::Expr::Range(expr) => {
            if let Some(from) = &mut expr.from {
                replace_self_in_expr(from);
            }
            if let Some(to) = &mut expr.to {
                replace_self_in_expr(to);
            }
        }
        syn::Expr::Reference(expr) => {
            replace_self_in_expr(&mut expr.expr);
        }
        syn::Expr::Return(expr) => {
            if let Some(ret) = &mut expr.expr {
                replace_self_in_expr(ret);
            }
        }
        syn::Expr::Struct(expr) => {
            replace_self_in_path(&mut expr.path);
            for field in &mut expr.fields {
                replace_self_in_expr(&mut field.expr);
            }
        }
        syn::Expr::Try(expr) => {
            replace_self_in_expr(&mut expr.expr);
        }
        syn::Expr::TryBlock(expr) => {
            for stmt in &mut expr.block.stmts {
                replace_self_in_stmt(stmt);
            }
        }
        syn::Expr::Tuple(expr) => {
            for elem in &mut expr.elems {
                replace_self_in_expr(elem);
            }
        }
        syn::Expr::Unary(expr) => {
            replace_self_in_expr(&mut expr.expr);
        }
        syn::Expr::Unsafe(expr) => {
            for stmt in &mut expr.block.stmts {
                replace_self_in_stmt(stmt);
            }
        }
        syn::Expr::While(expr) => {
            replace_self_in_expr(&mut expr.cond);
            for stmt in &mut expr.body.stmts {
                replace_self_in_stmt(stmt);
            }
        }
        syn::Expr::Yield(expr) => {
            if let Some(expr) = &mut expr.expr {
                replace_self_in_expr(expr);
            }
        }
        _ => (),
    }
}

fn replace_self_in_path(path: &mut syn::Path) {
    for segment in &mut path.segments {
        if segment.ident == "self" {
            let span = segment.ident.span();
            segment.ident = Ident::new("_self", span);
        }
    }
}

fn replace_self_in_token_stream(tokens: &mut TokenStream) {
    let mut out = Vec::new();
    let mut iter = tokens.clone().into_iter().peekable();
    while let Some(tt) = iter.next() {
        match tt {
            TokenTree::Ident(mut ident) => {
                if ident == "self" {
                    let span = ident.span();
                    ident = Ident::new("_self", span);
                    out.push(TokenTree::Ident(ident));
                } else {
                    out.push(TokenTree::Ident(ident));
                }
            }
            other => out.push(other),
        }
    }
    *tokens = TokenStream::from_iter(out);
}
<|endoftext|>