<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

/// 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::ops::DerefMut;
use std::collections::BTreeMap;
use std::fmt::Debug;

use libimagstore::store::Entry;
use libimagstore::store::Store;
use libimagstore::storeid::StoreId;
use libimagstore::storeid::IntoStoreId;
use libimagutil::debug_result::*;

use toml_query::read::TomlValueReadExt;
use toml_query::insert::TomlValueInsertExt;

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

use self::iter::*;

use toml::Value;
use url::Url;
use crypto::sha1::Sha1;
use crypto::digest::Digest;

pub trait Link {

    fn get_link_uri_from_filelockentry(&self) -> Result<Option<Url>>;

    fn get_url(&self) -> Result<Option<Url>>;

}

impl Link for Entry {

    fn get_link_uri_from_filelockentry(&self) -> Result<Option<Url>> {
        self.get_header()
            .read("links.external.url")
            .chain_err(|| LEK::EntryHeaderReadError)
            .and_then(|opt| match opt {
                Some(&Value::String(ref s)) => {
                    debug!("Found url, parsing: {:?}", s);
                    Url::parse(&s[..]).chain_err(|| LEK::InvalidUri).map(Some)
                },
                Some(_) => Err(LE::from_kind(LEK::LinkParserFieldTypeError)),
                None    => Ok(None),
            })
    }

    fn get_url(&self) -> Result<Option<Url>> {
        match self.get_header().read("links.external.url") {
            Ok(Some(&Value::String(ref s))) => {
                Url::parse(&s[..])
                     .map(Some)
                     .chain_err(|| LEK::EntryHeaderReadError)
            },
            Ok(None) => Ok(None),
            _ => Err(LE::from_kind(LEK::EntryHeaderReadError))
        }
    }

}

pub trait ExternalLinker : InternalLinker {

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

    /// 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<()>;

}

pub mod iter {
    //! Iterator helpers for external linking stuff
    //!
    //! Contains also helpers to filter iterators for external/internal links
    //!
    //!
    //! # Warning
    //!
    //! This module uses `internal::Link` as link type, so we operate on _store ids_ here.
    //!
    //! Not to confuse with `external::Link` which is a real `FileLockEntry` under the hood.
    //!

    use libimagutil::debug_result::*;
    use libimagstore::store::Store;

    use internal::Link;
    use internal::iter::LinkIter;
    use error::LinkErrorKind as LEK;
    use error::ResultExt;
    use error::Result;

    use url::Url;

    /// Helper for building `OnlyExternalIter` and `NoExternalIter`
    ///
    /// The boolean value defines, how to interpret the `is_external_link_storeid()` return value
    /// (here as "pred"):
    ///
    ///     pred | bool | xor | take?
    ///     ---- | ---- | --- | ----
    ///        0 |    0 |   0 |   1
    ///        0 |    1 |   1 |   0
    ///        1 |    0 |   1 |   0
    ///        1 |    1 |   0 |   1
    ///
    /// If `bool` says "take if return value is false", we take the element if the `pred` returns
    /// false... and so on.
    ///
    /// As we can see, the operator between these two operants is `!(a ^ b)`.
    pub struct ExternalFilterIter(LinkIter, bool);

    impl Iterator for ExternalFilterIter {
        type Item = Link;

        fn next(&mut self) -> Option<Self::Item> {
            use super::is_external_link_storeid;

            while let Some(elem) = self.0.next() {
                if !(self.1 ^ is_external_link_storeid(&elem)) {
                    return Some(elem);
                }
            }
            None
        }
    }

    /// Helper trait to be implemented on `LinkIter` to select or deselect all external links
    ///
    /// # See also
    ///
    /// Also see `OnlyExternalIter` and `NoExternalIter` and the helper traits/functions
    /// `OnlyInteralLinks`/`only_internal_links()` and `OnlyExternalLinks`/`only_external_links()`.
    pub trait SelectExternal {
        fn select_external_links(self, b: bool) -> ExternalFilterIter;
    }

    impl SelectExternal for LinkIter {
        fn select_external_links(self, b: bool) -> ExternalFilterIter {
            ExternalFilterIter(self, b)
        }
    }

    pub struct OnlyExternalIter(ExternalFilterIter);

    impl OnlyExternalIter {
        pub fn new(li: LinkIter) -> OnlyExternalIter {
            OnlyExternalIter(ExternalFilterIter(li, true))
        }

        pub fn urls<'a>(self, store: &'a Store) -> UrlIter<'a> {
            UrlIter(self, store)
        }
    }

    impl Iterator for OnlyExternalIter {
        type Item = Link;

        fn next(&mut self) -> Option<Self::Item> {
            self.0.next()
        }
    }

    pub struct NoExternalIter(ExternalFilterIter);

    impl NoExternalIter {
        pub fn new(li: LinkIter) -> NoExternalIter {
            NoExternalIter(ExternalFilterIter(li, false))
        }
    }

    impl Iterator for NoExternalIter {
        type Item = Link;

        fn next(&mut self) -> Option<Self::Item> {
            self.0.next()
        }
    }

    pub trait OnlyExternalLinks : Sized {
        fn only_external_links(self) -> OnlyExternalIter ;

        fn no_internal_links(self) -> OnlyExternalIter {
            self.only_external_links()
        }
    }

    impl OnlyExternalLinks for LinkIter {
        fn only_external_links(self) -> OnlyExternalIter {
            OnlyExternalIter::new(self)
        }
    }

    pub trait OnlyInternalLinks : Sized {
        fn only_internal_links(self) -> NoExternalIter;

        fn no_external_links(self) -> NoExternalIter {
            self.only_internal_links()
        }
    }

    impl OnlyInternalLinks for LinkIter {
        fn only_internal_links(self) -> NoExternalIter {
            NoExternalIter::new(self)
        }
    }

    pub struct UrlIter<'a>(OnlyExternalIter, &'a Store);

    impl<'a> Iterator for UrlIter<'a> {
        type Item = Result<Url>;

        fn next(&mut self) -> Option<Self::Item> {
            use external::Link;

            loop {
                let next = self.0
                    .next()
                    .map(|id| {
                        debug!("Retrieving entry for id: '{:?}'", id);
                        self.1
                            .retrieve(id.clone())
                            .chain_err(|| LEK::StoreReadError)
                            .map_dbg_err(|_| format!("Retrieving entry for id: '{:?}' failed", id))
                            .and_then(|f| {
                                debug!("Store::retrieve({:?}) succeeded", id);
                                debug!("getting external link from file now");
                                f.get_link_uri_from_filelockentry()
                                    .map_dbg_err(|e| format!("URL -> Err = {:?}", e))
                            })
                    });

                match next {
                    Some(Ok(Some(link))) => return Some(Ok(link)),
                    Some(Ok(None))       => continue,
                    Some(Err(e))         => return Some(Err(e)),
                    None                 => return None
                }
            }
        }

    }

}

/// Check whether the StoreId starts with `/link/external/`
pub fn is_external_link_storeid<A: AsRef<StoreId> + Debug>(id: A) -> bool {
    debug!("Checking whether this is a 'links/external/': '{:?}'", id);
    id.as_ref().local().starts_with("links/external")
}

/// 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<'a>(&self, store: &'a Store) -> Result<UrlIter<'a>> {
        // 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()
            .chain_err(|| LEK::StoreReadError)
            .map(|iter| {
                debug!("Getting external links");
                iter.only_external_links().urls(store)
            })
    }

    /// 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>

        debug!("Iterating {} links = {:?}", links.len(), links);
        for link in links { // for all links
            let hash = {
                let mut s = Sha1::new();
                s.input_str(&link.as_str()[..]);
                s.result_str()
            };
            let file_id = try!(
                ModuleEntryPath::new(format!("external/{}", hash)).into_storeid()
                    .chain_err(|| LEK::StoreWriteError)
                    .map_dbg_err(|_| {
                        format!("Failed to build StoreId for this hash '{:?}'", hash)
                    })
                );

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

            // retrieve the file from the store, which implicitely creates the entry if it does not
            // exist
            let mut file = try!(store
                .retrieve(file_id.clone())
                .chain_err(|| LEK::StoreWriteError)
                .map_dbg_err(|_| {
                    format!("Failed to create or retrieve an file for this link '{:?}'", link)
                }));

            debug!("Generating header content!");
            {
                let hdr = file.deref_mut().get_header_mut();

                let mut table = match hdr.read("links.external.content") {
                    Ok(Some(&Value::Table(ref table))) => table.clone(),
                    Ok(Some(_)) => {
                        warn!("There is a value at 'links.external.content' which is not a table.");
                        warn!("Going to override this value");
                        BTreeMap::new()
                    },
                    Ok(None) => BTreeMap::new(),
                    Err(e)   => return Err(e).chain_err(|| LEK::StoreWriteError),
                };

                let v = Value::String(link.into_string());

                debug!("setting URL = '{:?}", v);
                table.insert(String::from("url"), v);

                if let Err(e) = hdr.insert("links.external.content", Value::Table(table)) {
                    return Err(e).chain_err(|| LEK::StoreWriteError);
                } else {
                    debug!("Setting URL worked");
                }
            }

            // 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(e).chain_err(|| LEK::StoreWriteError);
            }
        }
        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(|links| {
                // TODO: Do not ignore errors here
                let mut links = links.filter_map(Result::ok).collect::<Vec<_>>();
                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(|links| {
                debug!("Removing link = '{:?}'", link);
                let links = links
                    .filter_map(Result::ok)
                    .filter(|l| l.as_str() != link.as_str())
                    .collect::<Vec<_>>();
                self.set_external_links(store, links)
            })
    }

}

#[cfg(test)]
mod tests {
    use super::*;
    use std::path::PathBuf;

    use libimagstore::store::Store;

    fn setup_logging() {
        use env_logger;
        let _ = env_logger::init().unwrap_or(());
    }

    pub fn get_store() -> Store {
        use libimagstore::file_abstraction::InMemoryFileAbstraction;
        let backend = Box::new(InMemoryFileAbstraction::new());
        Store::new_with_backend(PathBuf::from("/"), None, backend).unwrap()
    }

