<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() {
                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());
    }

}

<commit_msg>Query-path fixed<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.content.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 rlua::{UserData, UserDataMethods};

use engine::components::{FactionId, Hp, Pos};
use engine::resources::Spawn;

use std::collections::HashMap;

use rand::Rng;

#[derive(Copy, Clone)]
pub struct UserDataRng<R: Rng + 'static> {
    pub rng: *mut R,
}

unsafe impl<R: Rng + 'static> Send for UserDataRng<R> {}

impl<R: Rng + 'static> UserData for UserDataRng<R> {
    fn add_methods(methods: &mut UserDataMethods<Self>) {
        methods.add_method("rand_int", |_, this, (min, max): (i64, i64)| unsafe {
            Ok((*this.rng).gen_range::<i64>(min, max))
        });

        methods.add_method("rand_double", |_, this, (min, max): (f64, f64)| unsafe {
            Ok((*this.rng).gen_range::<f64>(min, max))
        });
    }
}

#[derive(Clone)]
pub struct UserDataWorld<R: Rng + 'static> {
    pub victory: Option<FactionId>,
    pub rewards: HashMap<String, f64>,
    pub override_skip: bool,
    pub spawn: Vec<Spawn>,
    pub delete_all: bool,
    pub rng: UserDataRng<R>,
}

impl<R: Rng + 'static> UserDataWorld<R> {
    pub fn new(rng: UserDataRng<R>) -> Self {
        UserDataWorld {
            victory: Default::default(),
            rewards: Default::default(),
            override_skip: false,
            spawn: vec![],
            delete_all: false,
            rng,
        }
    }
}

impl<R: Rng + 'static> UserData for UserDataWorld<R> {
    fn add_methods(methods: &mut UserDataMethods<Self>) {
        use rlua::Table;

        methods.add_method_mut("victory", |_, this, faction: usize| {
            this.victory = Some(FactionId(faction));
            Ok(())
        });

        methods.add_method_mut("emit_reward", |_, this, (reward, r_type): (f64, String)| {
            *this.rewards.entry(r_type).or_insert(0.0) += reward;
            Ok(())
        });

        methods.add_method_mut("override_skip", |_, this, val: bool| {
            this.override_skip = val;
            Ok(())
        });

        methods.add_method_mut("spawn", |_, this, to_spawn: Table| {
            let pos: Table = to_spawn.get("pos").unwrap();
            let pos = Pos::new(pos.get("x").unwrap(), pos.get("y").unwrap());

            let spawn = Spawn {
                delay: to_spawn.get("delay").unwrap_or(0),
                pos: pos,
                curr_hp: to_spawn.get("hp").ok(),
                faction: to_spawn.get("faction").unwrap(),
                u_type: to_spawn.get("unit_type").unwrap(),
            };

            this.spawn.push(spawn);
            Ok(())
        });

        methods.add_method_mut("delete_all", |_, this, ()| {
            this.delete_all = true;
            Ok(())
        });

        // Don't ask me why, but just returning this.rng doesn't work even though
        // it's Copy.
        methods.add_method("rng", |_, this, ()| Ok(UserDataRng { rng: this.rng.rng }))
    }
}

#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default, Debug)]
pub struct UserDataReadWorld;

impl UserData for UserDataReadWorld {}

#[derive(Clone, PartialEq, Default, Debug)]
pub struct UserDataUnit {
    pub faction: FactionId,
    pub u_type: String,
    pub hp: Hp,
}

impl UserData for UserDataUnit {
    fn add_methods(methods: &mut UserDataMethods<Self>) {
        methods.add_method("faction", |_, this, ()| Ok(this.faction.0));
        methods.add_method("unit_type", |_, this, ()| Ok(this.u_type.clone()));
        methods.add_method("hp", |_, this, ()| Ok(this.hp.curr_hp));
    }
}
<commit_msg>Add data store<commit_after>use rlua::{UserData, UserDataMethods};

use engine::components::{FactionId, Hp, Pos};
use engine::resources::Spawn;

use std::collections::HashMap;

use rand::Rng;

/// This is a workaround for Lua not having any serializable elements,
/// and no clear semantics to serialize a `Table` object. This may be reworked
/// later with a better API based around a custom foreign serialization implementation
/// for Table and other Lua types.
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Default)]
pub struct DataStore {
    int_data: HashMap<String, i64>,
    float_data: HashMap<String, f64>,
    string_data: HashMap<String, String>,
    bool_data: HashMap<String, bool>,
}

impl DataStore {
    fn remove_key(&mut self, idx: &str) {
        self.int_data.remove(idx);
        self.float_data.remove(idx);
        self.string_data.remove(idx);
        self.bool_data.remove(idx);
    }

    pub fn len(&self) -> usize {
        self.int_data.len() + self.float_data.len() + self.string_data.len() + self.bool_data.len()
    }

    pub fn display_all(&self, out: Option<HashMap<String, String>>) -> HashMap<String, String> {
        let mut out = out.unwrap_or_else(|| HashMap::with_capacity(self.len()));
        out.clear();

        let iter = self.int_data
            .iter()
            .map(|(k, v)| (k.clone(), format!("{}", v)));
        out.extend(iter);

        let iter = self.float_data
            .iter()
            .map(|(k, v)| (k.clone(), format!("{}", v)));
        out.extend(iter);

        let iter = self.bool_data
            .iter()
            .map(|(k, v)| (k.clone(), format!("{}", v)));
        out.extend(iter);

        // Iter.cloned doesn't play nice with hash map's iter method for some reason
        out.extend(self.string_data.iter().map(|(k, v)| (k.clone(), v.clone())));

        out
    }
}

impl UserData for DataStore {
    fn add_methods(methods: &mut UserDataMethods<Self>) {
        use rlua::{MetaMethod, Value};
        use rlua::String as LuaString;

        methods.add_meta_method(MetaMethod::Index, |lua, this, idx: LuaString| {
            let idx = idx.to_str()?;

            if let Some(val) = this.int_data.get(idx) {
                Ok(Value::Integer(*val))
            } else if let Some(val) = this.float_data.get(idx) {
                Ok(Value::Number(*val))
            } else if let Some(val) = this.string_data.get(idx) {
                Ok(Value::String(lua.create_string(val)?))
            } else if let Some(val) = this.bool_data.get(idx) {
                Ok(Value::Boolean(*val))
            } else {
                panic!("Lua used unexpected index {:?}", idx);
            }
        });

        methods.add_meta_method_mut(
            MetaMethod::NewIndex,
            |_, this, (idx, val): (LuaString, Value)| {
                let idx = idx.to_str()?;

                this.remove_key(idx);

                match val {
                    Value::Integer(val) => {
                        this.int_data.insert(idx.to_string(), val);
                    }
                    Value::Number(val) => {
                        this.float_data.insert(idx.to_string(), val);
                    }
                    Value::Boolean(val) => {
                        this.bool_data.insert(idx.to_string(), val);
                    }
                    Value::String(val) => {
                        this.string_data
                            .insert(idx.to_string(), val.to_str()?.to_string());
                    }
                    Value::Nil => {
                        // ... do nothing, we interpret writing nil as deleting a key
                    }
                    _ => panic!("Unsupported type"), // todo, change when we upgrade to failure
                };

                Ok(())
            },
        );
    }
}

#[derive(Copy, Clone)]
pub struct UserDataRng<R: Rng + 'static> {
    pub rng: *mut R,
}

unsafe impl<R: Rng + 'static> Send for UserDataRng<R> {}

impl<R: Rng + 'static> UserData for UserDataRng<R> {
    fn add_methods(methods: &mut UserDataMethods<Self>) {
        methods.add_method("rand_int", |_, this, (min, max): (i64, i64)| unsafe {
            Ok((*this.rng).gen_range::<i64>(min, max))
        });

        methods.add_method("rand_double", |_, this, (min, max): (f64, f64)| unsafe {
            Ok((*this.rng).gen_range::<f64>(min, max))
        });
    }
}

#[derive(Clone)]
pub struct UserDataWorld<R: Rng + 'static> {
    pub victory: Option<FactionId>,
    pub rewards: HashMap<String, f64>,
    pub override_skip: bool,
    pub spawn: Vec<Spawn>,
    pub delete_all: bool,
    pub rng: UserDataRng<R>,
}

impl<R: Rng + 'static> UserDataWorld<R> {
    pub fn new(rng: UserDataRng<R>) -> Self {
        UserDataWorld {
            victory: Default::default(),
            rewards: Default::default(),
            override_skip: false,
            spawn: vec![],
            delete_all: false,
            rng,
        }
    }
}

impl<R: Rng + 'static> UserData for UserDataWorld<R> {
    fn add_methods(methods: &mut UserDataMethods<Self>) {
        use rlua::Table;

        methods.add_method_mut("victory", |_, this, faction: usize| {
            this.victory = Some(FactionId(faction));
            Ok(())
        });

        methods.add_method_mut("emit_reward", |_, this, (reward, r_type): (f64, String)| {
            *this.rewards.entry(r_type).or_insert(0.0) += reward;
            Ok(())
        });

        methods.add_method_mut("override_skip", |_, this, val: bool| {
            this.override_skip = val;
            Ok(())
        });

        methods.add_method_mut("spawn", |_, this, to_spawn: Table| {
            let pos: Table = to_spawn.get("pos").unwrap();
            let pos = Pos::new(pos.get("x").unwrap(), pos.get("y").unwrap());

            let spawn = Spawn {
                delay: to_spawn.get("delay").unwrap_or(0),
                pos: pos,
                curr_hp: to_spawn.get("hp").ok(),
                faction: to_spawn.get("faction").unwrap(),
                u_type: to_spawn.get("unit_type").unwrap(),
            };

            this.spawn.push(spawn);
            Ok(())
        });

        methods.add_method_mut("delete_all", |_, this, ()| {
            this.delete_all = true;
            Ok(())
        });

        // Don't ask me why, but just returning this.rng doesn't work even though
        // it's Copy.
        methods.add_method("rng", |_, this, ()| Ok(UserDataRng { rng: this.rng.rng }))
    }
}

#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default, Debug)]
pub struct UserDataReadWorld;

impl UserData for UserDataReadWorld {}

#[derive(Clone, PartialEq, Default, Debug)]
pub struct UserDataUnit {
    pub faction: FactionId,
    pub u_type: String,
    pub hp: Hp,
}

impl UserData for UserDataUnit {
    fn add_methods(methods: &mut UserDataMethods<Self>) {
        methods.add_method("faction", |_, this, ()| Ok(this.faction.0));
        methods.add_method("unit_type", |_, this, ()| Ok(this.u_type.clone()));
        methods.add_method("hp", |_, this, ()| Ok(this.hp.curr_hp));
    }
}

#[cfg(test)]
mod tests {
    use rlua::Lua;