    #[test]
    fn test_simple() {
        setup_logging();
        let store = get_store();
        let mut e = store.retrieve(PathBuf::from("base-test_simple")).unwrap();
        let url   = Url::parse("http://google.de").unwrap();

        assert!(e.add_external_link(&store, url.clone()).is_ok());

        assert_eq!(1, e.get_external_links(&store).unwrap().count());
        assert_eq!(url, e.get_external_links(&store).unwrap().next().unwrap().unwrap());
    }

}

<commit_msg>Add more debugging/tracing output<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

/// 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::ops::DerefMut;
use std::collections::BTreeMap;
use std::fmt::Debug;

use libimagstore::store::Entry;
use libimagstore::store::Store;
use libimagstore::storeid::StoreId;
use libimagstore::storeid::IntoStoreId;
use libimagutil::debug_result::*;

use toml_query::read::TomlValueReadExt;
use toml_query::insert::TomlValueInsertExt;

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

use self::iter::*;

use toml::Value;
use url::Url;
use crypto::sha1::Sha1;
use crypto::digest::Digest;

pub trait Link {

    fn get_link_uri_from_filelockentry(&self) -> Result<Option<Url>>;

    fn get_url(&self) -> Result<Option<Url>>;

}

impl Link for Entry {

    fn get_link_uri_from_filelockentry(&self) -> Result<Option<Url>> {
        self.get_header()
            .read("links.external.url")
            .chain_err(|| LEK::EntryHeaderReadError)
            .and_then(|opt| match opt {
                Some(&Value::String(ref s)) => {
                    debug!("Found url, parsing: {:?}", s);
                    Url::parse(&s[..]).chain_err(|| LEK::InvalidUri).map(Some)
                },
                Some(_) => Err(LE::from_kind(LEK::LinkParserFieldTypeError)),
                None    => Ok(None),
            })
    }

    fn get_url(&self) -> Result<Option<Url>> {
        match self.get_header().read("links.external.url") {
            Ok(Some(&Value::String(ref s))) => {
                Url::parse(&s[..])
                     .map(Some)
                     .chain_err(|| LEK::EntryHeaderReadError)
            },
            Ok(None) => Ok(None),
            _ => Err(LE::from_kind(LEK::EntryHeaderReadError))
        }
    }

}

pub trait ExternalLinker : InternalLinker {

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

    /// 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<()>;

}

pub mod iter {
    //! Iterator helpers for external linking stuff
    //!
    //! Contains also helpers to filter iterators for external/internal links
    //!
    //!
    //! # Warning
    //!
    //! This module uses `internal::Link` as link type, so we operate on _store ids_ here.
    //!
    //! Not to confuse with `external::Link` which is a real `FileLockEntry` under the hood.
    //!

    use libimagutil::debug_result::*;
    use libimagstore::store::Store;

    use internal::Link;
    use internal::iter::LinkIter;
    use error::LinkErrorKind as LEK;
    use error::ResultExt;
    use error::Result;

    use url::Url;

    /// Helper for building `OnlyExternalIter` and `NoExternalIter`
    ///
    /// The boolean value defines, how to interpret the `is_external_link_storeid()` return value
    /// (here as "pred"):
    ///
    ///     pred | bool | xor | take?
    ///     ---- | ---- | --- | ----
    ///        0 |    0 |   0 |   1
    ///        0 |    1 |   1 |   0
    ///        1 |    0 |   1 |   0
    ///        1 |    1 |   0 |   1
    ///
    /// If `bool` says "take if return value is false", we take the element if the `pred` returns
    /// false... and so on.
    ///
    /// As we can see, the operator between these two operants is `!(a ^ b)`.
    pub struct ExternalFilterIter(LinkIter, bool);

    impl Iterator for ExternalFilterIter {
        type Item = Link;

        fn next(&mut self) -> Option<Self::Item> {
            use super::is_external_link_storeid;

            while let Some(elem) = self.0.next() {
                trace!("Check whether is external: {:?}", elem);
                if !(self.1 ^ is_external_link_storeid(&elem)) {
                    trace!("Is external id: {:?}", elem);
                    return Some(elem);
                }
            }
            None
        }
    }

    /// Helper trait to be implemented on `LinkIter` to select or deselect all external links
    ///
    /// # See also
    ///
    /// Also see `OnlyExternalIter` and `NoExternalIter` and the helper traits/functions
    /// `OnlyInteralLinks`/`only_internal_links()` and `OnlyExternalLinks`/`only_external_links()`.
    pub trait SelectExternal {
        fn select_external_links(self, b: bool) -> ExternalFilterIter;
    }

    impl SelectExternal for LinkIter {
        fn select_external_links(self, b: bool) -> ExternalFilterIter {
            ExternalFilterIter(self, b)
        }
    }

    pub struct OnlyExternalIter(ExternalFilterIter);

    impl OnlyExternalIter {
        pub fn new(li: LinkIter) -> OnlyExternalIter {
            OnlyExternalIter(ExternalFilterIter(li, true))
        }

        pub fn urls<'a>(self, store: &'a Store) -> UrlIter<'a> {
            UrlIter(self, store)
        }
    }

    impl Iterator for OnlyExternalIter {
        type Item = Link;

        fn next(&mut self) -> Option<Self::Item> {
            self.0.next()
        }
    }

    pub struct NoExternalIter(ExternalFilterIter);

    impl NoExternalIter {
        pub fn new(li: LinkIter) -> NoExternalIter {
            NoExternalIter(ExternalFilterIter(li, false))
        }
    }

    impl Iterator for NoExternalIter {
        type Item = Link;

        fn next(&mut self) -> Option<Self::Item> {
            self.0.next()
        }
    }

    pub trait OnlyExternalLinks : Sized {
        fn only_external_links(self) -> OnlyExternalIter ;

        fn no_internal_links(self) -> OnlyExternalIter {
            self.only_external_links()
        }
    }

    impl OnlyExternalLinks for LinkIter {
        fn only_external_links(self) -> OnlyExternalIter {
            OnlyExternalIter::new(self)
        }
    }

    pub trait OnlyInternalLinks : Sized {
        fn only_internal_links(self) -> NoExternalIter;

        fn no_external_links(self) -> NoExternalIter {
            self.only_internal_links()
        }
    }

    impl OnlyInternalLinks for LinkIter {
        fn only_internal_links(self) -> NoExternalIter {
            NoExternalIter::new(self)
        }
    }

    pub struct UrlIter<'a>(OnlyExternalIter, &'a Store);

    impl<'a> Iterator for UrlIter<'a> {
        type Item = Result<Url>;

        fn next(&mut self) -> Option<Self::Item> {
            use external::Link;

            loop {
                let next = self.0
                    .next()
                    .map(|id| {
                        debug!("Retrieving entry for id: '{:?}'", id);
                        self.1
                            .retrieve(id.clone())
                            .chain_err(|| LEK::StoreReadError)
                            .map_dbg_err(|_| format!("Retrieving entry for id: '{:?}' failed", id))
                            .and_then(|f| {
                                debug!("Store::retrieve({:?}) succeeded", id);
                                debug!("getting external link from file now");
                                f.get_link_uri_from_filelockentry()
                                    .map_dbg_str("Error happened while getting link URI from FLE")
                                    .map_dbg_err(|e| format!("URL -> Err = {:?}", e))
                            })
                    });

                match next {
                    Some(Ok(Some(link))) => return Some(Ok(link)),
                    Some(Ok(None))       => continue,
                    Some(Err(e))         => return Some(Err(e)),
                    None                 => return None
                }
            }
        }

    }

}

/// Check whether the StoreId starts with `/link/external/`
pub fn is_external_link_storeid<A: AsRef<StoreId> + Debug>(id: A) -> bool {
    debug!("Checking whether this is a 'links/external/': '{:?}'", id);
    id.as_ref().local().starts_with("links/external")
}

/// 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<'a>(&self, store: &'a Store) -> Result<UrlIter<'a>> {
        // 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()
            .chain_err(|| LEK::StoreReadError)
            .map(|iter| {
                debug!("Getting external links");
                iter.only_external_links().urls(store)
            })
    }

    /// 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>

        debug!("Iterating {} links = {:?}", links.len(), links);
        for link in links { // for all links
            let hash = {
                let mut s = Sha1::new();
                s.input_str(&link.as_str()[..]);
                s.result_str()
            };
            let file_id = try!(
                ModuleEntryPath::new(format!("external/{}", hash)).into_storeid()
                    .chain_err(|| LEK::StoreWriteError)
                    .map_dbg_err(|_| {
                        format!("Failed to build StoreId for this hash '{:?}'", hash)
                    })
                );

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

            // retrieve the file from the store, which implicitely creates the entry if it does not
            // exist
            let mut file = try!(store
                .retrieve(file_id.clone())
                .chain_err(|| LEK::StoreWriteError)
                .map_dbg_err(|_| {
                    format!("Failed to create or retrieve an file for this link '{:?}'", link)
                }));

            debug!("Generating header content!");
            {
                let hdr = file.deref_mut().get_header_mut();

                let mut table = match hdr.read("links.external.content") {
                    Ok(Some(&Value::Table(ref table))) => table.clone(),
                    Ok(Some(_)) => {
                        warn!("There is a value at 'links.external.content' which is not a table.");
                        warn!("Going to override this value");
                        BTreeMap::new()
                    },
                    Ok(None) => BTreeMap::new(),
                    Err(e)   => return Err(e).chain_err(|| LEK::StoreWriteError),
                };

                let v = Value::String(link.into_string());

                debug!("setting URL = '{:?}", v);
                table.insert(String::from("url"), v);

                if let Err(e) = hdr.insert("links.external.content", Value::Table(table)) {
                    return Err(e).chain_err(|| LEK::StoreWriteError);
                } else {
                    debug!("Setting URL worked");
                }
            }

            // 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(e).chain_err(|| LEK::StoreWriteError);
            }
        }
        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(|links| {
                // TODO: Do not ignore errors here
                let mut links = links.filter_map(Result::ok).collect::<Vec<_>>();
                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(|links| {
                debug!("Removing link = '{:?}'", link);
                let links = links
                    .filter_map(Result::ok)
                    .filter(|l| l.as_str() != link.as_str())
                    .collect::<Vec<_>>();
                self.set_external_links(store, links)
            })
    }

}

#[cfg(test)]
mod tests {
    use super::*;
    use std::path::PathBuf;