    const SETUP_PROGRAM: &'static str = r#"store.x = 5
store.a_bool = true
store.str = "hello"
store.pi = 3.14"#;

    fn setup_data_store() -> Lua {
        use super::DataStore;

        let lua = Lua::new();
        lua.globals().set("store", DataStore::default()).unwrap();

        lua.exec::<()>(SETUP_PROGRAM, Some("Data Store")).unwrap();

        lua
    }

    #[test]
    fn lua_data_store_add() {
        use super::DataStore;

        let lua = setup_data_store();
        let store: DataStore = lua.globals().get("store").unwrap();

        assert_eq!(store.int_data.get("x"), Some(&5));
        assert_eq!(store.float_data.get("pi"), Some(&3.14));
        assert_eq!(store.bool_data.get("a_bool"), Some(&true));
        assert_eq!(store.string_data.get("str").map(|s| &**s), Some("hello"));
    }

    #[test]
    fn lua_data_store_remove() {
        use super::DataStore;

        let lua = setup_data_store();

        lua.exec::<()>("store.pi = nil", Some("remove")).unwrap();

        let store: DataStore = lua.globals().get("store").unwrap();

        assert!(store.float_data.get("pi").is_none());
    }

    #[test]
    fn lua_data_store_replace() {
        use super::DataStore;

        let lua = setup_data_store();

        lua.exec::<()>("store.pi = 12", Some("replace")).unwrap();

        let store: DataStore = lua.globals().get("store").unwrap();

        assert!(store.float_data.get("pi").is_none());
        assert_eq!(store.int_data.get("pi"), Some(&12));
    }

    #[test]
    fn lua_data_store_read() {
        use rlua::Number;

        let lua = setup_data_store();

        let pi = lua.exec::<Number>("return store.pi", Some("read")).unwrap();

        assert_eq!(pi, 3.14);
    }
}
<|endoftext|>


<commit_before>//! Code for the 'normalization' query. This consists of a wrapper
//! which folds deeply, invoking the underlying
//! `normalize_projection_ty` query when it encounters projections.

use crate::infer::at::At;
use crate::infer::canonical::OriginalQueryValues;
use crate::infer::{InferCtxt, InferOk};
use crate::traits::error_reporting::InferCtxtExt;
use crate::traits::project::needs_normalization;
use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal};
use rustc_data_structures::sso::SsoHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_infer::traits::Normalized;
use rustc_middle::mir;
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor};

use std::ops::ControlFlow;

use super::NoSolution;

pub use rustc_middle::traits::query::NormalizationResult;

pub trait AtExt<'tcx> {
    fn normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
    where
        T: TypeFoldable<'tcx>;
}

impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> {
    /// Normalize `value` in the context of the inference context,
    /// yielding a resulting type, or an error if `value` cannot be
    /// normalized. If you don't care about regions, you should prefer
    /// `normalize_erasing_regions`, which is more efficient.
    ///
    /// If the normalization succeeds and is unambiguous, returns back
    /// the normalized value along with various outlives relations (in
    /// the form of obligations that must be discharged).
    ///
    /// N.B., this will *eventually* be the main means of
    /// normalizing, but for now should be used only when we actually
    /// know that normalization will succeed, since error reporting
    /// and other details are still "under development".
    fn normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
    where
        T: TypeFoldable<'tcx>,
    {
        debug!(
            "normalize::<{}>(value={:?}, param_env={:?})",
            std::any::type_name::<T>(),
            value,
            self.param_env,
        );
        if !needs_normalization(&value, self.param_env.reveal()) {
            return Ok(Normalized { value, obligations: vec![] });
        }

        let mut normalizer = QueryNormalizer {
            infcx: self.infcx,
            cause: self.cause,
            param_env: self.param_env,
            obligations: vec![],
            error: false,
            cache: SsoHashMap::new(),
            anon_depth: 0,
            universes: vec![],
        };

        // This is actually a consequence by the way `normalize_erasing_regions` works currently.
        // Because it needs to call the `normalize_generic_arg_after_erasing_regions`, it folds
        // through tys and consts in a `TypeFoldable`. Importantly, it skips binders, leaving us
        // with trying to normalize with escaping bound vars.
        //
        // Here, we just add the universes that we *would* have created had we passed through the binders.
        //
        // We *could* replace escaping bound vars eagerly here, but it doesn't seem really necessary.
        // The rest of the code is already set up to be lazy about replacing bound vars,
        // and only when we actually have to normalize.
        if value.has_escaping_bound_vars() {
            let mut max_visitor =
                MaxEscapingBoundVarVisitor { outer_index: ty::INNERMOST, escaping: 0 };
            value.visit_with(&mut max_visitor);
            if max_visitor.escaping > 0 {
                normalizer.universes.extend((0..max_visitor.escaping).map(|_| None));
            }
        }
        let result = value.fold_with(&mut normalizer);
        info!(
            "normalize::<{}>: result={:?} with {} obligations",
            std::any::type_name::<T>(),
            result,
            normalizer.obligations.len(),
        );
        debug!(
            "normalize::<{}>: obligations={:?}",
            std::any::type_name::<T>(),
            normalizer.obligations,
        );
        if normalizer.error {
            Err(NoSolution)
        } else {
            Ok(Normalized { value: result, obligations: normalizer.obligations })
        }
    }
}

/// Visitor to find the maximum escaping bound var
struct MaxEscapingBoundVarVisitor {
    // The index which would count as escaping
    outer_index: ty::DebruijnIndex,
    escaping: usize,
}

impl<'tcx> TypeVisitor<'tcx> for MaxEscapingBoundVarVisitor {
    fn visit_binder<T: TypeFoldable<'tcx>>(
        &mut self,
        t: &ty::Binder<'tcx, T>,
    ) -> ControlFlow<Self::BreakTy> {
        self.outer_index.shift_in(1);
        let result = t.super_visit_with(self);
        self.outer_index.shift_out(1);
        result
    }

    #[inline]
    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
        if t.outer_exclusive_binder() > self.outer_index {
            self.escaping = self
                .escaping
                .max(t.outer_exclusive_binder().as_usize() - self.outer_index.as_usize());
        }
        ControlFlow::CONTINUE
    }

    #[inline]
    fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
        match *r {
            ty::ReLateBound(debruijn, _) if debruijn > self.outer_index => {
                self.escaping =
                    self.escaping.max(debruijn.as_usize() - self.outer_index.as_usize());
            }
            _ => {}
        }
        ControlFlow::CONTINUE
    }

    fn visit_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
        match ct.val {
            ty::ConstKind::Bound(debruijn, _) if debruijn >= self.outer_index => {
                self.escaping =
                    self.escaping.max(debruijn.as_usize() - self.outer_index.as_usize());
                ControlFlow::CONTINUE
            }
            _ => ct.super_visit_with(self),
        }
    }
}

struct QueryNormalizer<'cx, 'tcx> {
    infcx: &'cx InferCtxt<'cx, 'tcx>,
    cause: &'cx ObligationCause<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    obligations: Vec<PredicateObligation<'tcx>>,
    cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>,
    error: bool,
    anon_depth: usize,
    universes: Vec<Option<ty::UniverseIndex>>,
}

impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
    fn tcx<'c>(&'c self) -> TyCtxt<'tcx> {
        self.infcx.tcx
    }

    fn fold_binder<T: TypeFoldable<'tcx>>(
        &mut self,
        t: ty::Binder<'tcx, T>,
    ) -> ty::Binder<'tcx, T> {
        self.universes.push(None);
        let t = t.super_fold_with(self);
        self.universes.pop();
        t
    }

    #[instrument(level = "debug", skip(self))]
    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
        if !needs_normalization(&ty, self.param_env.reveal()) {
            return ty;
        }

        if let Some(ty) = self.cache.get(&ty) {
            return ty;
        }

        // See note in `rustc_trait_selection::traits::project` about why we
        // wait to fold the substs.

        // Wrap this in a closure so we don't accidentally return from the outer function
        let res = (|| match *ty.kind() {
            ty::Opaque(def_id, substs) => {
                // Only normalize `impl Trait` after type-checking, usually in codegen.
                match self.param_env.reveal() {
                    Reveal::UserFacing => ty.super_fold_with(self),

                    Reveal::All => {
                        // N.b. there is an assumption here all this code can handle
                        // escaping bound vars.

                        let substs = substs.super_fold_with(self);
                        let recursion_limit = self.tcx().recursion_limit();
                        if !recursion_limit.value_within_limit(self.anon_depth) {
                            let obligation = Obligation::with_depth(
                                self.cause.clone(),
                                recursion_limit.0,
                                self.param_env,
                                ty,
                            );
                            self.infcx.report_overflow_error(&obligation, true);
                        }

                        let generic_ty = self.tcx().type_of(def_id);
                        let concrete_ty = generic_ty.subst(self.tcx(), substs);
                        self.anon_depth += 1;
                        if concrete_ty == ty {
                            bug!(
                                "infinite recursion generic_ty: {:#?}, substs: {:#?}, \
                                 concrete_ty: {:#?}, ty: {:#?}",
                                generic_ty,
                                substs,
                                concrete_ty,
                                ty
                            );
                        }
                        let folded_ty = ensure_sufficient_stack(|| self.fold_ty(concrete_ty));
                        self.anon_depth -= 1;
                        folded_ty
                    }
                }
            }

            ty::Projection(data) if !data.has_escaping_bound_vars() => {
                // This branch is just an optimization: when we don't have escaping bound vars,
                // we don't need to replace them with placeholders (see branch below).

                let tcx = self.infcx.tcx;
                let data = data.super_fold_with(self);

                let mut orig_values = OriginalQueryValues::default();
                // HACK(matthewjasper) `'static` is special-cased in selection,
                // so we cannot canonicalize it.
                let c_data = self
                    .infcx
                    .canonicalize_query_keep_static(self.param_env.and(data), &mut orig_values);
                debug!("QueryNormalizer: c_data = {:#?}", c_data);
                debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
                match tcx.normalize_projection_ty(c_data) {
                    Ok(result) => {
                        // We don't expect ambiguity.
                        if result.is_ambiguous() {
                            self.error = true;
                            return ty.super_fold_with(self);
                        }

                        match self.infcx.instantiate_query_response_and_region_obligations(
                            self.cause,
                            self.param_env,
                            &orig_values,
                            result,
                        ) {
                            Ok(InferOk { value: result, obligations }) => {
                                debug!("QueryNormalizer: result = {:#?}", result);
                                debug!("QueryNormalizer: obligations = {:#?}", obligations);
                                self.obligations.extend(obligations);
                                result.normalized_ty
                            }

                            Err(_) => {
                                self.error = true;
                                ty.super_fold_with(self)
                            }
                        }
                    }

                    Err(NoSolution) => {
                        self.error = true;
                        ty.super_fold_with(self)
                    }
                }
            }

            ty::Projection(data) => {
                // See note in `rustc_trait_selection::traits::project`

                let tcx = self.infcx.tcx;
                let infcx = self.infcx;
                let (data, mapped_regions, mapped_types, mapped_consts) =
                    crate::traits::project::BoundVarReplacer::replace_bound_vars(
                        infcx,
                        &mut self.universes,
                        data,
                    );
                let data = data.super_fold_with(self);

                let mut orig_values = OriginalQueryValues::default();
                // HACK(matthewjasper) `'static` is special-cased in selection,
                // so we cannot canonicalize it.
                let c_data = self
                    .infcx
                    .canonicalize_query_keep_static(self.param_env.and(data), &mut orig_values);
                debug!("QueryNormalizer: c_data = {:#?}", c_data);
                debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
                match tcx.normalize_projection_ty(c_data) {
                    Ok(result) => {
                        // We don't expect ambiguity.
                        if result.is_ambiguous() {
                            self.error = true;
                            return ty.super_fold_with(self);
                        }
                        match self.infcx.instantiate_query_response_and_region_obligations(
                            self.cause,
                            self.param_env,
                            &orig_values,
                            result,
                        ) {
                            Ok(InferOk { value: result, obligations }) => {
                                debug!("QueryNormalizer: result = {:#?}", result);
                                debug!("QueryNormalizer: obligations = {:#?}", obligations);
                                self.obligations.extend(obligations);
                                crate::traits::project::PlaceholderReplacer::replace_placeholders(
                                    infcx,
                                    mapped_regions,
                                    mapped_types,
                                    mapped_consts,
                                    &self.universes,
                                    result.normalized_ty,
                                )
                            }
                            Err(_) => {
                                self.error = true;
                                ty.super_fold_with(self)
                            }
                        }
                    }
                    Err(NoSolution) => {
                        self.error = true;
                        ty.super_fold_with(self)
                    }
                }
            }

            _ => ty.super_fold_with(self),
        })();
        self.cache.insert(ty, res);
        res
    }

    fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
        let constant = constant.super_fold_with(self);
        constant.eval(self.infcx.tcx, self.param_env)
    }

    fn fold_mir_const(&mut self, constant: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> {
        constant.super_fold_with(self)
    }
}
<commit_msg>rebase<commit_after>//! Code for the 'normalization' query. This consists of a wrapper
//! which folds deeply, invoking the underlying
//! `normalize_projection_ty` query when it encounters projections.