    use libimagstore::store::Store;

    fn setup_logging() {
        use env_logger;
        let _ = env_logger::init().unwrap_or(());
    }

    pub fn get_store() -> Store {
        use libimagstore::file_abstraction::InMemoryFileAbstraction;
        let backend = Box::new(InMemoryFileAbstraction::new());
        Store::new_with_backend(PathBuf::from("/"), None, backend).unwrap()
    }

    #[test]
    fn test_simple() {
        setup_logging();
        let store = get_store();
        let mut e = store.retrieve(PathBuf::from("base-test_simple")).unwrap();
        let url   = Url::parse("http://google.de").unwrap();

        assert!(e.add_external_link(&store, url.clone()).is_ok());

        assert_eq!(1, e.get_external_links(&store).unwrap().count());
        assert_eq!(url, e.get_external_links(&store).unwrap().next().unwrap().unwrap());
    }

}

<|endoftext|>


<commit_before>use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::ops::Range;
use std::rc::Rc;

use core::{Dependency, PackageId, PackageIdSpec, Registry, Summary};
use core::interning::InternedString;
use util::{CargoError, CargoResult};

pub struct RegistryQueryer<'a> {
    pub registry: &'a mut (Registry + 'a),
    replacements: &'a [(PackageIdSpec, Dependency)],
    try_to_use: &'a HashSet<&'a PackageId>,
    // TODO: with nll the Rc can be removed
    cache: HashMap<Dependency, Rc<Vec<Candidate>>>,
    // If set the list of dependency candidates will be sorted by minimal
    // versions first. That allows `cargo update -Z minimal-versions` which will
    // specify minimum depedency versions to be used.
    minimal_versions: bool,
}

impl<'a> RegistryQueryer<'a> {
    pub fn new(
        registry: &'a mut Registry,
        replacements: &'a [(PackageIdSpec, Dependency)],
        try_to_use: &'a HashSet<&'a PackageId>,
        minimal_versions: bool,
    ) -> Self {
        RegistryQueryer {
            registry,
            replacements,
            cache: HashMap::new(),
            try_to_use,
            minimal_versions,
        }
    }

    /// Queries the `registry` to return a list of candidates for `dep`.
    ///
    /// This method is the location where overrides are taken into account. If
    /// any candidates are returned which match an override then the override is
    /// applied by performing a second query for what the override should
    /// return.
    pub fn query(&mut self, dep: &Dependency) -> CargoResult<Rc<Vec<Candidate>>> {
        if let Some(out) = self.cache.get(dep).cloned() {
            return Ok(out);
        }

        let mut ret = Vec::new();
        self.registry.query(dep, &mut |s| {
            ret.push(Candidate {
                summary: s,
                replace: None,
            });
        })?;
        for candidate in ret.iter_mut() {
            let summary = &candidate.summary;

            let mut potential_matches = self.replacements
                .iter()
                .filter(|&&(ref spec, _)| spec.matches(summary.package_id()));

            let &(ref spec, ref dep) = match potential_matches.next() {
                None => continue,
                Some(replacement) => replacement,
            };
            debug!("found an override for {} {}", dep.name(), dep.version_req());

            let mut summaries = self.registry.query_vec(dep)?.into_iter();
            let s = summaries.next().ok_or_else(|| {
                format_err!(
                    "no matching package for override `{}` found\n\
                     location searched: {}\n\
                     version required: {}",
                    spec,
                    dep.source_id(),
                    dep.version_req()
                )
            })?;
            let summaries = summaries.collect::<Vec<_>>();
            if !summaries.is_empty() {
                let bullets = summaries
                    .iter()
                    .map(|s| format!("  * {}", s.package_id()))
                    .collect::<Vec<_>>();
                bail!(
                    "the replacement specification `{}` matched \
                     multiple packages:\n  * {}\n{}",
                    spec,
                    s.package_id(),
                    bullets.join("\n")
                );
            }

            // The dependency should be hard-coded to have the same name and an
            // exact version requirement, so both of these assertions should
            // never fail.
            assert_eq!(s.version(), summary.version());
            assert_eq!(s.name(), summary.name());

            let replace = if s.source_id() == summary.source_id() {
                debug!("Preventing\n{:?}\nfrom replacing\n{:?}", summary, s);
                None
            } else {
                Some(s)
            };
            let matched_spec = spec.clone();

            // Make sure no duplicates
            if let Some(&(ref spec, _)) = potential_matches.next() {
                bail!(
                    "overlapping replacement specifications found:\n\n  \
                     * {}\n  * {}\n\nboth specifications match: {}",
                    matched_spec,
                    spec,
                    summary.package_id()
                );
            }

            for dep in summary.dependencies() {
                debug!("\t{} => {}", dep.name(), dep.version_req());
            }

            candidate.replace = replace;
        }

        // When we attempt versions for a package we'll want to do so in a
        // sorted fashion to pick the "best candidates" first. Currently we try
        // prioritized summaries (those in `try_to_use`) and failing that we
        // list everything from the maximum version to the lowest version.
        ret.sort_unstable_by(|a, b| {
            let a_in_previous = self.try_to_use.contains(a.summary.package_id());
            let b_in_previous = self.try_to_use.contains(b.summary.package_id());
            let previous_cmp = a_in_previous.cmp(&b_in_previous).reverse();
            match previous_cmp {
                Ordering::Equal => {
                    let cmp = a.summary.version().cmp(b.summary.version());
                    if self.minimal_versions {
                        // Lower version ordered first.
                        cmp
                    } else {
                        // Higher version ordered first.
                        cmp.reverse()
                    }
                }
                _ => previous_cmp,
            }
        });

        let out = Rc::new(ret);

        self.cache.insert(dep.clone(), out.clone());

        Ok(out)
    }
}

#[derive(Clone, Copy)]
pub enum Method<'a> {
    Everything, // equivalent to Required { dev_deps: true, all_features: true, .. }
    Required {
        dev_deps: bool,
        features: &'a [InternedString],
        all_features: bool,
        uses_default_features: bool,
    },
}

impl<'r> Method<'r> {
    pub fn split_features(features: &[String]) -> Vec<InternedString> {
        features
            .iter()
            .flat_map(|s| s.split_whitespace())
            .flat_map(|s| s.split(','))
            .filter(|s| !s.is_empty())
            .map(|s| InternedString::new(s))
            .collect::<Vec<InternedString>>()
    }
}

#[derive(Clone)]
pub struct Candidate {
    pub summary: Summary,
    pub replace: Option<Summary>,
}

#[derive(Clone)]
pub struct DepsFrame {
    pub parent: Summary,
    pub just_for_error_messages: bool,
    pub remaining_siblings: RcVecIter<DepInfo>,
}

impl DepsFrame {
    /// Returns the least number of candidates that any of this frame's siblings
    /// has.
    ///
    /// The `remaining_siblings` array is already sorted with the smallest
    /// number of candidates at the front, so we just return the number of
    /// candidates in that entry.
    fn min_candidates(&self) -> usize {
        self.remaining_siblings
            .clone()
            .next()
            .map(|(_, (_, candidates, _))| candidates.len())
            .unwrap_or(0)
    }

    pub fn flatten<'s>(&'s self) -> Box<Iterator<Item = (&PackageId, Dependency)> + 's> {
        // TODO: with impl Trait the Box can be removed
        Box::new(
            self.remaining_siblings
                .clone()
                .map(move |(_, (d, _, _))| (self.parent.package_id(), d)),
        )
    }
}

impl PartialEq for DepsFrame {
    fn eq(&self, other: &DepsFrame) -> bool {
        self.just_for_error_messages == other.just_for_error_messages
            && self.min_candidates() == other.min_candidates()
    }
}

impl Eq for DepsFrame {}

impl PartialOrd for DepsFrame {
    fn partial_cmp(&self, other: &DepsFrame) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for DepsFrame {
    fn cmp(&self, other: &DepsFrame) -> Ordering {
        self.just_for_error_messages
            .cmp(&other.just_for_error_messages)
            .then_with(||
            // the frame with the sibling that has the least number of candidates
            // needs to get bubbled up to the top of the heap we use below, so
            // reverse comparison here.
            self.min_candidates().cmp(&other.min_candidates()).reverse())
    }
}

// Information about the dependencies for a crate, a tuple of:
//
// (dependency info, candidates, features activated)
pub type DepInfo = (Dependency, Rc<Vec<Candidate>>, Rc<Vec<InternedString>>);

pub type ActivateResult<T> = Result<T, ActivateError>;

pub enum ActivateError {
    Fatal(CargoError),
    Conflict(PackageId, ConflictReason),
}

impl From<::failure::Error> for ActivateError {
    fn from(t: ::failure::Error) -> Self {
        ActivateError::Fatal(t)
    }
}

impl From<(PackageId, ConflictReason)> for ActivateError {
    fn from(t: (PackageId, ConflictReason)) -> Self {
        ActivateError::Conflict(t.0, t.1)
    }
}

/// All possible reasons that a package might fail to activate.
///
/// We maintain a list of conflicts for error reporting as well as backtracking
/// purposes. Each reason here is why candidates may be rejected or why we may
/// fail to resolve a dependency.
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub enum ConflictReason {
    /// There was a semver conflict, for example we tried to activate a package
    /// 1.0.2 but 1.1.0 was already activated (aka a compatible semver version
    /// is already activated)
    Semver,

    /// The `links` key is being violated. For example one crate in the
    /// dependency graph has `links = "foo"` but this crate also had that, and
    /// we're only allowed one per dependency graph.
    Links(String),

    /// A dependency listed features that weren't actually available on the
    /// candidate. For example we tried to activate feature `foo` but the
    /// candidiate we're activating didn't actually have the feature `foo`.
    MissingFeatures(String),
}

impl ConflictReason {
    pub fn is_links(&self) -> bool {
        if let ConflictReason::Links(_) = *self {
            return true;
        }
        false
    }

    pub fn is_missing_features(&self) -> bool {
        if let ConflictReason::MissingFeatures(_) = *self {
            return true;
        }
        false
    }
}

pub struct RcVecIter<T> {
    vec: Rc<Vec<T>>,
    rest: Range<usize>,
}

impl<T> RcVecIter<T> {
    pub fn new(vec: Rc<Vec<T>>) -> RcVecIter<T> {
        RcVecIter {
            rest: 0..vec.len(),
            vec,
        }
    }
}

// Not derived to avoid `T: Clone`
impl<T> Clone for RcVecIter<T> {
    fn clone(&self) -> RcVecIter<T> {
        RcVecIter {
            vec: self.vec.clone(),
            rest: self.rest.clone(),
        }
    }
}

impl<T> Iterator for RcVecIter<T>
where
    T: Clone,
{
    type Item = (usize, T);

    fn next(&mut self) -> Option<(usize, T)> {
        self.rest
            .next()
            .and_then(|i| self.vec.get(i).map(|val| (i, val.clone())))
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        // rest is a std::ops::Range, which is an ExactSizeIterator.
        self.rest.size_hint()
    }
}

impl<T: Clone> ExactSizeIterator for RcVecIter<T> {}

pub struct RcList<T> {
    pub head: Option<Rc<(T, RcList<T>)>>,
}

impl<T> RcList<T> {
    pub fn new() -> RcList<T> {
        RcList { head: None }
    }

    pub fn push(&mut self, data: T) {
        let node = Rc::new((
            data,
            RcList {
                head: self.head.take(),
            },
        ));
        self.head = Some(node);
    }
}

// Not derived to avoid `T: Clone`
impl<T> Clone for RcList<T> {
    fn clone(&self) -> RcList<T> {
        RcList {
            head: self.head.clone(),
        }
    }
}

// Avoid stack overflows on drop by turning recursion into a loop
impl<T> Drop for RcList<T> {
    fn drop(&mut self) {
        let mut cur = self.head.take();
        while let Some(head) = cur {
            match Rc::try_unwrap(head) {
                Ok((_data, mut next)) => cur = next.head.take(),
                Err(_) => break,
            }
        }
    }
}

pub enum GraphNode {
    Add(PackageId),
    Link(PackageId, PackageId, Dependency),
}
<commit_msg>use impl Trait<commit_after>use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::ops::Range;
use std::rc::Rc;

use core::{Dependency, PackageId, PackageIdSpec, Registry, Summary};
use core::interning::InternedString;
use util::{CargoError, CargoResult};

pub struct RegistryQueryer<'a> {
    pub registry: &'a mut (Registry + 'a),
    replacements: &'a [(PackageIdSpec, Dependency)],
    try_to_use: &'a HashSet<&'a PackageId>,
    // TODO: with nll the Rc can be removed
    cache: HashMap<Dependency, Rc<Vec<Candidate>>>,
    // If set the list of dependency candidates will be sorted by minimal
    // versions first. That allows `cargo update -Z minimal-versions` which will
    // specify minimum depedency versions to be used.
    minimal_versions: bool,
}

impl<'a> RegistryQueryer<'a> {
    pub fn new(
        registry: &'a mut Registry,
        replacements: &'a [(PackageIdSpec, Dependency)],
        try_to_use: &'a HashSet<&'a PackageId>,
        minimal_versions: bool,
    ) -> Self {
        RegistryQueryer {
            registry,
            replacements,
            cache: HashMap::new(),
            try_to_use,
            minimal_versions,
        }
    }

    /// Queries the `registry` to return a list of candidates for `dep`.
    ///
    /// This method is the location where overrides are taken into account. If
    /// any candidates are returned which match an override then the override is
    /// applied by performing a second query for what the override should
    /// return.
    pub fn query(&mut self, dep: &Dependency) -> CargoResult<Rc<Vec<Candidate>>> {
        if let Some(out) = self.cache.get(dep).cloned() {
            return Ok(out);
        }

        let mut ret = Vec::new();
        self.registry.query(dep, &mut |s| {
            ret.push(Candidate {
                summary: s,
                replace: None,
            });
        })?;
        for candidate in ret.iter_mut() {
            let summary = &candidate.summary;

            let mut potential_matches = self.replacements
                .iter()
                .filter(|&&(ref spec, _)| spec.matches(summary.package_id()));

            let &(ref spec, ref dep) = match potential_matches.next() {
                None => continue,
                Some(replacement) => replacement,
            };
            debug!("found an override for {} {}", dep.name(), dep.version_req());

            let mut summaries = self.registry.query_vec(dep)?.into_iter();
            let s = summaries.next().ok_or_else(|| {
                format_err!(
                    "no matching package for override `{}` found\n\
                     location searched: {}\n\
                     version required: {}",
                    spec,
                    dep.source_id(),
                    dep.version_req()
                )
            })?;
            let summaries = summaries.collect::<Vec<_>>();
            if !summaries.is_empty() {
                let bullets = summaries
                    .iter()
                    .map(|s| format!("  * {}", s.package_id()))
                    .collect::<Vec<_>>();
                bail!(
                    "the replacement specification `{}` matched \
                     multiple packages:\n  * {}\n{}",
                    spec,
                    s.package_id(),
                    bullets.join("\n")
                );
            }

            // The dependency should be hard-coded to have the same name and an
            // exact version requirement, so both of these assertions should
            // never fail.
            assert_eq!(s.version(), summary.version());
            assert_eq!(s.name(), summary.name());

            let replace = if s.source_id() == summary.source_id() {
                debug!("Preventing\n{:?}\nfrom replacing\n{:?}", summary, s);
                None
            } else {
                Some(s)
            };
            let matched_spec = spec.clone();

            // Make sure no duplicates
            if let Some(&(ref spec, _)) = potential_matches.next() {
                bail!(
                    "overlapping replacement specifications found:\n\n  \
                     * {}\n  * {}\n\nboth specifications match: {}",
                    matched_spec,
                    spec,
                    summary.package_id()
                );
            }

            for dep in summary.dependencies() {
                debug!("\t{} => {}", dep.name(), dep.version_req());
            }

            candidate.replace = replace;
        }

        // When we attempt versions for a package we'll want to do so in a
        // sorted fashion to pick the "best candidates" first. Currently we try
        // prioritized summaries (those in `try_to_use`) and failing that we
        // list everything from the maximum version to the lowest version.
        ret.sort_unstable_by(|a, b| {
            let a_in_previous = self.try_to_use.contains(a.summary.package_id());
            let b_in_previous = self.try_to_use.contains(b.summary.package_id());
            let previous_cmp = a_in_previous.cmp(&b_in_previous).reverse();
            match previous_cmp {
                Ordering::Equal => {
                    let cmp = a.summary.version().cmp(b.summary.version());
                    if self.minimal_versions {
                        // Lower version ordered first.
                        cmp
                    } else {
                        // Higher version ordered first.
                        cmp.reverse()
                    }
                }
                _ => previous_cmp,
            }
        });

        let out = Rc::new(ret);

        self.cache.insert(dep.clone(), out.clone());

        Ok(out)
    }
}

#[derive(Clone, Copy)]
pub enum Method<'a> {
    Everything, // equivalent to Required { dev_deps: true, all_features: true, .. }
    Required {
        dev_deps: bool,
        features: &'a [InternedString],
        all_features: bool,
        uses_default_features: bool,
    },
}

impl<'r> Method<'r> {
    pub fn split_features(features: &[String]) -> Vec<InternedString> {
        features
            .iter()
            .flat_map(|s| s.split_whitespace())
            .flat_map(|s| s.split(','))
            .filter(|s| !s.is_empty())
            .map(|s| InternedString::new(s))
            .collect::<Vec<InternedString>>()
    }
}

#[derive(Clone)]
pub struct Candidate {
    pub summary: Summary,
    pub replace: Option<Summary>,
}

#[derive(Clone)]
pub struct DepsFrame {
    pub parent: Summary,
    pub just_for_error_messages: bool,
    pub remaining_siblings: RcVecIter<DepInfo>,
}

impl DepsFrame {
    /// Returns the least number of candidates that any of this frame's siblings
    /// has.
    ///
    /// The `remaining_siblings` array is already sorted with the smallest
    /// number of candidates at the front, so we just return the number of
    /// candidates in that entry.
    fn min_candidates(&self) -> usize {
        self.remaining_siblings
            .clone()
            .next()
            .map(|(_, (_, candidates, _))| candidates.len())
            .unwrap_or(0)
    }

    pub fn flatten<'s>(&'s self) -> impl Iterator<Item=(&PackageId, Dependency)> + 's {
        self.remaining_siblings
            .clone()
            .map(move |(_, (d, _, _))| (self.parent.package_id(), d))
    }
}

impl PartialEq for DepsFrame {
    fn eq(&self, other: &DepsFrame) -> bool {
        self.just_for_error_messages == other.just_for_error_messages
            && self.min_candidates() == other.min_candidates()
    }
}

impl Eq for DepsFrame {}

impl PartialOrd for DepsFrame {
    fn partial_cmp(&self, other: &DepsFrame) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for DepsFrame {
    fn cmp(&self, other: &DepsFrame) -> Ordering {
        self.just_for_error_messages
            .cmp(&other.just_for_error_messages)
            .then_with(||
            // the frame with the sibling that has the least number of candidates
            // needs to get bubbled up to the top of the heap we use below, so
            // reverse comparison here.
            self.min_candidates().cmp(&other.min_candidates()).reverse())
    }
}

// Information about the dependencies for a crate, a tuple of:
//
// (dependency info, candidates, features activated)
pub type DepInfo = (Dependency, Rc<Vec<Candidate>>, Rc<Vec<InternedString>>);

pub type ActivateResult<T> = Result<T, ActivateError>;

pub enum ActivateError {
    Fatal(CargoError),
    Conflict(PackageId, ConflictReason),
}

impl From<::failure::Error> for ActivateError {
    fn from(t: ::failure::Error) -> Self {
        ActivateError::Fatal(t)
    }
}

impl From<(PackageId, ConflictReason)> for ActivateError {
    fn from(t: (PackageId, ConflictReason)) -> Self {
        ActivateError::Conflict(t.0, t.1)
    }
}

/// All possible reasons that a package might fail to activate.
///
/// We maintain a list of conflicts for error reporting as well as backtracking
/// purposes. Each reason here is why candidates may be rejected or why we may
/// fail to resolve a dependency.
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub enum ConflictReason {
    /// There was a semver conflict, for example we tried to activate a package
    /// 1.0.2 but 1.1.0 was already activated (aka a compatible semver version
    /// is already activated)
    Semver,

    /// The `links` key is being violated. For example one crate in the
    /// dependency graph has `links = "foo"` but this crate also had that, and
    /// we're only allowed one per dependency graph.
    Links(String),

    /// A dependency listed features that weren't actually available on the
    /// candidate. For example we tried to activate feature `foo` but the
    /// candidiate we're activating didn't actually have the feature `foo`.
    MissingFeatures(String),
}

impl ConflictReason {
    pub fn is_links(&self) -> bool {
        if let ConflictReason::Links(_) = *self {
            return true;
        }
        false
    }