use crate::infer::at::At;
use crate::infer::canonical::OriginalQueryValues;
use crate::infer::{InferCtxt, InferOk};
use crate::traits::error_reporting::InferCtxtExt;
use crate::traits::project::needs_normalization;
use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal};
use rustc_data_structures::sso::SsoHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_infer::traits::Normalized;
use rustc_middle::mir;
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor};

use std::ops::ControlFlow;

use super::NoSolution;

pub use rustc_middle::traits::query::NormalizationResult;

pub trait AtExt<'tcx> {
    fn normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
    where
        T: TypeFoldable<'tcx>;
}

impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> {
    /// Normalize `value` in the context of the inference context,
    /// yielding a resulting type, or an error if `value` cannot be
    /// normalized. If you don't care about regions, you should prefer
    /// `normalize_erasing_regions`, which is more efficient.
    ///
    /// If the normalization succeeds and is unambiguous, returns back
    /// the normalized value along with various outlives relations (in
    /// the form of obligations that must be discharged).
    ///
    /// N.B., this will *eventually* be the main means of
    /// normalizing, but for now should be used only when we actually
    /// know that normalization will succeed, since error reporting
    /// and other details are still "under development".
    fn normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
    where
        T: TypeFoldable<'tcx>,
    {
        debug!(
            "normalize::<{}>(value={:?}, param_env={:?})",
            std::any::type_name::<T>(),
            value,
            self.param_env,
        );
        if !needs_normalization(&value, self.param_env.reveal()) {
            return Ok(Normalized { value, obligations: vec![] });
        }

        let mut normalizer = QueryNormalizer {
            infcx: self.infcx,
            cause: self.cause,
            param_env: self.param_env,
            obligations: vec![],
            error: false,
            cache: SsoHashMap::new(),
            anon_depth: 0,
            universes: vec![],
        };

        // This is actually a consequence by the way `normalize_erasing_regions` works currently.
        // Because it needs to call the `normalize_generic_arg_after_erasing_regions`, it folds
        // through tys and consts in a `TypeFoldable`. Importantly, it skips binders, leaving us
        // with trying to normalize with escaping bound vars.
        //
        // Here, we just add the universes that we *would* have created had we passed through the binders.
        //
        // We *could* replace escaping bound vars eagerly here, but it doesn't seem really necessary.
        // The rest of the code is already set up to be lazy about replacing bound vars,
        // and only when we actually have to normalize.
        if value.has_escaping_bound_vars() {
            let mut max_visitor = MaxEscapingBoundVarVisitor {
                tcx: self.infcx.tcx,
                outer_index: ty::INNERMOST,
                escaping: 0,
            };
            value.visit_with(&mut max_visitor);
            if max_visitor.escaping > 0 {
                normalizer.universes.extend((0..max_visitor.escaping).map(|_| None));
            }
        }
        let result = value.fold_with(&mut normalizer);
        info!(
            "normalize::<{}>: result={:?} with {} obligations",
            std::any::type_name::<T>(),
            result,
            normalizer.obligations.len(),
        );
        debug!(
            "normalize::<{}>: obligations={:?}",
            std::any::type_name::<T>(),
            normalizer.obligations,
        );
        if normalizer.error {
            Err(NoSolution)
        } else {
            Ok(Normalized { value: result, obligations: normalizer.obligations })
        }
    }
}