    pub fn is_missing_features(&self) -> bool {
        if let ConflictReason::MissingFeatures(_) = *self {
            return true;
        }
        false
    }
}

pub struct RcVecIter<T> {
    vec: Rc<Vec<T>>,
    rest: Range<usize>,
}

impl<T> RcVecIter<T> {
    pub fn new(vec: Rc<Vec<T>>) -> RcVecIter<T> {
        RcVecIter {
            rest: 0..vec.len(),
            vec,
        }
    }
}

// Not derived to avoid `T: Clone`
impl<T> Clone for RcVecIter<T> {
    fn clone(&self) -> RcVecIter<T> {
        RcVecIter {
            vec: self.vec.clone(),
            rest: self.rest.clone(),
        }
    }
}

impl<T> Iterator for RcVecIter<T>
where
    T: Clone,
{
    type Item = (usize, T);

    fn next(&mut self) -> Option<(usize, T)> {
        self.rest
            .next()
            .and_then(|i| self.vec.get(i).map(|val| (i, val.clone())))
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        // rest is a std::ops::Range, which is an ExactSizeIterator.
        self.rest.size_hint()
    }
}

impl<T: Clone> ExactSizeIterator for RcVecIter<T> {}

pub struct RcList<T> {
    pub head: Option<Rc<(T, RcList<T>)>>,
}

impl<T> RcList<T> {
    pub fn new() -> RcList<T> {
        RcList { head: None }
    }

    pub fn push(&mut self, data: T) {
        let node = Rc::new((
            data,
            RcList {
                head: self.head.take(),
            },
        ));
        self.head = Some(node);
    }
}

// Not derived to avoid `T: Clone`
impl<T> Clone for RcList<T> {
    fn clone(&self) -> RcList<T> {
        RcList {
            head: self.head.clone(),
        }
    }
}

// Avoid stack overflows on drop by turning recursion into a loop
impl<T> Drop for RcList<T> {
    fn drop(&mut self) {
        let mut cur = self.head.take();
        while let Some(head) = cur {
            match Rc::try_unwrap(head) {
                Ok((_data, mut next)) => cur = next.head.take(),
                Err(_) => break,
            }
        }
    }
}

pub enum GraphNode {
    Add(PackageId),
    Link(PackageId, PackageId, Dependency),
}
<|endoftext|>


<commit_before>use mio::*;
use std::cell::RefCell;
use std::io;
use std::rc::Rc;
use std::time::Duration;
use slab::Slab;

pub trait EventHandler {
    fn on_ready(&mut self, selector: &mut Selector, event: Event);
}

impl<F> EventHandler for F where F: FnMut(&mut Selector, Event) {
    fn on_ready(&mut self, selector: &mut Selector, event: Event) {
        self(selector, event);
    }
}

// for convenience
impl<T: EventHandler> EventHandler for Rc<RefCell<T>> {
    fn on_ready(&mut self, selector: &mut Selector, event: Event) {
        self.borrow_mut().on_ready(selector, event);
    }
}

pub struct Selector {
    poll: Poll,
    handlers: Slab<SelectionHandler, Token>,
}

struct SelectionHandler {
    handler: Rc<RefCell<Box<EventHandler>>>,
    registered: bool,
}

impl SelectionHandler {
    fn new(handler: Box<EventHandler>) -> Self {
        Self {
            handler: Rc::new(RefCell::new(handler)),
            registered: true,
        }
    }
}

impl Selector {
    pub fn new() -> io::Result<Self> {
        Ok(Self {
            poll: Poll::new()?,
            handlers: Slab::with_capacity(1024),
        })
    }

    pub fn register<E>(&mut self, handle: &E, handler: Box<EventHandler>,
                   interest: Ready, opts: PollOpt) -> io::Result<Token>
            where E: Evented + ?Sized {
        let token = self.handlers.insert(SelectionHandler::new(handler))
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, "Cannot allocate slab slot"))?;
        self.poll.register(handle, token, interest, opts)?;
        Ok(token)
    }

    pub fn reregister<E>(&mut self, handle: &E, token: Token,
                   interest: Ready, opts: PollOpt) -> io::Result<()>
            where E: Evented + ?Sized {
        // a Poll does not accept to register an empty Ready
        // for simplifying its usage, expose an API that does
        let selection_handler = self.handlers.get_mut(token).expect("Token not found");
        if interest.is_empty() {
            if selection_handler.registered {
                selection_handler.registered = false;
                self.poll.deregister(handle)?;
            }
            Ok(())
        } else {
            if !selection_handler.registered {
                selection_handler.registered = true;
                self.poll.register(handle, token, interest, opts)?;
            }
            self.poll.reregister(handle, token, interest, opts)
        }
    }

    pub fn deregister<E>(&mut self, handle: &E, token: Token) -> io::Result<()>
            where E: Evented + ?Sized {
        let selection_handler = self.handlers.remove(token).expect("Unknown token removed");
        if selection_handler.registered {
            self.poll.deregister(handle)?;
        }
        Ok(())
    }

    pub fn poll(&mut self, events: &mut Events, timeout: Option<Duration>) -> io::Result<usize> {
        self.poll.poll(events, timeout)
    }

    pub fn run_handler(&mut self, event: Event) {
        let handler = self.handlers.get_mut(event.token()).expect("Token not found").handler.clone();
        handler.borrow_mut().on_ready(self, event);
    }
}
<commit_msg>Do not reregister unnecessarily<commit_after>use mio::*;
use std::cell::RefCell;
use std::io;
use std::rc::Rc;
use std::time::Duration;
use slab::Slab;

pub trait EventHandler {
    fn on_ready(&mut self, selector: &mut Selector, event: Event);
}

impl<F> EventHandler for F where F: FnMut(&mut Selector, Event) {
    fn on_ready(&mut self, selector: &mut Selector, event: Event) {
        self(selector, event);
    }
}

// for convenience
impl<T: EventHandler> EventHandler for Rc<RefCell<T>> {
    fn on_ready(&mut self, selector: &mut Selector, event: Event) {
        self.borrow_mut().on_ready(selector, event);
    }
}

pub struct Selector {
    poll: Poll,
    handlers: Slab<SelectionHandler, Token>,
}

struct SelectionHandler {
    handler: Rc<RefCell<Box<EventHandler>>>,
    registered: bool,
}

impl SelectionHandler {
    fn new(handler: Box<EventHandler>) -> Self {
        Self {
            handler: Rc::new(RefCell::new(handler)),
            registered: true,
        }
    }
}

impl Selector {
    pub fn new() -> io::Result<Self> {
        Ok(Self {
            poll: Poll::new()?,
            handlers: Slab::with_capacity(1024),
        })
    }

    pub fn register<E>(&mut self, handle: &E, handler: Box<EventHandler>,
                   interest: Ready, opts: PollOpt) -> io::Result<Token>
            where E: Evented + ?Sized {
        let token = self.handlers.insert(SelectionHandler::new(handler))
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, "Cannot allocate slab slot"))?;
        self.poll.register(handle, token, interest, opts)?;
        Ok(token)
    }

    pub fn reregister<E>(&mut self, handle: &E, token: Token,
                   interest: Ready, opts: PollOpt) -> io::Result<()>
            where E: Evented + ?Sized {
        // a Poll does not accept to register an empty Ready
        // for simplifying its usage, expose an API that does
        let selection_handler = self.handlers.get_mut(token).expect("Token not found");
        if interest.is_empty() {
            if selection_handler.registered {
                selection_handler.registered = false;
                self.poll.deregister(handle)?;
            }
            Ok(())
        } else {
            if !selection_handler.registered {
                selection_handler.registered = true;
                self.poll.register(handle, token, interest, opts)
            } else {
                self.poll.reregister(handle, token, interest, opts)
            }
        }
    }

    pub fn deregister<E>(&mut self, handle: &E, token: Token) -> io::Result<()>
            where E: Evented + ?Sized {
        let selection_handler = self.handlers.remove(token).expect("Unknown token removed");
        if selection_handler.registered {
            self.poll.deregister(handle)?;
        }
        Ok(())
    }

    pub fn poll(&mut self, events: &mut Events, timeout: Option<Duration>) -> io::Result<usize> {
        self.poll.poll(events, timeout)
    }

    pub fn run_handler(&mut self, event: Event) {
        let handler = self.handlers.get_mut(event.token()).expect("Token not found").handler.clone();
        handler.borrow_mut().on_ready(self, event);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::path::Path;

#[derive(Debug, Default)]
pub struct LanguageSpec {
    pub reserved_words: HashMap<String, String>,
    pub hash_comment: bool,
    pub c_style_comments: bool,
    pub backtick_strings: bool,
    pub regexp_literals: bool,
    pub triple_quote_literals: bool,
    pub c_preprocessor: bool,
    // Rust is mostly C-like, with a couple of differences.
    pub rust_tweaks: bool,
    pub cxx14_digit_separators: bool,
}

fn make_reserved(v: &[&str]) -> HashMap<String, String> {
    let mut reserved_words = HashMap::new();
    for word in v {
        reserved_words.insert(word.to_string(), "class=\"syn_reserved\" ".to_string());
    }
    reserved_words
}

static RESERVED_WORDS_JS: &'static [&'static str] = &[
    "abstract",
    "else",
    "instanceof",
    "super",
    "boolean",
    "enum",
    "int",
    "switch",
    "break",
    "export",
    "interface",
    "synchronized",
    "byte",
    "extends",
    "let",
    "this",
    "case",
    "false",
    "long",
    "throw",
    "catch",
    "final",
    "native",
    "throws",
    "char",
    "finally",
    "new",
    "transient",
    "class",
    "float",
    "null",
    "true",
    "const",
    "for",
    "package",
    "try",
    "continue",
    "function",
    "private",
    "typeof",
    "debugger",
    "goto",
    "protected",
    "var",
    "default",
    "if",
    "public",
    "void",
    "delete",
    "implements",
    "return",
    "volatile",
    "do",
    "import",
    "short",
    "while",
    "double",
    "in",
    "static",
    "with",
    "get",
    "set",
];

static RESERVED_WORDS_CPP: &'static [&'static str] = &[
    "alignas",
    "alignof",
    "and",
    "and_eq",
    "asm",
    "atomic_cancel",
    "atomic_commit",
    "atomic_noexcept",
    "auto",
    "bitand",
    "bitor",
    "bool",
    "break",
    "case",
    "catch",
    "char",
    "char16_t",
    "char32_t",
    "class",
    "compl",
    "concept",
    "const",
    "constexpr",
    "const_cast",
    "continue",
    "decltype",
    "default",
    "delete",
    "do",
    "double",
    "dynamic_cast",
    "else",
    "enum",
    "explicit",
    "export",
    "extern",
    "false",
    "float",
    "for",
    "friend",
    "goto",
    "if",
    "inline",
    "int",
    "import",
    "long",
    "module",
    "mutable",
    "namespace",
    "new",
    "noexcept",
    "not",
    "not_eq",
    "nullptr",
    // Don't mark "operator" as a keyword so that people can click
    // on it.
    //"operator",
    "or",
    "or_eq",
    "private",
    "protected",
    "public",
    "register",
    "reinterpret_cast",
    "requires",
    "return",
    "short",
    "signed",
    "sizeof",
    "static",
    "static_assert",
    "static_cast",
    "struct",
    "switch",
    "synchronized",
    "template",
    "this",
    "thread_local",
    "throw",
    "true",
    "try",
    "typedef",
    "typeid",
    "typename",
    "union",
    "unsigned",
    "using",
    "virtual",
    "void",
    "volatile",
    "wchar_t",
    "while",
    "xor",
    "xor_eq",
    "#if",
    "#ifdef",
    "#ifndef",
    "#else",
    "#elif",
    "#endif",
    "#define",
    "#undef",
    "#include",
    "#error",
    "defined",
];

// From 'reserved' in ipc/ipdl/ipdl/parser.py
static RESERVED_WORDS_IPDL: &'static [&'static str] = &[
    "async",
    "both",
    "child",
    "class",
    "compress",
    "compressall",
    "from",
    "include",
    "intr",
    "manager",
    "manages",
    "namespace",
    "nested",
    "nullable",
    "or",
    "parent",
    "prio",
    "protocol",
    "refcounted",
    "moveonly",
    "returns",
    "struct",
    "sync",
    "union",
    "UniquePtr",
    "upto",
    "using",
    "verify",
];

static RESERVED_WORDS_IDL: &'static [&'static str] = &[
    "cenum",
    "const",
    "interface",
    "in",
    "inout",
    "out",
    "attribute",
    "raises",
    "readonly",
    "native",
    "typedef",
    "webidl",
    "array",
    "shared",
    "iid_is",
    "size_is",
    "retval",
    "boolean",
    "void",
    "octet",
    "short",
    "long",
    "unsigned",
    "float",
    "double",
    "char",
    "string",
    "wchar",
    "wstring",
    "nsid",
    "AUTF8String",
    "ACString",
    "AString",
    "jsval",
    "Promise",
    "ptr",
    "ref",
    "uuid",
    "scriptable",
    "builtinclass",
    "function",
    "noscript",
    "deprecated",
    "object",
    "main_process_scriptable_only",
    "binaryname",
    "notxpcom",
    "symbol",
    "implicit_jscontext",
    "nostdcall",
    "must_use",
    "infallible",
    "can_run_script",
];

static RESERVED_WORDS_WEBIDL: &'static [&'static str] = &[
    "module",
    "interface",
    "partial",
    "dictionary",
    "exception",
    "enum",
    "callback",
    "typedef",
    "implements",
    "const",
    "null",
    "true",
    "false",
    "serializer",
    "stringifier",
    "jsonifier",
    "unrestricted",
    "attribute",
    "readonly",
    "inherit",
    "static",
    "getter",
    "setter",
    "creator",
    "deleter",
    "legacycaller",
    "optional",
    "Date",
    "DOMString",
    "ByteString",
    "USVString",
    "any",
    "boolean",
    "byte",
    "double",
    "float",
    "long",
    "object",
    "octet",
    "Promise",
    "required",
    "sequence",
    "MozMap",
    "short",
    "unsigned",
    "void",
    "ArrayBuffer",
    "SharedArrayBuffer",
    "or",
    "maplike",
    "setlike",
    "iterable",
    "Exposed",
    "ChromeOnly",
    "ChromeConstructor",
    "Pref",
    "Func",
    "AvailableIn",
    "CheckAnyPermissions",
    "CheckAllPermissions",
    "JSImplementation",
    "HeaderFile",
    "NavigatorProperty",
    "AvailableIn",
    "Func",
    "CheckAnyPermissions",
    "CheckAllPermissions",
    "Deprecated",
    "NeedResolve",
    "OverrideBuiltins",
    "ChromeOnly",
    "Unforgeable",
    "UnsafeInPrerendering",
    "LegacyEventInit",
    "ProbablyShortLivingObject",
    "ArrayClass",
    "Clamp",
    "Constructor",
    "EnforceRange",
    "ExceptionClass",
    "Exposed",
    "ImplicitThis",
    "Global",
    "PrimaryGlobal",
    "LegacyArrayClass",
    "LegacyUnenumerableNamedProperties",
    "LenientSetter",
    "LenientThis",
    "NamedConstructor",
    "NewObject",
    "NoInterfaceObject",
    "OverrideBuiltins",
    "PutForwards",
    "Replaceable",
    "SameObject",
    "SecureContext",
    "Throws",
    "TreatNonObjectAsNull",
    "TreatNullAs",
    "Unforgeable",
    "Unscopable",
];

static RESERVED_WORDS_PYTHON: &'static [&'static str] = &[
    "and", "del", "from", "not", "while", "as", "elif", "global", "or", "with", "assert", "else",
    "if", "pass", "yield", "break", "except", "import", "print", "class", "exec", "in", "raise",
    "continue", "finally", "is", "return", "def", "for", "lambda", "try",
];

// List of Rust reserved words pulled from
// https://github.com/rust-lang/rust/blob/master/src/libsyntax/symbol.rs
static RESERVED_WORDS_RUST: &'static [&'static str] = &[
    "as", "box", "break", "const", "continue", "crate", "else", "enum", "extern", "false", "fn",
    "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub", "ref",
    "return", "self", "Self", "static", "struct", "super", "trait", "true", "type", "unsafe",
    "use", "where", "while", "abstract", "alignof", "become", "do", "final", "macro", "offsetof",
    "override", "priv", "proc", "pure", "sizeof", "typeof", "unsized", "virtual", "yield",
    "default", "union",
];

static RESERVED_WORDS_JAVA: &'static [&'static str] = &[
    "abstract",
    "continue",
    "for",
    "new",
    "switch",
    "assert",
    "default",
    "goto",
    "package",
    "synchronized",
    "boolean",
    "do",
    "if",
    "private",
    "this",
    "break",
    "double",
    "implements",
    "protected",
    "throw",
    "byte",
    "else",
    "import",
    "public",
    "throws",
    "case",
    "enum",
    "instanceof",
    "return",
    "transient",
    "catch",
    "extends",
    "int",
    "short",
    "try",
    "char",
    "final",
    "interface",
    "static",
    "void",
    "class",
    "finally",
    "long",
    "strictfp",
    "volatile",
    "const",
    "float",
    "native",
    "super",
    "while",
    "null",
    "true",
    "false",
];

// http://kotlinlang.org/docs/reference/keyword-reference.html
static RESERVED_WORDS_KOTLIN: &'static [&'static str] = &[
    "as",
    "as?",
    "break",
    "class",
    "continue",
    "do",
    "else",
    "false",
    "for",
    "fun",
    "if",
    "in",
    "!in",
    "interface",
    "is",
    "!is",
    "null",
    "object",
    "package",
    "return",
    "super",
    "this",
    "throw",
    "true",
    "try",
    "typealias",
    "val",
    "var",
    "when",
    "while",
    "by",
    "catch",
    "constructor",
    "delegate",
    "dynamic",
    "field",
    "file",
    "finally",
    "get",
    "import",
    "init",
    "param",
    "property",
    "receiver",
    "set",
    "setparam",
    "where",
    "actual",
    "abstract",
    "annotation",
    "companion",
    "const",
    "crossinline",
    "data",
    "enum",
    "expect",
    "external",
    "final",
    "infix",
    "inline",
    "inner",
    "internal",
    "lateinit",
    "noinline",
    "open",
    "operator",
    "out",
    "override",
    "private",
    "protected",
    "public",
    "reified",
    "sealed",
    "suspend",
    "tailrec",
    "vararg",
    "field",
    "it",
];

lazy_static! {
    static ref JS_SPEC : LanguageSpec = LanguageSpec {
        reserved_words: make_reserved(&*RESERVED_WORDS_JS),
        c_style_comments: true,
        backtick_strings: true,
        regexp_literals: true,
        .. LanguageSpec::default()
    };

    static ref CPP_SPEC : LanguageSpec = LanguageSpec {
        reserved_words: make_reserved(&*RESERVED_WORDS_CPP),
        c_style_comments: true,
        c_preprocessor: true,
        cxx14_digit_separators: true,
        .. LanguageSpec::default()
    };

    static ref IPDL_SPEC : LanguageSpec = LanguageSpec {
        reserved_words: make_reserved(&*RESERVED_WORDS_IPDL),
        c_style_comments: true,
        .. LanguageSpec::default()
    };

    static ref IDL_SPEC : LanguageSpec = LanguageSpec {
        reserved_words: make_reserved(&*RESERVED_WORDS_IDL),
        c_style_comments: true,
        .. LanguageSpec::default()
    };

    static ref WEBIDL_SPEC : LanguageSpec = LanguageSpec {
        reserved_words: make_reserved(&*RESERVED_WORDS_WEBIDL),
        c_style_comments: true,
        .. LanguageSpec::default()
    };

    static ref PYTHON_SPEC : LanguageSpec = LanguageSpec {
        reserved_words: make_reserved(&*RESERVED_WORDS_PYTHON),
        hash_comment: true,
        triple_quote_literals: true,
        .. LanguageSpec::default()
    };

    static ref RUST_SPEC : LanguageSpec = LanguageSpec {
        reserved_words: make_reserved(&*RESERVED_WORDS_RUST),
        hash_comment: true, // for now, for attributes
        c_style_comments: true,
        rust_tweaks: true,
        .. LanguageSpec::default()
    };

    static ref JAVA_SPEC : LanguageSpec = LanguageSpec {
        reserved_words: make_reserved(&*RESERVED_WORDS_JAVA),
        c_style_comments: true,
        .. LanguageSpec::default()
    };

    static ref KOTLIN_SPEC : LanguageSpec = LanguageSpec {
        reserved_words: make_reserved(&*RESERVED_WORDS_KOTLIN),
        c_style_comments: true,
        .. LanguageSpec::default()
    };
}