/// Visitor to find the maximum escaping bound var
struct MaxEscapingBoundVarVisitor<'tcx> {
    tcx: TyCtxt<'tcx>,
    // The index which would count as escaping
    outer_index: ty::DebruijnIndex,
    escaping: usize,
}

impl<'tcx> TypeVisitor<'tcx> for MaxEscapingBoundVarVisitor<'tcx> {
    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
        Some(self.tcx)
    }

    fn visit_binder<T: TypeFoldable<'tcx>>(
        &mut self,
        t: &ty::Binder<'tcx, T>,
    ) -> ControlFlow<Self::BreakTy> {
        self.outer_index.shift_in(1);
        let result = t.super_visit_with(self);
        self.outer_index.shift_out(1);
        result
    }

    #[inline]
    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
        if t.outer_exclusive_binder() > self.outer_index {
            self.escaping = self
                .escaping
                .max(t.outer_exclusive_binder().as_usize() - self.outer_index.as_usize());
        }
        ControlFlow::CONTINUE
    }

    #[inline]
    fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
        match *r {
            ty::ReLateBound(debruijn, _) if debruijn > self.outer_index => {
                self.escaping =
                    self.escaping.max(debruijn.as_usize() - self.outer_index.as_usize());
            }
            _ => {}
        }
        ControlFlow::CONTINUE
    }

    fn visit_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
        match ct.val {
            ty::ConstKind::Bound(debruijn, _) if debruijn >= self.outer_index => {
                self.escaping =
                    self.escaping.max(debruijn.as_usize() - self.outer_index.as_usize());
                ControlFlow::CONTINUE
            }
            _ => ct.super_visit_with(self),
        }
    }
}

struct QueryNormalizer<'cx, 'tcx> {
    infcx: &'cx InferCtxt<'cx, 'tcx>,
    cause: &'cx ObligationCause<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    obligations: Vec<PredicateObligation<'tcx>>,
    cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>,
    error: bool,
    anon_depth: usize,
    universes: Vec<Option<ty::UniverseIndex>>,
}

impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
    fn tcx<'c>(&'c self) -> TyCtxt<'tcx> {
        self.infcx.tcx
    }

    fn fold_binder<T: TypeFoldable<'tcx>>(
        &mut self,
        t: ty::Binder<'tcx, T>,
    ) -> ty::Binder<'tcx, T> {
        self.universes.push(None);
        let t = t.super_fold_with(self);
        self.universes.pop();
        t
    }

    #[instrument(level = "debug", skip(self))]
    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
        if !needs_normalization(&ty, self.param_env.reveal()) {
            return ty;
        }

        if let Some(ty) = self.cache.get(&ty) {
            return ty;
        }

        // See note in `rustc_trait_selection::traits::project` about why we
        // wait to fold the substs.

        // Wrap this in a closure so we don't accidentally return from the outer function
        let res = (|| match *ty.kind() {
            ty::Opaque(def_id, substs) => {
                // Only normalize `impl Trait` after type-checking, usually in codegen.
                match self.param_env.reveal() {
                    Reveal::UserFacing => ty.super_fold_with(self),

                    Reveal::All => {
                        // N.b. there is an assumption here all this code can handle
                        // escaping bound vars.

                        let substs = substs.super_fold_with(self);
                        let recursion_limit = self.tcx().recursion_limit();
                        if !recursion_limit.value_within_limit(self.anon_depth) {
                            let obligation = Obligation::with_depth(
                                self.cause.clone(),
                                recursion_limit.0,
                                self.param_env,
                                ty,
                            );
                            self.infcx.report_overflow_error(&obligation, true);
                        }

                        let generic_ty = self.tcx().type_of(def_id);
                        let concrete_ty = generic_ty.subst(self.tcx(), substs);
                        self.anon_depth += 1;
                        if concrete_ty == ty {
                            bug!(
                                "infinite recursion generic_ty: {:#?}, substs: {:#?}, \
                                 concrete_ty: {:#?}, ty: {:#?}",
                                generic_ty,
                                substs,
                                concrete_ty,
                                ty
                            );
                        }
                        let folded_ty = ensure_sufficient_stack(|| self.fold_ty(concrete_ty));
                        self.anon_depth -= 1;
                        folded_ty
                    }
                }
            }

            ty::Projection(data) if !data.has_escaping_bound_vars() => {
                // This branch is just an optimization: when we don't have escaping bound vars,
                // we don't need to replace them with placeholders (see branch below).

                let tcx = self.infcx.tcx;
                let data = data.super_fold_with(self);

                let mut orig_values = OriginalQueryValues::default();
                // HACK(matthewjasper) `'static` is special-cased in selection,
                // so we cannot canonicalize it.
                let c_data = self
                    .infcx
                    .canonicalize_query_keep_static(self.param_env.and(data), &mut orig_values);
                debug!("QueryNormalizer: c_data = {:#?}", c_data);
                debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
                match tcx.normalize_projection_ty(c_data) {
                    Ok(result) => {
                        // We don't expect ambiguity.
                        if result.is_ambiguous() {
                            self.error = true;
                            return ty.super_fold_with(self);
                        }

                        match self.infcx.instantiate_query_response_and_region_obligations(
                            self.cause,
                            self.param_env,
                            &orig_values,
                            result,
                        ) {
                            Ok(InferOk { value: result, obligations }) => {
                                debug!("QueryNormalizer: result = {:#?}", result);
                                debug!("QueryNormalizer: obligations = {:#?}", obligations);
                                self.obligations.extend(obligations);
                                result.normalized_ty
                            }

                            Err(_) => {
                                self.error = true;
                                ty.super_fold_with(self)
                            }
                        }
                    }

                    Err(NoSolution) => {
                        self.error = true;
                        ty.super_fold_with(self)
                    }
                }
            }

            ty::Projection(data) => {
                // See note in `rustc_trait_selection::traits::project`

                let tcx = self.infcx.tcx;
                let infcx = self.infcx;
                let (data, mapped_regions, mapped_types, mapped_consts) =
                    crate::traits::project::BoundVarReplacer::replace_bound_vars(
                        infcx,
                        &mut self.universes,
                        data,
                    );
                let data = data.super_fold_with(self);

                let mut orig_values = OriginalQueryValues::default();
                // HACK(matthewjasper) `'static` is special-cased in selection,
                // so we cannot canonicalize it.
                let c_data = self
                    .infcx
                    .canonicalize_query_keep_static(self.param_env.and(data), &mut orig_values);
                debug!("QueryNormalizer: c_data = {:#?}", c_data);
                debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
                match tcx.normalize_projection_ty(c_data) {
                    Ok(result) => {
                        // We don't expect ambiguity.
                        if result.is_ambiguous() {
                            self.error = true;
                            return ty.super_fold_with(self);
                        }
                        match self.infcx.instantiate_query_response_and_region_obligations(
                            self.cause,
                            self.param_env,
                            &orig_values,
                            result,
                        ) {
                            Ok(InferOk { value: result, obligations }) => {
                                debug!("QueryNormalizer: result = {:#?}", result);
                                debug!("QueryNormalizer: obligations = {:#?}", obligations);
                                self.obligations.extend(obligations);
                                crate::traits::project::PlaceholderReplacer::replace_placeholders(
                                    infcx,
                                    mapped_regions,
                                    mapped_types,
                                    mapped_consts,
                                    &self.universes,
                                    result.normalized_ty,
                                )
                            }
                            Err(_) => {
                                self.error = true;
                                ty.super_fold_with(self)
                            }
                        }
                    }
                    Err(NoSolution) => {
                        self.error = true;
                        ty.super_fold_with(self)
                    }
                }
            }

            _ => ty.super_fold_with(self),
        })();
        self.cache.insert(ty, res);
        res
    }

    fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
        let constant = constant.super_fold_with(self);
        constant.eval(self.infcx.tcx, self.param_env)
    }

    fn fold_mir_const(&mut self, constant: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> {
        constant.super_fold_with(self)
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 Kubos Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

extern crate chrono;
#[macro_use]
extern crate failure;
#[macro_use]
extern crate kubos_app;

use app_service::*;
use chrono::Utc;
use kubos_app::{AppHandler, ServiceConfig};
use monitor_service::*;
use std::fs::OpenOptions;
use std::io::Write;
use std::thread;
use std::time::Duration;
use telem_service::*;

struct MyApp;

const TELEMFILE: &str = "/home/kubos/release-test/telem-results";
const LOGFILE: &str = "/home/kubos/release-test/test-output";

macro_rules! log {
    ($log_file:ident, $msg:expr) => {{
        writeln!($log_file, "{}: {}", Utc::now(), $msg).unwrap();
    }};
    ($log_file:ident, $msg:expr, $($arg:tt)*) => {{
        let message = format!($msg, $($arg)*);
        writeln!($log_file, "{}: {}", Utc::now(), message).unwrap();
    }};
}

mod app_service;
mod monitor_service;
mod telem_service;

impl AppHandler for MyApp {
    fn on_boot(&self, _args: Vec<String>) {
        // Set up the log file
        let mut log_file = OpenOptions::new()
            .create(true)
            .append(true)
            .open(LOGFILE)
            .unwrap();

        log!(log_file, "OnBoot logic called");
    }

    fn on_command(&self, _args: Vec<String>) {
        // Monitor Service Tests
        monitor_test().unwrap();

        // Telemetry Service Tests
        telemetry_test().unwrap();

        // App Service Tests
        apps_test().unwrap();
    }
}

fn main() {
    let app = MyApp;
    app_main!(&app);
}
<commit_msg>Updating release test to conform to Rust app trait<commit_after>/*
 * Copyright (C) 2018 Kubos Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

extern crate chrono;
#[macro_use]
extern crate failure;
#[macro_use]
extern crate kubos_app;

use app_service::*;
use chrono::Utc;
use failure::Error;
use kubos_app::{AppHandler, ServiceConfig};
use monitor_service::*;
use std::fs::OpenOptions;
use std::io::Write;
use std::time::Duration;
use telem_service::*;

struct MyApp;

const TELEMFILE: &str = "/home/kubos/release-test/telem-results";
const LOGFILE: &str = "/home/kubos/release-test/test-output";

macro_rules! log {
    ($log_file:ident, $msg:expr) => {{
        writeln!($log_file, "{}: {}", Utc::now(), $msg).unwrap();
    }};
    ($log_file:ident, $msg:expr, $($arg:tt)*) => {{
        let message = format!($msg, $($arg)*);
        writeln!($log_file, "{}: {}", Utc::now(), message).unwrap();
    }};
}

mod app_service;
mod monitor_service;
mod telem_service;

impl AppHandler for MyApp {
    fn on_boot(&self, _args: Vec<String>) -> Result<(), Error> {
        // Set up the log file
        let mut log_file = OpenOptions::new()
            .create(true)
            .append(true)
            .open(LOGFILE)?;

        log!(log_file, "OnBoot logic called");

        Ok(())
    }

    fn on_command(&self, _args: Vec<String>) -> Result<(), Error> {
        // Monitor Service Tests
        monitor_test()?;

        // Telemetry Service Tests
        telemetry_test()?;

        // App Service Tests
        apps_test()?;

        Ok(())
    }
}

fn main() -> Result<(), Error> {
    let app = MyApp;
    app_main!(&app)?;

    Ok(())
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use font::FontHandleMethods;
use font_context::FontContextHandleMethods;
use font_list::{FontEntry, FontFamily, FontFamilyMap};
use platform::macos::font::FontHandle;
use platform::macos::font_context::FontContextHandle;

use core_foundation::base::TCFType;
use core_foundation::string::{CFString, CFStringRef};
use core_text;
use core_text::font_descriptor::{CTFontDescriptor, CTFontDescriptorRef};

use std::cast;
use std::hashmap::HashMap;

pub struct FontListHandle {
    fctx: FontContextHandle,
}

impl FontListHandle {
    pub fn new(fctx: &FontContextHandle) -> FontListHandle {
        FontListHandle {
            fctx: fctx.clone()
        }
    }

    pub fn get_available_families(&self) -> FontFamilyMap {
        let family_names = core_text::font_collection::get_family_names();
        let mut family_map: FontFamilyMap = HashMap::new();
        for strref in family_names.iter() {
            let family_name_ref: CFStringRef = unsafe { cast::transmute(strref) };
            let family_name_cf: CFString = unsafe { TCFType::wrap_under_get_rule(family_name_ref) };
            let family_name = family_name_cf.to_str();
            debug!("Creating new FontFamily for family: {:s}", family_name);

            let new_family = FontFamily::new(family_name);
            family_map.insert(family_name, new_family);
        }
        family_map
    }

    pub fn load_variations_for_family(&self, family: &mut FontFamily) {
        debug!("Looking for faces of family: {:s}", family.family_name);

        let family_collection = core_text::font_collection::create_for_family(family.family_name);
        let family_descriptors = family_collection.get_descriptors();
        for descref in family_descriptors.iter() {
            let descref: CTFontDescriptorRef = unsafe { cast::transmute(descref) };
            let desc: CTFontDescriptor = unsafe { TCFType::wrap_under_get_rule(descref) };
            let font = core_text::font::new_from_descriptor(&desc, 0.0);
            let handle = FontHandle::new_from_CTFont(&self.fctx, font).unwrap();

            debug!("Creating new FontEntry for face: {:s}", handle.face_name());
            let entry = FontEntry::new(handle);
            family.entries.push(entry)
        }
    }

    pub fn get_last_resort_font_families() -> ~[~str] {
        ~[~"Arial Unicode MS",~"Arial"]
    }
}
<commit_msg>auto merge of #1401 : saneyuki/servo/unused, r=metajack<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use font::FontHandleMethods;
use font_list::{FontEntry, FontFamily, FontFamilyMap};
use platform::macos::font::FontHandle;
use platform::macos::font_context::FontContextHandle;

use core_foundation::base::TCFType;
use core_foundation::string::{CFString, CFStringRef};
use core_text;
use core_text::font_descriptor::{CTFontDescriptor, CTFontDescriptorRef};

use std::cast;
use std::hashmap::HashMap;

pub struct FontListHandle {
    fctx: FontContextHandle,
}

impl FontListHandle {
    pub fn new(fctx: &FontContextHandle) -> FontListHandle {
        FontListHandle {
            fctx: fctx.clone()
        }
    }

    pub fn get_available_families(&self) -> FontFamilyMap {
        let family_names = core_text::font_collection::get_family_names();
        let mut family_map: FontFamilyMap = HashMap::new();
        for strref in family_names.iter() {
            let family_name_ref: CFStringRef = unsafe { cast::transmute(strref) };
            let family_name_cf: CFString = unsafe { TCFType::wrap_under_get_rule(family_name_ref) };
            let family_name = family_name_cf.to_str();
            debug!("Creating new FontFamily for family: {:s}", family_name);

            let new_family = FontFamily::new(family_name);
            family_map.insert(family_name, new_family);
        }
        family_map
    }

    pub fn load_variations_for_family(&self, family: &mut FontFamily) {
        debug!("Looking for faces of family: {:s}", family.family_name);

        let family_collection = core_text::font_collection::create_for_family(family.family_name);
        let family_descriptors = family_collection.get_descriptors();
        for descref in family_descriptors.iter() {
            let descref: CTFontDescriptorRef = unsafe { cast::transmute(descref) };
            let desc: CTFontDescriptor = unsafe { TCFType::wrap_under_get_rule(descref) };
            let font = core_text::font::new_from_descriptor(&desc, 0.0);
            let handle = FontHandle::new_from_CTFont(&self.fctx, font).unwrap();

            debug!("Creating new FontEntry for face: {:s}", handle.face_name());
            let entry = FontEntry::new(handle);
            family.entries.push(entry)
        }
    }

    pub fn get_last_resort_font_families() -> ~[~str] {
        ~[~"Arial Unicode MS",~"Arial"]
    }
}
<|endoftext|>