#[derive(Debug)]
pub enum FormatAs {
    FormatCLike(&'static LanguageSpec),
    FormatTagLike(&'static LanguageSpec),
    Plain,
    Binary,
}

pub fn select_formatting(filename: &str) -> FormatAs {
    let ext = match Path::new(filename).extension() {
        Some(ext) => ext.to_str().unwrap(),
        None => "",
    };
    match ext {
        "c" | "cc" | "cpp" | "cxx" | "h" | "hh" | "hxx" | "hpp" | "mm" => {
            FormatAs::FormatCLike(&*CPP_SPEC)
        }
        "ipdl" | "ipdlh" => FormatAs::FormatCLike(&*IPDL_SPEC),
        "idl" => FormatAs::FormatCLike(&*IDL_SPEC),
        "webidl" => FormatAs::FormatCLike(&*WEBIDL_SPEC),
        "js" | "jsm" | "json" | "sjs" => FormatAs::FormatCLike(&*JS_SPEC),
        "py" | "build" | "configure" => FormatAs::FormatCLike(&*PYTHON_SPEC),
        "rs" => FormatAs::FormatCLike(&*RUST_SPEC),
        "java" => FormatAs::FormatCLike(&*JAVA_SPEC),
        "kt" => FormatAs::FormatCLike(&*KOTLIN_SPEC),

        "html" | "htm" | "xhtml" | "xht" | "xml" | "xul" => FormatAs::FormatTagLike(&*JS_SPEC),

        // Keep this list in sync with the binary types list in nginx-setup.py
        "ogg" | "ttf" | "xpi" | "png" | "bcmap" | "gif" | "ogv" | "jpg" | "jpeg" | "bmp"
        | "icns" | "ico" | "mp4" | "sqlite" | "jar" | "webm" | "webp" | "woff" | "class"
        | "m4s" | "mgif" | "wav" | "opus" | "mp3" | "otf" => FormatAs::Binary,

        _ => FormatAs::Plain,
    }
}
<commit_msg>Bug 1672004 - Format mjs files as JS<commit_after>use std::collections::HashMap;
use std::path::Path;

#[derive(Debug, Default)]
pub struct LanguageSpec {
    pub reserved_words: HashMap<String, String>,
    pub hash_comment: bool,
    pub c_style_comments: bool,
    pub backtick_strings: bool,
    pub regexp_literals: bool,
    pub triple_quote_literals: bool,
    pub c_preprocessor: bool,
    // Rust is mostly C-like, with a couple of differences.
    pub rust_tweaks: bool,
    pub cxx14_digit_separators: bool,
}

fn make_reserved(v: &[&str]) -> HashMap<String, String> {
    let mut reserved_words = HashMap::new();
    for word in v {
        reserved_words.insert(word.to_string(), "class=\"syn_reserved\" ".to_string());
    }
    reserved_words
}

static RESERVED_WORDS_JS: &'static [&'static str] = &[
    "abstract",
    "else",
    "instanceof",
    "super",
    "boolean",
    "enum",
    "int",
    "switch",
    "break",
    "export",
    "interface",
    "synchronized",
    "byte",
    "extends",
    "let",
    "this",
    "case",
    "false",
    "long",
    "throw",
    "catch",
    "final",
    "native",
    "throws",
    "char",
    "finally",
    "new",
    "transient",
    "class",
    "float",
    "null",
    "true",
    "const",
    "for",
    "package",
    "try",
    "continue",
    "function",
    "private",
    "typeof",
    "debugger",
    "goto",
    "protected",
    "var",
    "default",
    "if",
    "public",
    "void",
    "delete",
    "implements",
    "return",
    "volatile",
    "do",
    "import",
    "short",
    "while",
    "double",
    "in",
    "static",
    "with",
    "get",
    "set",
];

static RESERVED_WORDS_CPP: &'static [&'static str] = &[
    "alignas",
    "alignof",
    "and",
    "and_eq",
    "asm",
    "atomic_cancel",
    "atomic_commit",
    "atomic_noexcept",
    "auto",
    "bitand",
    "bitor",
    "bool",
    "break",
    "case",
    "catch",
    "char",
    "char16_t",
    "char32_t",
    "class",
    "compl",
    "concept",
    "const",
    "constexpr",
    "const_cast",
    "continue",
    "decltype",
    "default",
    "delete",
    "do",
    "double",
    "dynamic_cast",
    "else",
    "enum",
    "explicit",
    "export",
    "extern",
    "false",
    "float",
    "for",
    "friend",
    "goto",
    "if",
    "inline",
    "int",
    "import",
    "long",
    "module",
    "mutable",
    "namespace",
    "new",
    "noexcept",
    "not",
    "not_eq",
    "nullptr",
    // Don't mark "operator" as a keyword so that people can click
    // on it.
    //"operator",
    "or",
    "or_eq",
    "private",
    "protected",
    "public",
    "register",
    "reinterpret_cast",
    "requires",
    "return",
    "short",
    "signed",
    "sizeof",
    "static",
    "static_assert",
    "static_cast",
    "struct",
    "switch",
    "synchronized",
    "template",
    "this",
    "thread_local",
    "throw",
    "true",
    "try",
    "typedef",
    "typeid",
    "typename",
    "union",
    "unsigned",
    "using",
    "virtual",
    "void",
    "volatile",
    "wchar_t",
    "while",
    "xor",
    "xor_eq",
    "#if",
    "#ifdef",
    "#ifndef",
    "#else",
    "#elif",
    "#endif",
    "#define",
    "#undef",
    "#include",
    "#error",
    "defined",
];

// From 'reserved' in ipc/ipdl/ipdl/parser.py
static RESERVED_WORDS_IPDL: &'static [&'static str] = &[
    "async",
    "both",
    "child",
    "class",
    "compress",
    "compressall",
    "from",
    "include",
    "intr",
    "manager",
    "manages",
    "namespace",
    "nested",
    "nullable",
    "or",
    "parent",
    "prio",
    "protocol",
    "refcounted",
    "moveonly",
    "returns",
    "struct",
    "sync",
    "union",
    "UniquePtr",
    "upto",
    "using",
    "verify",
];

static RESERVED_WORDS_IDL: &'static [&'static str] = &[
    "cenum",
    "const",
    "interface",
    "in",
    "inout",
    "out",
    "attribute",
    "raises",
    "readonly",
    "native",
    "typedef",
    "webidl",
    "array",
    "shared",
    "iid_is",
    "size_is",
    "retval",
    "boolean",
    "void",
    "octet",
    "short",
    "long",
    "unsigned",
    "float",
    "double",
    "char",
    "string",
    "wchar",
    "wstring",
    "nsid",
    "AUTF8String",
    "ACString",
    "AString",
    "jsval",
    "Promise",
    "ptr",
    "ref",
    "uuid",
    "scriptable",
    "builtinclass",
    "function",
    "noscript",
    "deprecated",
    "object",
    "main_process_scriptable_only",
    "binaryname",
    "notxpcom",
    "symbol",
    "implicit_jscontext",
    "nostdcall",
    "must_use",
    "infallible",
    "can_run_script",
];

static RESERVED_WORDS_WEBIDL: &'static [&'static str] = &[
    "module",
    "interface",
    "partial",
    "dictionary",
    "exception",
    "enum",
    "callback",
    "typedef",
    "implements",
    "const",
    "null",
    "true",
    "false",
    "serializer",
    "stringifier",
    "jsonifier",
    "unrestricted",
    "attribute",
    "readonly",
    "inherit",
    "static",
    "getter",
    "setter",
    "creator",
    "deleter",
    "legacycaller",
    "optional",
    "Date",
    "DOMString",
    "ByteString",
    "USVString",
    "any",
    "boolean",
    "byte",
    "double",
    "float",
    "long",
    "object",
    "octet",
    "Promise",
    "required",
    "sequence",
    "MozMap",
    "short",
    "unsigned",
    "void",
    "ArrayBuffer",
    "SharedArrayBuffer",
    "or",
    "maplike",
    "setlike",
    "iterable",
    "Exposed",
    "ChromeOnly",
    "ChromeConstructor",
    "Pref",
    "Func",
    "AvailableIn",
    "CheckAnyPermissions",
    "CheckAllPermissions",
    "JSImplementation",
    "HeaderFile",
    "NavigatorProperty",
    "AvailableIn",
    "Func",
    "CheckAnyPermissions",
    "CheckAllPermissions",
    "Deprecated",
    "NeedResolve",
    "OverrideBuiltins",
    "ChromeOnly",
    "Unforgeable",
    "UnsafeInPrerendering",
    "LegacyEventInit",
    "ProbablyShortLivingObject",
    "ArrayClass",
    "Clamp",
    "Constructor",
    "EnforceRange",
    "ExceptionClass",
    "Exposed",
    "ImplicitThis",
    "Global",
    "PrimaryGlobal",
    "LegacyArrayClass",
    "LegacyUnenumerableNamedProperties",
    "LenientSetter",
    "LenientThis",
    "NamedConstructor",
    "NewObject",
    "NoInterfaceObject",
    "OverrideBuiltins",
    "PutForwards",
    "Replaceable",
    "SameObject",
    "SecureContext",
    "Throws",
    "TreatNonObjectAsNull",
    "TreatNullAs",
    "Unforgeable",
    "Unscopable",
];

static RESERVED_WORDS_PYTHON: &'static [&'static str] = &[
    "and", "del", "from", "not", "while", "as", "elif", "global", "or", "with", "assert", "else",
    "if", "pass", "yield", "break", "except", "import", "print", "class", "exec", "in", "raise",
    "continue", "finally", "is", "return", "def", "for", "lambda", "try",
];

// List of Rust reserved words pulled from
// https://github.com/rust-lang/rust/blob/master/src/libsyntax/symbol.rs
static RESERVED_WORDS_RUST: &'static [&'static str] = &[
    "as", "box", "break", "const", "continue", "crate", "else", "enum", "extern", "false", "fn",
    "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub", "ref",
    "return", "self", "Self", "static", "struct", "super", "trait", "true", "type", "unsafe",
    "use", "where", "while", "abstract", "alignof", "become", "do", "final", "macro", "offsetof",
    "override", "priv", "proc", "pure", "sizeof", "typeof", "unsized", "virtual", "yield",
    "default", "union",
];

static RESERVED_WORDS_JAVA: &'static [&'static str] = &[
    "abstract",
    "continue",
    "for",
    "new",
    "switch",
    "assert",
    "default",
    "goto",
    "package",
    "synchronized",
    "boolean",
    "do",
    "if",
    "private",
    "this",
    "break",
    "double",
    "implements",
    "protected",
    "throw",
    "byte",
    "else",
    "import",
    "public",
    "throws",
    "case",
    "enum",
    "instanceof",
    "return",
    "transient",
    "catch",
    "extends",
    "int",
    "short",
    "try",
    "char",
    "final",
    "interface",
    "static",
    "void",
    "class",
    "finally",
    "long",
    "strictfp",
    "volatile",
    "const",
    "float",
    "native",
    "super",
    "while",
    "null",
    "true",
    "false",
];

// http://kotlinlang.org/docs/reference/keyword-reference.html
static RESERVED_WORDS_KOTLIN: &'static [&'static str] = &[
    "as",
    "as?",
    "break",
    "class",
    "continue",
    "do",
    "else",
    "false",
    "for",
    "fun",
    "if",
    "in",
    "!in",
    "interface",
    "is",
    "!is",
    "null",
    "object",
    "package",
    "return",
    "super",
    "this",
    "throw",
    "true",
    "try",
    "typealias",
    "val",
    "var",
    "when",
    "while",
    "by",
    "catch",
    "constructor",
    "delegate",
    "dynamic",
    "field",
    "file",
    "finally",
    "get",
    "import",
    "init",
    "param",
    "property",
    "receiver",
    "set",
    "setparam",
    "where",
    "actual",
    "abstract",
    "annotation",
    "companion",
    "const",
    "crossinline",
    "data",
    "enum",
    "expect",
    "external",
    "final",
    "infix",
    "inline",
    "inner",
    "internal",
    "lateinit",
    "noinline",
    "open",
    "operator",
    "out",
    "override",
    "private",
    "protected",
    "public",
    "reified",
    "sealed",
    "suspend",
    "tailrec",
    "vararg",
    "field",
    "it",
];

lazy_static! {
    static ref JS_SPEC : LanguageSpec = LanguageSpec {
        reserved_words: make_reserved(&*RESERVED_WORDS_JS),
        c_style_comments: true,
        backtick_strings: true,
        regexp_literals: true,
        .. LanguageSpec::default()
    };

    static ref CPP_SPEC : LanguageSpec = LanguageSpec {
        reserved_words: make_reserved(&*RESERVED_WORDS_CPP),
        c_style_comments: true,
        c_preprocessor: true,
        cxx14_digit_separators: true,
        .. LanguageSpec::default()
    };

    static ref IPDL_SPEC : LanguageSpec = LanguageSpec {
        reserved_words: make_reserved(&*RESERVED_WORDS_IPDL),
        c_style_comments: true,
        .. LanguageSpec::default()
    };

    static ref IDL_SPEC : LanguageSpec = LanguageSpec {
        reserved_words: make_reserved(&*RESERVED_WORDS_IDL),
        c_style_comments: true,
        .. LanguageSpec::default()
    };

    static ref WEBIDL_SPEC : LanguageSpec = LanguageSpec {
        reserved_words: make_reserved(&*RESERVED_WORDS_WEBIDL),
        c_style_comments: true,
        .. LanguageSpec::default()
    };

    static ref PYTHON_SPEC : LanguageSpec = LanguageSpec {
        reserved_words: make_reserved(&*RESERVED_WORDS_PYTHON),
        hash_comment: true,
        triple_quote_literals: true,
        .. LanguageSpec::default()
    };

    static ref RUST_SPEC : LanguageSpec = LanguageSpec {
        reserved_words: make_reserved(&*RESERVED_WORDS_RUST),
        hash_comment: true, // for now, for attributes
        c_style_comments: true,
        rust_tweaks: true,
        .. LanguageSpec::default()
    };

    static ref JAVA_SPEC : LanguageSpec = LanguageSpec {
        reserved_words: make_reserved(&*RESERVED_WORDS_JAVA),
        c_style_comments: true,
        .. LanguageSpec::default()
    };

    static ref KOTLIN_SPEC : LanguageSpec = LanguageSpec {
        reserved_words: make_reserved(&*RESERVED_WORDS_KOTLIN),
        c_style_comments: true,
        .. LanguageSpec::default()
    };
}

#[derive(Debug)]
pub enum FormatAs {
    FormatCLike(&'static LanguageSpec),
    FormatTagLike(&'static LanguageSpec),
    Plain,
    Binary,
}

pub fn select_formatting(filename: &str) -> FormatAs {
    let ext = match Path::new(filename).extension() {
        Some(ext) => ext.to_str().unwrap(),
        None => "",
    };
    match ext {
        "c" | "cc" | "cpp" | "cxx" | "h" | "hh" | "hxx" | "hpp" | "mm" => {
            FormatAs::FormatCLike(&*CPP_SPEC)
        }
        "ipdl" | "ipdlh" => FormatAs::FormatCLike(&*IPDL_SPEC),
        "idl" => FormatAs::FormatCLike(&*IDL_SPEC),
        "webidl" => FormatAs::FormatCLike(&*WEBIDL_SPEC),
        "js" | "jsm" | "json" | "mjs" | "sjs" => FormatAs::FormatCLike(&*JS_SPEC),
        "py" | "build" | "configure" => FormatAs::FormatCLike(&*PYTHON_SPEC),
        "rs" => FormatAs::FormatCLike(&*RUST_SPEC),
        "java" => FormatAs::FormatCLike(&*JAVA_SPEC),
        "kt" => FormatAs::FormatCLike(&*KOTLIN_SPEC),

        "html" | "htm" | "xhtml" | "xht" | "xml" | "xul" => FormatAs::FormatTagLike(&*JS_SPEC),

        // Keep this list in sync with the binary types list in nginx-setup.py
        "ogg" | "ttf" | "xpi" | "png" | "bcmap" | "gif" | "ogv" | "jpg" | "jpeg" | "bmp"
        | "icns" | "ico" | "mp4" | "sqlite" | "jar" | "webm" | "webp" | "woff" | "class"
        | "m4s" | "mgif" | "wav" | "opus" | "mp3" | "otf" => FormatAs::Binary,

        _ => FormatAs::Plain,
    }
}
<|endoftext|>


<commit_before>// error-pattern:no mir for DefId

use std::env;
use std::process::{Command, Output};

fn run_miri(file: &str, sysroot: &str) -> Output {
    let path = env::current_dir().unwrap();
    let libpath = path.join("target").join("debug");
    let libpath = libpath.to_str().unwrap();
    let libpath2 = path.join("target").join("debug").join("deps");
    let libpath2 = libpath2.to_str().unwrap();
    Command::new("cargo")
        .args(&[
            "run", "--",
            "--sysroot", sysroot,
            "-L", libpath,
            "-L", libpath2,
            file
        ])
        .output()
        .unwrap_or_else(|e| panic!("failed to execute process: {}", e))
}

fn main() {
    let sysroot = env::var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set");
    let test_run = run_miri("src/bin/miri.rs", &sysroot);

    if test_run.status.code().unwrap_or(-1) != 0 {
        println!("{}", String::from_utf8(test_run.stdout).unwrap());
        panic!("{}", String::from_utf8(test_run.stderr).unwrap());
    }
}
<commit_msg>fallout because compiletest tried to use rustc's log crate<commit_after>// error-pattern:no mir for DefId

use std::env;
use std::process::{Command, Output};

fn run_miri(file: &str, sysroot: &str) -> Output {
    let path = env::current_dir().unwrap();
    let libpath = path.join("target").join("debug");
    let libpath = libpath.to_str().unwrap();
    let libpath2 = path.join("target").join("debug").join("deps");
    let libpath2 = libpath2.to_str().unwrap();
    let mut args = vec![
        "run".to_string(), "--".to_string(),
        "--sysroot".to_string(), sysroot.to_string(),
        "-L".to_string(), libpath.to_string(),
        "-L".to_string(), libpath2.to_string(),
        file.to_string()
    ];
    for file in std::fs::read_dir("target/debug/deps").unwrap() {
        let file = file.unwrap();
        if file.file_type().unwrap().is_file() {
            let path = file.path();
            if let Some(ext) = path.extension() {
                if ext == "rlib" {
                    let name = path.file_stem().unwrap().to_str().unwrap();
                    if let Some(dash) = name.rfind('-') {
                        if name.starts_with("lib") {
                            args.push("--extern".to_string());
                            args.push(format!("{}={}", &name[3..dash], path.to_str().unwrap()));
                        }
                    }
                }
            }
        }
    }
    Command::new("cargo")
        .args(&args)
        .output()
        .unwrap_or_else(|e| panic!("failed to execute process: {}", e))
}

fn main() {
    let sysroot = env::var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set");
    let test_run = run_miri("src/bin/miri.rs", &sysroot);

    if test_run.status.code().unwrap_or(-1) != 0 {
        println!("{}", String::from_utf8(test_run.stdout).unwrap());
        panic!("{}", String::from_utf8(test_run.stderr).unwrap());
    }
}
<|endoftext|>