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

use error::MarkdownError as ME;
use error::MarkdownErrorKind as MEK;
use error::*;
use link::extract_links;

use libimagentrylink::external::ExternalLinker;
use libimagentrylink::internal::InternalLinker;
use libimagentryref::refstore::RefStore;
use libimagentryref::flags::RefFlags;
use libimagstore::store::Entry;
use libimagstore::store::Store;
use libimagstore::storeid::StoreId;
use libimagutil::iter::FoldResult;

use std::path::PathBuf;

use url::Url;

/// A link Processor which collects the links from a Markdown and passes them on to
/// `libimagentrylink` functionality
///
/// The processor can be configured to
///
///  * Process internal links (from store entry to store entry)
///  * Process internal links with automatically creating targets
///     If an internal link is encountered, the corrosponding target must be present in the store.
///     If it is not, it will either be created or the processing fails
///  * Process external links (from store entry to URL)
///  * Process refs (from store entry to files on the filesystem and outside of the store)
///  (default: false)
///
///  # Note
///
///  There's no LinkProcessor::new() function, please use `LinkProcessor::default()`.
///
pub struct LinkProcessor {
    process_internal_links: bool,
    create_internal_targets: bool,
    process_external_links: bool,
    process_refs: bool
}

impl LinkProcessor {

    /// Switch internal link processing on/off
    ///
    /// Internal links are links which are simply `dirctory/file`, but not `/directory/file`, as
    /// beginning an id with `/` is discouraged in imag.
    pub fn process_internal_links(mut self, b: bool) -> Self {
        self.process_internal_links = b;
        self
    }

    /// Switch internal link target creation on/off
    ///
    /// If a link points to a non-existing imag entry, a `false` here will cause the processor to
    /// return an error from `process()`. A `true` setting will create the entry and then fetch it
    /// to link it to the processed entry.
    pub fn create_internal_targets(mut self, b: bool) -> Self {
        self.create_internal_targets = b;
        self
    }

    /// Switch external link processing on/off
    ///
    /// An external link must start with `https://` or `http://`.
    pub fn process_external_links(mut self, b: bool) -> Self {
        self.process_external_links = b;
        self
    }

    /// Switch ref processing on/off
    ///
    /// A Ref is to be expected beeing a link with `file::///` at the beginning.
    pub fn process_refs(mut self, b: bool) -> Self {
        self.process_refs = b;
        self
    }

    /// Process an Entry for its links
    ///
    /// # Warning
    ///
    /// When `LinkProcessor::create_internal_targets()` was called to set the setting to true, this
    /// function returns all errors returned by the Store.
    ///
    pub fn process<'a>(&self, entry: &mut Entry, store: &'a Store) -> Result<()> {
        let text = entry.to_str();
        trace!("Processing: {:?}", entry.get_location());
        extract_links(&text)
            .into_iter()
            .fold_result::<_, MarkdownError, _>(|link| {
                trace!("Processing {:?}", link);
                match LinkQualification::qualify(&link.link) {
                    LinkQualification::InternalLink => {
                        if !self.process_internal_links {
                            return Ok(());
                        }

                        let spath      = Some(store.path().clone());
                        let id         = StoreId::new(spath, PathBuf::from(&link.link))?;
                        let mut target = if self.create_internal_targets {
                            try!(store.retrieve(id))
                        } else {
                            store.get(id.clone())?
                                .ok_or(ME::from_kind(MEK::StoreGetError(id)))?
                        };

                        entry.add_internal_link(&mut target).map_err(From::from)
                    },
                    LinkQualification::ExternalLink(url) => {
                        if !self.process_external_links {
                            return Ok(());
                        }

                        entry.add_external_link(store, url).map_err(From::from)
                    },
                    LinkQualification::RefLink(url) => {
                        if !self.process_refs {
                            return Ok(());
                        }

                        let flags = RefFlags::default()
                            .with_content_hashing(false)
                            .with_permission_tracking(false);
                        trace!("URL            = {:?}", url);
                        trace!("URL.path()     = {:?}", url.path());
                        trace!("URL.host_str() = {:?}", url.host_str());
                        let path = url.host_str().unwrap_or_else(|| url.path());
                        let path = PathBuf::from(path);
                        let mut target = try!(RefStore::create(store, path, flags));

                        entry.add_internal_link(&mut target).map_err(From::from)
                    },
                    LinkQualification::Undecidable(e) => {
                        // error
                        Err(e).chain_err(|| MEK::UndecidableLinkType(link.link.clone()))
                    },
                }
            })
    }

}

/// Enum to tell what kind of link a string of text is
enum LinkQualification {
    InternalLink,
    ExternalLink(Url),
    RefLink(Url),
    Undecidable(ME),
}

impl LinkQualification {
    fn qualify(text: &str) -> LinkQualification {
        match Url::parse(text) {
            Ok(url) => {
                if url.scheme() == "file" {
                    return LinkQualification::RefLink(url)
                }

                // else we assume the following, as other stuff gets thrown out by
                // url::Url::parse() as Err(_)
                //
                // if url.scheme() == "https" || url.scheme() == "http" {
                    return LinkQualification::ExternalLink(url);
                // }
            },

            Err(e) => {
                match e {
                    ::url::ParseError::RelativeUrlWithoutBase => {
                        LinkQualification::InternalLink
                    },

                    _ => LinkQualification::Undecidable(ME::from(e)),
                }
            }
        }
    }
}

impl Default for LinkProcessor {
    fn default() -> Self {
        LinkProcessor {
            process_internal_links: true,
            create_internal_targets: false,
            process_external_links: true,
            process_refs: false
        }
    }
}

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

    use std::path::PathBuf;

    use libimagstore::store::Store;
    use libimagentrylink::internal::InternalLinker;

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

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

    #[test]
    fn test_process_no_links() {
        setup_logging();
        let store = get_store();

        let mut base = store.create(PathBuf::from("test-1")).unwrap();
        *base.get_content_mut() = format!("This is an example entry with no links");

        let update = store.update(&mut base);
        assert!(update.is_ok());

        let processor = LinkProcessor::default();

        let result = processor.process(&mut base, &store);
        assert!(result.is_ok());
    }

    #[test]
    fn test_process_one_existing_file_linked() {
        setup_logging();
        let store = get_store();

        let mut base = store.create(PathBuf::from("test-2.1")).unwrap();
        *base.get_content_mut() = format!("This is an example entry with one [link](test-2.2)");

        let update = store.update(&mut base);
        assert!(update.is_ok());

        // immediately drop as we don't need this entry right now
        let _ = store.create(PathBuf::from("test-2.2")).unwrap();

        let processor = LinkProcessor::default()
            .process_internal_links(true)
            .create_internal_targets(false)
            .process_external_links(false)
            .process_refs(false);

        let result = processor.process(&mut base, &store);
        assert!(result.is_ok(), "Should be Ok(()): {:?}", result);

        {
            let base_links = base.get_internal_links();
            assert!(base_links.is_ok());
            let base_links : Vec<_> = base_links.unwrap().collect();

            assert_eq!(1, base_links.len());
            assert_eq!("test-2.2", base_links[0].to_str().unwrap());
        }

        {
            let link = store.get(PathBuf::from("test-2.2")).unwrap().unwrap();
            let link_links = link.get_internal_links();
            assert!(link_links.is_ok());
            let link_links : Vec<_> = link_links.unwrap().collect();

            assert_eq!(1, link_links.len());
            assert_eq!("test-2.1", link_links[0].to_str().unwrap());
        }
    }

    #[test]
    fn test_process_one_existing_file_linked_faulty() {
        setup_logging();
        let store = get_store();

        let mut base = store.create(PathBuf::from("test-2.1")).unwrap();
        *base.get_content_mut() = format!("This is an example entry with one [link](/test-2.2)");

        let update = store.update(&mut base);
        assert!(update.is_ok());

        let processor = LinkProcessor::default()
            .process_internal_links(true)
            .create_internal_targets(false)
            .process_external_links(false)
            .process_refs(false);

        let result = processor.process(&mut base, &store);
        assert!(result.is_err(), "Should be Err(_), but is Ok(())");
    }

    #[test]
    fn test_process_one_nonexisting_file_linked() {
        setup_logging();
        let store = get_store();

        let mut base = store.create(PathBuf::from("test-2.1")).unwrap();
        *base.get_content_mut() = format!("This is an example entry with one [link](test-2.2)");

        let update = store.update(&mut base);
        assert!(update.is_ok());

        let processor = LinkProcessor::default()
            .process_internal_links(true)
            .create_internal_targets(true)
            .process_external_links(false)
            .process_refs(false);

        let result = processor.process(&mut base, &store);
        assert!(result.is_ok(), "Should be Ok(()): {:?}", result);

        {
            let base_links = base.get_internal_links();
            assert!(base_links.is_ok());
            let base_links : Vec<_> = base_links.unwrap().collect();

            assert_eq!(1, base_links.len());
            assert_eq!("test-2.2", base_links[0].to_str().unwrap());
        }

        {
            let link = store.get(PathBuf::from("test-2.2")).unwrap().unwrap();
            let link_links = link.get_internal_links();
            assert!(link_links.is_ok());
            let link_links : Vec<_> = link_links.unwrap().collect();

            assert_eq!(1, link_links.len());
            assert_eq!("test-2.1", link_links[0].to_str().unwrap());
        }
    }

    #[test]
    fn test_process_one_external_link() {
        setup_logging();
        let store = get_store();

        let mut base = store.create(PathBuf::from("test-5.1")).unwrap();
        *base.get_content_mut() = format!("An [example](http://example.com) is here.");

        let update = store.update(&mut base);
        assert!(update.is_ok());

        let processor = LinkProcessor::default()
            .process_internal_links(true)
            .create_internal_targets(true)
            .process_external_links(false)
            .process_refs(false);

        let result = processor.process(&mut base, &store);
        assert!(result.is_ok(), "Should be Ok(()): {:?}", result);

        // The hash of "http://example.com" processed in the `libimagentrylink` way.
        let expected_link = "links/external/9c17e047f58f9220a7008d4f18152fee4d111d14";
        {
            let base_links = base.get_internal_links();
            assert!(base_links.is_ok());
            let base_links : Vec<_> = base_links.unwrap().collect();

            assert_eq!(1, base_links.len());
            assert_eq!(expected_link, base_links[0].to_str().unwrap());
        }

        let entries = store.entries();
        assert!(entries.is_ok());
        let entries : Vec<_> = entries.unwrap().collect();

        assert_eq!(2, entries.len(), "Expected 2 links, got: {:?}", entries);

        {
            let link = store.get(PathBuf::from(expected_link)).unwrap().unwrap();
            let link_links = link.get_internal_links();
            assert!(link_links.is_ok());
            let link_links : Vec<_> = link_links.unwrap().collect();

            assert_eq!(1, link_links.len());
            assert_eq!("test-5.1", link_links[0].to_str().unwrap());
        }
    }

    #[test]
    fn test_process_one_ref() {
        setup_logging();
        let store = get_store();

        let mut base = store.create(PathBuf::from("test-5.1")).unwrap();

        // As the ref target must exist, we're using /etc/hosts here
        *base.get_content_mut() = format!("An [example ref](file:///etc/hosts) is here.");

        let update = store.update(&mut base);
        assert!(update.is_ok());

        let processor = LinkProcessor::default()
            .process_internal_links(false)
            .create_internal_targets(false)
            .process_external_links(false)
            .process_refs(true);

        let result = processor.process(&mut base, &store);
        assert!(result.is_ok(), "Should be Ok(()): {:?}", result);

        let entries = store.entries();
        assert!(entries.is_ok());
        let entries : Vec<_> = entries.unwrap().collect();

        assert_eq!(2, entries.len(), "Expected 2 links, got: {:?}", entries);
        println!("{:?}", entries);
    }
}

<commit_msg>Add test for processing two refs<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
//

use error::MarkdownError as ME;
use error::MarkdownErrorKind as MEK;
use error::*;
use link::extract_links;

use libimagentrylink::external::ExternalLinker;
use libimagentrylink::internal::InternalLinker;
use libimagentryref::refstore::RefStore;
use libimagentryref::flags::RefFlags;
use libimagstore::store::Entry;
use libimagstore::store::Store;
use libimagstore::storeid::StoreId;
use libimagutil::iter::FoldResult;

use std::path::PathBuf;

use url::Url;

/// A link Processor which collects the links from a Markdown and passes them on to
/// `libimagentrylink` functionality
///
/// The processor can be configured to
///
///  * Process internal links (from store entry to store entry)
///  * Process internal links with automatically creating targets
///     If an internal link is encountered, the corrosponding target must be present in the store.
///     If it is not, it will either be created or the processing fails
///  * Process external links (from store entry to URL)
///  * Process refs (from store entry to files on the filesystem and outside of the store)
///  (default: false)
///
///  # Note
///
///  There's no LinkProcessor::new() function, please use `LinkProcessor::default()`.
///
pub struct LinkProcessor {
    process_internal_links: bool,
    create_internal_targets: bool,
    process_external_links: bool,
    process_refs: bool
}

impl LinkProcessor {

    /// Switch internal link processing on/off
    ///
    /// Internal links are links which are simply `dirctory/file`, but not `/directory/file`, as
    /// beginning an id with `/` is discouraged in imag.
    pub fn process_internal_links(mut self, b: bool) -> Self {
        self.process_internal_links = b;
        self
    }

    /// Switch internal link target creation on/off
    ///
    /// If a link points to a non-existing imag entry, a `false` here will cause the processor to
    /// return an error from `process()`. A `true` setting will create the entry and then fetch it
    /// to link it to the processed entry.
    pub fn create_internal_targets(mut self, b: bool) -> Self {
        self.create_internal_targets = b;
        self
    }

    /// Switch external link processing on/off
    ///
    /// An external link must start with `https://` or `http://`.
    pub fn process_external_links(mut self, b: bool) -> Self {
        self.process_external_links = b;
        self
    }

    /// Switch ref processing on/off
    ///
    /// A Ref is to be expected beeing a link with `file::///` at the beginning.
    pub fn process_refs(mut self, b: bool) -> Self {
        self.process_refs = b;
        self
    }

    /// Process an Entry for its links
    ///
    /// # Warning
    ///
    /// When `LinkProcessor::create_internal_targets()` was called to set the setting to true, this
    /// function returns all errors returned by the Store.
    ///
    pub fn process<'a>(&self, entry: &mut Entry, store: &'a Store) -> Result<()> {
        let text = entry.to_str();
        trace!("Processing: {:?}", entry.get_location());
        extract_links(&text)
            .into_iter()
            .fold_result::<_, MarkdownError, _>(|link| {
                trace!("Processing {:?}", link);
                match LinkQualification::qualify(&link.link) {
                    LinkQualification::InternalLink => {
                        if !self.process_internal_links {
                            return Ok(());
                        }

                        let spath      = Some(store.path().clone());
                        let id         = StoreId::new(spath, PathBuf::from(&link.link))?;
                        let mut target = if self.create_internal_targets {
                            try!(store.retrieve(id))
                        } else {
                            store.get(id.clone())?
                                .ok_or(ME::from_kind(MEK::StoreGetError(id)))?
                        };

                        entry.add_internal_link(&mut target).map_err(From::from)
                    },
                    LinkQualification::ExternalLink(url) => {
                        if !self.process_external_links {
                            return Ok(());
                        }

                        entry.add_external_link(store, url).map_err(From::from)
                    },
                    LinkQualification::RefLink(url) => {
                        if !self.process_refs {
                            return Ok(());
                        }

                        let flags = RefFlags::default()
                            .with_content_hashing(false)
                            .with_permission_tracking(false);
                        trace!("URL            = {:?}", url);
                        trace!("URL.path()     = {:?}", url.path());
                        trace!("URL.host_str() = {:?}", url.host_str());
                        let path = url.host_str().unwrap_or_else(|| url.path());
                        let path = PathBuf::from(path);
                        let mut target = try!(RefStore::create(store, path, flags));

                        entry.add_internal_link(&mut target).map_err(From::from)
                    },
                    LinkQualification::Undecidable(e) => {
                        // error
                        Err(e).chain_err(|| MEK::UndecidableLinkType(link.link.clone()))
                    },
                }
            })
    }

}

/// Enum to tell what kind of link a string of text is
enum LinkQualification {
    InternalLink,
    ExternalLink(Url),
    RefLink(Url),
    Undecidable(ME),
}

impl LinkQualification {
    fn qualify(text: &str) -> LinkQualification {
        match Url::parse(text) {
            Ok(url) => {
                if url.scheme() == "file" {
                    return LinkQualification::RefLink(url)
                }

                // else we assume the following, as other stuff gets thrown out by
                // url::Url::parse() as Err(_)
                //
                // if url.scheme() == "https" || url.scheme() == "http" {
                    return LinkQualification::ExternalLink(url);
                // }
            },

            Err(e) => {
                match e {
                    ::url::ParseError::RelativeUrlWithoutBase => {
                        LinkQualification::InternalLink
                    },

                    _ => LinkQualification::Undecidable(ME::from(e)),
                }
            }
        }
    }
}

impl Default for LinkProcessor {
    fn default() -> Self {
        LinkProcessor {
            process_internal_links: true,
            create_internal_targets: false,
            process_external_links: true,
            process_refs: false
        }
    }
}

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

    use std::path::PathBuf;

    use libimagstore::store::Store;
    use libimagentrylink::internal::InternalLinker;

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

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

    #[test]
    fn test_process_no_links() {
        setup_logging();
        let store = get_store();

        let mut base = store.create(PathBuf::from("test-1")).unwrap();
        *base.get_content_mut() = format!("This is an example entry with no links");

        let update = store.update(&mut base);
        assert!(update.is_ok());

        let processor = LinkProcessor::default();

        let result = processor.process(&mut base, &store);
        assert!(result.is_ok());
    }

    #[test]
    fn test_process_one_existing_file_linked() {
        setup_logging();
        let store = get_store();

        let mut base = store.create(PathBuf::from("test-2.1")).unwrap();
        *base.get_content_mut() = format!("This is an example entry with one [link](test-2.2)");

        let update = store.update(&mut base);
        assert!(update.is_ok());

        // immediately drop as we don't need this entry right now
        let _ = store.create(PathBuf::from("test-2.2")).unwrap();

        let processor = LinkProcessor::default()
            .process_internal_links(true)
            .create_internal_targets(false)
            .process_external_links(false)
            .process_refs(false);

        let result = processor.process(&mut base, &store);
        assert!(result.is_ok(), "Should be Ok(()): {:?}", result);

        {
            let base_links = base.get_internal_links();
            assert!(base_links.is_ok());
            let base_links : Vec<_> = base_links.unwrap().collect();

            assert_eq!(1, base_links.len());
            assert_eq!("test-2.2", base_links[0].to_str().unwrap());
        }

        {
            let link = store.get(PathBuf::from("test-2.2")).unwrap().unwrap();
            let link_links = link.get_internal_links();
            assert!(link_links.is_ok());
            let link_links : Vec<_> = link_links.unwrap().collect();

            assert_eq!(1, link_links.len());
            assert_eq!("test-2.1", link_links[0].to_str().unwrap());
        }
    }

    #[test]
    fn test_process_one_existing_file_linked_faulty() {
        setup_logging();
        let store = get_store();

        let mut base = store.create(PathBuf::from("test-2.1")).unwrap();
        *base.get_content_mut() = format!("This is an example entry with one [link](/test-2.2)");

        let update = store.update(&mut base);
        assert!(update.is_ok());

        let processor = LinkProcessor::default()
            .process_internal_links(true)
            .create_internal_targets(false)
            .process_external_links(false)
            .process_refs(false);

        let result = processor.process(&mut base, &store);
        assert!(result.is_err(), "Should be Err(_), but is Ok(())");
    }

    #[test]
    fn test_process_one_nonexisting_file_linked() {
        setup_logging();
        let store = get_store();

        let mut base = store.create(PathBuf::from("test-2.1")).unwrap();
        *base.get_content_mut() = format!("This is an example entry with one [link](test-2.2)");

        let update = store.update(&mut base);
        assert!(update.is_ok());

        let processor = LinkProcessor::default()
            .process_internal_links(true)
            .create_internal_targets(true)
            .process_external_links(false)
            .process_refs(false);

        let result = processor.process(&mut base, &store);
        assert!(result.is_ok(), "Should be Ok(()): {:?}", result);

        {
            let base_links = base.get_internal_links();
            assert!(base_links.is_ok());
            let base_links : Vec<_> = base_links.unwrap().collect();

            assert_eq!(1, base_links.len());
            assert_eq!("test-2.2", base_links[0].to_str().unwrap());
        }

        {
            let link = store.get(PathBuf::from("test-2.2")).unwrap().unwrap();
            let link_links = link.get_internal_links();
            assert!(link_links.is_ok());
            let link_links : Vec<_> = link_links.unwrap().collect();

            assert_eq!(1, link_links.len());
            assert_eq!("test-2.1", link_links[0].to_str().unwrap());
        }
    }

    #[test]
    fn test_process_one_external_link() {
        setup_logging();
        let store = get_store();

        let mut base = store.create(PathBuf::from("test-5.1")).unwrap();
        *base.get_content_mut() = format!("An [example](http://example.com) is here.");

        let update = store.update(&mut base);
        assert!(update.is_ok());

        let processor = LinkProcessor::default()
            .process_internal_links(true)
            .create_internal_targets(true)
            .process_external_links(false)
            .process_refs(false);

        let result = processor.process(&mut base, &store);
        assert!(result.is_ok(), "Should be Ok(()): {:?}", result);

        // The hash of "http://example.com" processed in the `libimagentrylink` way.
        let expected_link = "links/external/9c17e047f58f9220a7008d4f18152fee4d111d14";
        {
            let base_links = base.get_internal_links();
            assert!(base_links.is_ok());
            let base_links : Vec<_> = base_links.unwrap().collect();

            assert_eq!(1, base_links.len());
            assert_eq!(expected_link, base_links[0].to_str().unwrap());
        }

        let entries = store.entries();
        assert!(entries.is_ok());
        let entries : Vec<_> = entries.unwrap().collect();

        assert_eq!(2, entries.len(), "Expected 2 links, got: {:?}", entries);

        {
            let link = store.get(PathBuf::from(expected_link)).unwrap().unwrap();
            let link_links = link.get_internal_links();
            assert!(link_links.is_ok());
            let link_links : Vec<_> = link_links.unwrap().collect();

            assert_eq!(1, link_links.len());
            assert_eq!("test-5.1", link_links[0].to_str().unwrap());
        }
    }

    #[test]
    fn test_process_one_ref() {
        setup_logging();
        let store = get_store();

        let mut base = store.create(PathBuf::from("test-5.1")).unwrap();

        // As the ref target must exist, we're using /etc/hosts here
        *base.get_content_mut() = format!("An [example ref](file:///etc/hosts) is here.");

        let update = store.update(&mut base);
        assert!(update.is_ok());

        let processor = LinkProcessor::default()
            .process_internal_links(false)
            .create_internal_targets(false)
            .process_external_links(false)
            .process_refs(true);

        let result = processor.process(&mut base, &store);
        assert!(result.is_ok(), "Should be Ok(()): {:?}", result);

        let entries = store.entries();
        assert!(entries.is_ok());
        let entries : Vec<_> = entries.unwrap().collect();

        assert_eq!(2, entries.len(), "Expected 2 links, got: {:?}", entries);
        println!("{:?}", entries);
    }

    #[test]
    fn test_process_two_refs() {
        setup_logging();
        let store = get_store();

        let mut base = store.create(PathBuf::from("test-5.1")).unwrap();

        // As the ref target must exist, we're using /etc/hosts here
        *base.get_content_mut() = format!(
            r#"An [example ref](file:///etc/hosts)
            is [here](file:///etc/group)."#
        );

        let update = store.update(&mut base);
        assert!(update.is_ok());

        let processor = LinkProcessor::default()
            .process_internal_links(false)
            .create_internal_targets(false)
            .process_external_links(false)
            .process_refs(true);

        let result = processor.process(&mut base, &store);
        assert!(result.is_ok(), "Should be Ok(()): {:?}", result);

        let entries = store.entries();
        assert!(entries.is_ok());
        let entries : Vec<_> = entries.unwrap().collect();

        assert_eq!(3, entries.len(), "Expected 3 links, got: {:?}", entries);
        println!("{:?}", entries);
    }
}

<|endoftext|>


<commit_before>// Copyright 2016 Amanieu d'Antras
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use crate::raw_fair_mutex::RawFairMutex;
use lock_api;

/// A always fair mutual exclusion primitive useful for protecting shared data
///
/// This mutex will block threads waiting for the lock to become available. The
/// mutex can also be statically initialized or created via a `new`
/// constructor. Each mutex has a type parameter which represents the data that
/// it is protecting. The data can only be accessed through the RAII guards
/// returned from `lock` and `try_lock`, which guarantees that the data is only
/// ever accessed when the mutex is locked.
///
/// This mutex is always fair.
///
/// # Differences from the standard library `FairMutex`
///
/// - No poisoning, the lock is released normally on panic.
/// - Only requires 1 byte of space, whereas the standard library boxes the
///   `FairMutex` due to platform limitations.
/// - Can be statically constructed (requires the `const_fn` nightly feature).
/// - Does not require any drop glue when dropped.
/// - Inline fast path for the uncontended case.
/// - Efficient handling of micro-contention using adaptive spinning.
/// - Allows raw locking & unlocking without a guard.
/// - Supports eventual fairness so that the mutex is fair on average.
/// - Optionally allows making the mutex fair by calling `FairMutexGuard::unlock_fair`.
///
/// # Examples
///
/// ```
/// use parking_lot::FairMutex;
/// use std::sync::{Arc, mpsc::channel};
/// use std::thread;
///
/// const N: usize = 10;
///
/// // Spawn a few threads to increment a shared variable (non-atomically), and
/// // let the main thread know once all increments are done.
/// //
/// // Here we're using an Arc to share memory among threads, and the data inside
/// // the Arc is protected with a mutex.
/// let data = Arc::new(FairMutex::new(0));
///
/// let (tx, rx) = channel();
/// for _ in 0..10 {
///     let (data, tx) = (Arc::clone(&data), tx.clone());
///     thread::spawn(move || {
///         // The shared state can only be accessed once the lock is held.
///         // Our non-atomic increment is safe because we're the only thread
///         // which can access the shared state when the lock is held.
///         let mut data = data.lock();
///         *data += 1;
///         if *data == N {
///             tx.send(()).unwrap();
///         }
///         // the lock is unlocked here when `data` goes out of scope.
///     });
/// }
///
/// rx.recv().unwrap();
/// ```
pub type FairMutex<T> = lock_api::Mutex<RawFairMutex, T>;

/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
/// dropped (falls out of scope), the lock will be unlocked.
///
/// The data protected by the mutex can be accessed through this guard via its
/// `Deref` and `DerefMut` implementations.
pub type FairMutexGuard<'a, T> = lock_api::MutexGuard<'a, RawFairMutex, T>;

/// An RAII mutex guard returned by `FairMutexGuard::map`, which can point to a
/// subfield of the protected data.
///
/// The main difference between `MappedFairMutexGuard` and `FairMutexGuard` is that the
/// former doesn't support temporarily unlocking and re-locking, since that
/// could introduce soundness issues if the locked object is modified by another
/// thread.
pub type MappedFairMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawFairMutex, T>;

#[cfg(test)]
mod tests {
    use crate::{Condvar, FairMutex};
    use std::sync::atomic::{AtomicUsize, Ordering};
    use std::sync::mpsc::channel;
    use std::sync::Arc;
    use std::thread;

    #[cfg(feature = "serde")]
    use bincode::{deserialize, serialize};

    struct Packet<T>(Arc<(FairMutex<T>, Condvar)>);

    #[derive(Eq, PartialEq, Debug)]
    struct NonCopy(i32);

    unsafe impl<T: Send> Send for Packet<T> {}
    unsafe impl<T> Sync for Packet<T> {}

    #[test]
    fn smoke() {
        let m = FairMutex::new(());
        drop(m.lock());
        drop(m.lock());
    }

    #[test]
    fn lots_and_lots() {
        const J: u32 = 1000;
        const K: u32 = 3;

        let m = Arc::new(FairMutex::new(0));

        fn inc(m: &FairMutex<u32>) {
            for _ in 0..J {
                *m.lock() += 1;
            }
        }

        let (tx, rx) = channel();
        for _ in 0..K {
            let tx2 = tx.clone();
            let m2 = m.clone();
            thread::spawn(move || {
                inc(&m2);
                tx2.send(()).unwrap();
            });
            let tx2 = tx.clone();
            let m2 = m.clone();
            thread::spawn(move || {
                inc(&m2);
                tx2.send(()).unwrap();
            });
        }

        drop(tx);
        for _ in 0..2 * K {
            rx.recv().unwrap();
        }
        assert_eq!(*m.lock(), J * K * 2);
    }

    #[test]
    fn try_lock() {
        let m = FairMutex::new(());
        *m.try_lock().unwrap() = ();
    }

    #[test]
    fn test_into_inner() {
        let m = FairMutex::new(NonCopy(10));
        assert_eq!(m.into_inner(), NonCopy(10));
    }

    #[test]
    fn test_into_inner_drop() {
        struct Foo(Arc<AtomicUsize>);
        impl Drop for Foo {
            fn drop(&mut self) {
                self.0.fetch_add(1, Ordering::SeqCst);
            }
        }
        let num_drops = Arc::new(AtomicUsize::new(0));
        let m = FairMutex::new(Foo(num_drops.clone()));
        assert_eq!(num_drops.load(Ordering::SeqCst), 0);
        {
            let _inner = m.into_inner();
            assert_eq!(num_drops.load(Ordering::SeqCst), 0);
        }
        assert_eq!(num_drops.load(Ordering::SeqCst), 1);
    }

    #[test]
    fn test_get_mut() {
        let mut m = FairMutex::new(NonCopy(10));
        *m.get_mut() = NonCopy(20);
        assert_eq!(m.into_inner(), NonCopy(20));
    }

    #[test]
    fn test_mutex_arc_condvar() {
        let packet = Packet(Arc::new((FairMutex::new(false), Condvar::new())));
        let packet2 = Packet(packet.0.clone());
        let (tx, rx) = channel();
        let _t = thread::spawn(move || {
            // wait until parent gets in
            rx.recv().unwrap();
            let &(ref lock, ref cvar) = &*packet2.0;
            let mut lock = lock.lock();
            *lock = true;
            cvar.notify_one();
        });

        let &(ref lock, ref cvar) = &*packet.0;
        let mut lock = lock.lock();
        tx.send(()).unwrap();
        assert!(!*lock);
        while !*lock {
            cvar.wait(&mut lock);
        }
    }

    #[test]
    fn test_mutex_arc_nested() {
        // Tests nested mutexes and access
        // to underlying data.
        let arc = Arc::new(FairMutex::new(1));
        let arc2 = Arc::new(FairMutex::new(arc));
        let (tx, rx) = channel();
        let _t = thread::spawn(move || {
            let lock = arc2.lock();
            let lock2 = lock.lock();
            assert_eq!(*lock2, 1);
            tx.send(()).unwrap();
        });
        rx.recv().unwrap();
    }

    #[test]
    fn test_mutex_arc_access_in_unwind() {
        let arc = Arc::new(FairMutex::new(1));
        let arc2 = arc.clone();
        let _ = thread::spawn(move || {
            struct Unwinder {
                i: Arc<FairMutex<i32>>,
            }
            impl Drop for Unwinder {
                fn drop(&mut self) {
                    *self.i.lock() += 1;
                }
            }
            let _u = Unwinder { i: arc2 };
            panic!();
        })
        .join();
        let lock = arc.lock();
        assert_eq!(*lock, 2);
    }

    #[test]
    fn test_mutex_unsized() {
        let mutex: &FairMutex<[i32]> = &FairMutex::new([1, 2, 3]);
        {
            let b = &mut *mutex.lock();
            b[0] = 4;
            b[2] = 5;
        }
        let comp: &[i32] = &[4, 2, 5];
        assert_eq!(&*mutex.lock(), comp);
    }

    #[test]
    fn test_mutexguard_sync() {
        fn sync<T: Sync>(_: T) {}

        let mutex = FairMutex::new(());
        sync(mutex.lock());
    }

    #[test]
    fn test_mutex_debug() {
        let mutex = FairMutex::new(vec![0u8, 10]);

        assert_eq!(format!("{:?}", mutex), "FairMutex { data: [0, 10] }");
        let _lock = mutex.lock();
        assert_eq!(format!("{:?}", mutex), "FairMutex { data: <locked> }");
    }

    #[cfg(feature = "serde")]
    #[test]
    fn test_serde() {
        let contents: Vec<u8> = vec![0, 1, 2];
        let mutex = FairMutex::new(contents.clone());

        let serialized = serialize(&mutex).unwrap();
        let deserialized: FairMutex<Vec<u8>> = deserialize(&serialized).unwrap();

        assert_eq!(*(mutex.lock()), *(deserialized.lock()));
        assert_eq!(contents, *(deserialized.lock()));
    }
}
<commit_msg>Remove condvar test<commit_after>// Copyright 2016 Amanieu d'Antras
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use crate::raw_fair_mutex::RawFairMutex;
use lock_api;

/// A always fair mutual exclusion primitive useful for protecting shared data
///
/// This mutex will block threads waiting for the lock to become available. The
/// mutex can also be statically initialized or created via a `new`
/// constructor. Each mutex has a type parameter which represents the data that
/// it is protecting. The data can only be accessed through the RAII guards
/// returned from `lock` and `try_lock`, which guarantees that the data is only
/// ever accessed when the mutex is locked.
///
/// This mutex is always fair.
///
/// # Differences from the standard library `FairMutex`
///
/// - No poisoning, the lock is released normally on panic.
/// - Only requires 1 byte of space, whereas the standard library boxes the
///   `FairMutex` due to platform limitations.
/// - Can be statically constructed (requires the `const_fn` nightly feature).
/// - Does not require any drop glue when dropped.
/// - Inline fast path for the uncontended case.
/// - Efficient handling of micro-contention using adaptive spinning.
/// - Allows raw locking & unlocking without a guard.
/// - Supports eventual fairness so that the mutex is fair on average.
/// - Optionally allows making the mutex fair by calling `FairMutexGuard::unlock_fair`.
///
/// # Examples
///
/// ```
/// use parking_lot::FairMutex;
/// use std::sync::{Arc, mpsc::channel};
/// use std::thread;
///
/// const N: usize = 10;
///
/// // Spawn a few threads to increment a shared variable (non-atomically), and
/// // let the main thread know once all increments are done.
/// //
/// // Here we're using an Arc to share memory among threads, and the data inside
/// // the Arc is protected with a mutex.
/// let data = Arc::new(FairMutex::new(0));
///
/// let (tx, rx) = channel();
/// for _ in 0..10 {
///     let (data, tx) = (Arc::clone(&data), tx.clone());
///     thread::spawn(move || {
///         // The shared state can only be accessed once the lock is held.
///         // Our non-atomic increment is safe because we're the only thread
///         // which can access the shared state when the lock is held.
///         let mut data = data.lock();
///         *data += 1;
///         if *data == N {
///             tx.send(()).unwrap();
///         }
///         // the lock is unlocked here when `data` goes out of scope.
///     });
/// }
///
/// rx.recv().unwrap();
/// ```
pub type FairMutex<T> = lock_api::Mutex<RawFairMutex, T>;

/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
/// dropped (falls out of scope), the lock will be unlocked.
///
/// The data protected by the mutex can be accessed through this guard via its
/// `Deref` and `DerefMut` implementations.
pub type FairMutexGuard<'a, T> = lock_api::MutexGuard<'a, RawFairMutex, T>;

/// An RAII mutex guard returned by `FairMutexGuard::map`, which can point to a
/// subfield of the protected data.
///
/// The main difference between `MappedFairMutexGuard` and `FairMutexGuard` is that the
/// former doesn't support temporarily unlocking and re-locking, since that
/// could introduce soundness issues if the locked object is modified by another
/// thread.
pub type MappedFairMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawFairMutex, T>;

#[cfg(test)]
mod tests {
    use crate::FairMutex;
    use std::sync::atomic::{AtomicUsize, Ordering};
    use std::sync::mpsc::channel;
    use std::sync::Arc;
    use std::thread;

    #[cfg(feature = "serde")]
    use bincode::{deserialize, serialize};

    #[derive(Eq, PartialEq, Debug)]
    struct NonCopy(i32);

    #[test]
    fn smoke() {
        let m = FairMutex::new(());
        drop(m.lock());
        drop(m.lock());
    }

    #[test]
    fn lots_and_lots() {
        const J: u32 = 1000;
        const K: u32 = 3;

        let m = Arc::new(FairMutex::new(0));

        fn inc(m: &FairMutex<u32>) {
            for _ in 0..J {
                *m.lock() += 1;
            }
        }

        let (tx, rx) = channel();
        for _ in 0..K {
            let tx2 = tx.clone();
            let m2 = m.clone();
            thread::spawn(move || {
                inc(&m2);
                tx2.send(()).unwrap();
            });
            let tx2 = tx.clone();
            let m2 = m.clone();
            thread::spawn(move || {
                inc(&m2);
                tx2.send(()).unwrap();
            });
        }

        drop(tx);
        for _ in 0..2 * K {
            rx.recv().unwrap();
        }
        assert_eq!(*m.lock(), J * K * 2);
    }

    #[test]
    fn try_lock() {
        let m = FairMutex::new(());
        *m.try_lock().unwrap() = ();
    }

    #[test]
    fn test_into_inner() {
        let m = FairMutex::new(NonCopy(10));
        assert_eq!(m.into_inner(), NonCopy(10));
    }

    #[test]
    fn test_into_inner_drop() {
        struct Foo(Arc<AtomicUsize>);
        impl Drop for Foo {
            fn drop(&mut self) {
                self.0.fetch_add(1, Ordering::SeqCst);
            }
        }
        let num_drops = Arc::new(AtomicUsize::new(0));
        let m = FairMutex::new(Foo(num_drops.clone()));
        assert_eq!(num_drops.load(Ordering::SeqCst), 0);
        {
            let _inner = m.into_inner();
            assert_eq!(num_drops.load(Ordering::SeqCst), 0);
        }
        assert_eq!(num_drops.load(Ordering::SeqCst), 1);
    }

    #[test]
    fn test_get_mut() {
        let mut m = FairMutex::new(NonCopy(10));
        *m.get_mut() = NonCopy(20);
        assert_eq!(m.into_inner(), NonCopy(20));
    }

    #[test]
    fn test_mutex_arc_nested() {
        // Tests nested mutexes and access
        // to underlying data.
        let arc = Arc::new(FairMutex::new(1));
        let arc2 = Arc::new(FairMutex::new(arc));
        let (tx, rx) = channel();
        let _t = thread::spawn(move || {
            let lock = arc2.lock();
            let lock2 = lock.lock();
            assert_eq!(*lock2, 1);
            tx.send(()).unwrap();
        });
        rx.recv().unwrap();
    }

    #[test]
    fn test_mutex_arc_access_in_unwind() {
        let arc = Arc::new(FairMutex::new(1));
        let arc2 = arc.clone();
        let _ = thread::spawn(move || {
            struct Unwinder {
                i: Arc<FairMutex<i32>>,
            }
            impl Drop for Unwinder {
                fn drop(&mut self) {
                    *self.i.lock() += 1;
                }
            }
            let _u = Unwinder { i: arc2 };
            panic!();
        })
        .join();
        let lock = arc.lock();
        assert_eq!(*lock, 2);
    }

    #[test]
    fn test_mutex_unsized() {
        let mutex: &FairMutex<[i32]> = &FairMutex::new([1, 2, 3]);
        {
            let b = &mut *mutex.lock();
            b[0] = 4;
            b[2] = 5;
        }
        let comp: &[i32] = &[4, 2, 5];
        assert_eq!(&*mutex.lock(), comp);
    }

    #[test]
    fn test_mutexguard_sync() {
        fn sync<T: Sync>(_: T) {}

        let mutex = FairMutex::new(());
        sync(mutex.lock());
    }

    #[test]
    fn test_mutex_debug() {
        let mutex = FairMutex::new(vec![0u8, 10]);

        assert_eq!(format!("{:?}", mutex), "FairMutex { data: [0, 10] }");
        let _lock = mutex.lock();
        assert_eq!(format!("{:?}", mutex), "FairMutex { data: <locked> }");
    }

    #[cfg(feature = "serde")]
    #[test]
    fn test_serde() {
        let contents: Vec<u8> = vec![0, 1, 2];
        let mutex = FairMutex::new(contents.clone());

        let serialized = serialize(&mutex).unwrap();
        let deserialized: FairMutex<Vec<u8>> = deserialize(&serialized).unwrap();

        assert_eq!(*(mutex.lock()), *(deserialized.lock()));
        assert_eq!(contents, *(deserialized.lock()));
    }
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::env::current_dir;
use std::ffi::OsStr;
use std::fs;
use std::io;
#[cfg(any(unix, target_os = "redox"))]
use std::os::unix::fs::PermissionsExt;
use std::path::{Component, Path, PathBuf};

use crate::walk;

pub fn path_absolute_form(path: &Path) -> io::Result<PathBuf> {
    if path.is_absolute() {
        return Ok(path.to_path_buf());
    }

    let path = path.strip_prefix(".").unwrap_or(path);
    current_dir().map(|path_buf| path_buf.join(path))
}

pub fn absolute_path(path: &Path) -> io::Result<PathBuf> {
    let path_buf = path_absolute_form(path)?;

    #[cfg(windows)]
    let path_buf = Path::new(
        path_buf
            .as_path()
            .to_string_lossy()
            .trim_start_matches(r"\\?\"),
    )
    .to_path_buf();

    Ok(path_buf)
}

// Path::is_dir() is not guaranteed to be intuitively correct for "." and ".."
// See: https://github.com/rust-lang/rust/issues/45302
pub fn is_dir(path: &Path) -> bool {
    path.is_dir() && (path.file_name().is_some() || path.canonicalize().is_ok())
}

#[cfg(any(unix, target_os = "redox"))]
pub fn is_executable(md: &fs::Metadata) -> bool {
    md.permissions().mode() & 0o111 != 0
}

#[cfg(windows)]
pub fn is_executable(_: &fs::Metadata) -> bool {
    false
}

pub fn is_empty(entry: &walk::DirEntry) -> bool {
    if let Some(file_type) = entry.file_type() {
        if file_type.is_dir() {
            if let Ok(mut entries) = fs::read_dir(entry.path()) {
                entries.next().is_none()
            } else {
                false
            }
        } else if file_type.is_file() {
            entry.metadata().map(|m| m.len() == 0).unwrap_or(false)
        } else {
            false
        }
    } else {
        false
    }
}

#[cfg(any(unix, target_os = "redox"))]
pub fn osstr_to_bytes(input: &OsStr) -> Cow<[u8]> {
    use std::os::unix::ffi::OsStrExt;
    Cow::Borrowed(input.as_bytes())
}

#[cfg(windows)]
pub fn osstr_to_bytes(input: &OsStr) -> Cow<[u8]> {
    let string = input.to_string_lossy();

    match string {
        Cow::Owned(string) => Cow::Owned(string.into_bytes()),
        Cow::Borrowed(string) => Cow::Borrowed(string.as_bytes()),
    }
}

/// Remove the `./` prefix from a path.
pub fn strip_current_dir(pathbuf: &PathBuf) -> &Path {
    let mut iter = pathbuf.components();
    let mut iter_next = iter.clone();
    if iter_next.next() == Some(Component::CurDir) {
        iter.next();
    }
    iter.as_path()
}

#[cfg(test)]
mod tests {
    #[test]
    fn strip_current_dir_basic() {
        use std::path::{Path, PathBuf};

        use super::strip_current_dir;

        assert_eq!(strip_current_dir(&PathBuf::from("./foo")), Path::new("foo"));
        assert_eq!(strip_current_dir(&PathBuf::from("foo")), Path::new("foo"));
        assert_eq!(
            strip_current_dir(&PathBuf::from("./foo/bar/baz")),
            Path::new("foo/bar/baz")
        );
        assert_eq!(
            strip_current_dir(&PathBuf::from("foo/bar/baz")),
            Path::new("foo/bar/baz")
        );
    }
}
<commit_msg>Cleanup<commit_after>use std::borrow::Cow;
use std::env::current_dir;
use std::ffi::OsStr;
use std::fs;
use std::io;
#[cfg(any(unix, target_os = "redox"))]
use std::os::unix::fs::PermissionsExt;
use std::path::{Component, Path, PathBuf};

use crate::walk;

pub fn path_absolute_form(path: &Path) -> io::Result<PathBuf> {
    if path.is_absolute() {
        return Ok(path.to_path_buf());
    }

    let path = path.strip_prefix(".").unwrap_or(path);
    current_dir().map(|path_buf| path_buf.join(path))
}

pub fn absolute_path(path: &Path) -> io::Result<PathBuf> {
    let path_buf = path_absolute_form(path)?;

    #[cfg(windows)]
    let path_buf = Path::new(
        path_buf
            .as_path()
            .to_string_lossy()
            .trim_start_matches(r"\\?\"),
    )
    .to_path_buf();

    Ok(path_buf)
}

// Path::is_dir() is not guaranteed to be intuitively correct for "." and ".."
// See: https://github.com/rust-lang/rust/issues/45302
pub fn is_dir(path: &Path) -> bool {
    path.is_dir() && (path.file_name().is_some() || path.canonicalize().is_ok())
}

#[cfg(any(unix, target_os = "redox"))]
pub fn is_executable(md: &fs::Metadata) -> bool {
    md.permissions().mode() & 0o111 != 0
}

#[cfg(windows)]
pub fn is_executable(_: &fs::Metadata) -> bool {
    false
}

pub fn is_empty(entry: &walk::DirEntry) -> bool {
    if let Some(file_type) = entry.file_type() {
        if file_type.is_dir() {
            if let Ok(mut entries) = fs::read_dir(entry.path()) {
                entries.next().is_none()
            } else {
                false
            }
        } else if file_type.is_file() {
            entry.metadata().map(|m| m.len() == 0).unwrap_or(false)
        } else {
            false
        }
    } else {
        false
    }
}

#[cfg(any(unix, target_os = "redox"))]
pub fn osstr_to_bytes(input: &OsStr) -> Cow<[u8]> {
    use std::os::unix::ffi::OsStrExt;
    Cow::Borrowed(input.as_bytes())
}

#[cfg(windows)]
pub fn osstr_to_bytes(input: &OsStr) -> Cow<[u8]> {
    let string = input.to_string_lossy();

    match string {
        Cow::Owned(string) => Cow::Owned(string.into_bytes()),
        Cow::Borrowed(string) => Cow::Borrowed(string.as_bytes()),
    }
}

/// Remove the `./` prefix from a path.
pub fn strip_current_dir(pathbuf: &PathBuf) -> &Path {
    let mut iter = pathbuf.components();
    let mut iter_next = iter.clone();
    if iter_next.next() == Some(Component::CurDir) {
        iter.next();
    }
    iter.as_path()
}

#[cfg(test)]
mod tests {
    use super::strip_current_dir;
    use std::path::{Path, PathBuf};

    #[test]
    fn strip_current_dir_basic() {
        assert_eq!(strip_current_dir(&PathBuf::from("./foo")), Path::new("foo"));
        assert_eq!(strip_current_dir(&PathBuf::from("foo")), Path::new("foo"));
        assert_eq!(
            strip_current_dir(&PathBuf::from("./foo/bar/baz")),
            Path::new("foo/bar/baz")
        );
        assert_eq!(
            strip_current_dir(&PathBuf::from("foo/bar/baz")),
            Path::new("foo/bar/baz")
        );
    }
}
<|endoftext|>


<commit_before>use game_map::{RoomId, GameMap};
use combat::CombatState;
use platform;

const PAUSE_MS: u64 = 2500;
const CHEATING_FOOD_DIVISOR: i32 = 4;
const INITIAL_STRENGTH: i32 = 100;
const INITIAL_WEALTH: i32 = 75;
const STRENGTH_PER_FOOD: i32 = 5;
const TALLY_PER_MOVE: i32 = 1;
const STRENGTH_LOSS_PER_MOVE: i32 = 5;

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum GameMode {
  AskName,
  Primary,
  Inventory,
  EatFood,
  Combat(CombatState),
  Finished,

  #[cfg(debug_assertions)]
  Debug,
}

pub struct GameState {
  pub map: GameMap,
  pub curr_mode: GameMode,
  pub shown_hint: bool,
  pub player_name: String,
  pub strength: i32,
  pub wealth: i32,
  pub food: i32,
  pub tally: i32,
  pub monsters_killed: i32,
  pub sword: bool,
  pub amulet: bool,
  pub axe: bool,
  pub suit: bool,
  pub light: bool,
  pub curr_room: RoomId,
  pub show_desc: bool,
}

impl GameState {
  pub fn new() -> Self {
    Self {
      map: GameMap::new(),
      player_name: String::from(""),
      curr_mode: GameMode::AskName,
      curr_room: RoomId::Entrance,
      shown_hint: false,
      strength: INITIAL_STRENGTH,
      wealth: INITIAL_WEALTH,
      food: 0,
      tally: 0,
      monsters_killed: 0,
      sword: false,
      amulet: false,
      axe: false,
      suit: false,
      light: false,
      show_desc: true,
    }
  }

  pub fn can_player_see(&self) -> bool {
    self.curr_room == RoomId::Entrance || self.light
  }

  pub fn set_mode(&mut self, mode: GameMode) {
    self.show_desc = true;
    self.curr_mode = mode;
  }

  pub fn print_wealth(&self) {
    print!("You have ");
    if self.wealth > 0 {
      println!("${}.", self.wealth);
    } else {
      println!("no money.");
    }
  }

  pub fn print_food(&self) {
    println!("Your provisions sack holds {} unit{} of food.",
             self.food, if self.food == 1 { "" } else { "s" });
  }

  fn get_score(&self) -> i32 {
    3  * self.tally +
    5  * self.strength +
    2  * self.wealth +
    1  * self.food +
    30 * self.monsters_killed
  }

  pub fn finish_game(&mut self) {
    self.curr_mode = GameMode::Finished;
    println!("Your score is {}.\n", self.get_score());
    println!("Farewell.");
  }

  pub fn is_finished(&self) -> bool {
    self.curr_mode == GameMode::Finished
  }

  pub fn accuse_player_of_cheating(&mut self) {
    println!("YOU HAVE TRIED TO CHEAT ME!");
    self.wealth = 0;
    self.light = false;
    self.axe = false;
    self.sword = false;
    self.food = self.food / CHEATING_FOOD_DIVISOR;
    self.amulet = false;
    self.suit = false;
    Self::pause();
  }

  pub fn pause() {
    platform::hide_prompt();
    platform::sleep(PAUSE_MS);
  }

  fn die(&mut self) {
    println!("You have died.........");
    Self::pause();
    self.finish_game();
  }

  fn tick_ask_name_mode(&mut self) {
    platform::show_prompt("What is your name, explorer? ");

    platform::read_input().map(|input| {
      if input.len() == 0 {
        println!("Pardon me?");
      } else {
        platform::hide_prompt();
        self.player_name = input;
        self.set_mode(GameMode::Primary);
      }
    });
  }

  fn tick_eat_food_mode(&mut self) {
    if self.show_desc {
      self.print_food();
      println!("");
      self.show_desc = false;
    }

    platform::show_prompt("How many do you want to eat? ");

    platform::read_input().map(|input| {
      match input.parse::<i32>() {
        Ok(amount) => {
          if amount < 0 {
            println!("GIVE ME A POSITIVE INTEGER.");
          } else if amount == 0 {
            println!("Fine, be that way.");
            Self::pause();
            self.set_mode(GameMode::Primary);
          } else if amount > self.food {
            self.accuse_player_of_cheating();
            self.set_mode(GameMode::Primary);
          } else {
            platform::hide_prompt();
            println!("After some munching, you feel stronger.");
            self.food -= amount;
            self.strength += amount * STRENGTH_PER_FOOD;
            self.set_mode(GameMode::Primary);
            Self::pause();
          }
        },
        Err(_) => {
          println!("That does not even look like a number, {}.",
                   self.player_name);
        }
      }
    });
  }

  pub fn process_move(&mut self) {
    self.tally += TALLY_PER_MOVE;
    self.strength -= STRENGTH_LOSS_PER_MOVE;
  }

  pub fn tick(&mut self) {
    if self.strength < 1 { self.die() }

    match self.curr_mode {
      GameMode::AskName => { self.tick_ask_name_mode() },
      GameMode::Primary => { self.tick_primary_mode() },
      GameMode::Inventory => { self.tick_inventory_mode() },
      GameMode::EatFood => { self.tick_eat_food_mode() },
      GameMode::Combat(state) => { self.tick_combat_mode(&state) },
      GameMode::Finished => {},

      #[cfg(debug_assertions)]
      GameMode::Debug => { self.tick_debug_mode() },
    }
  }
}
<commit_msg>Add GameState::input_callback.<commit_after>use std::rc::Rc;
use game_map::{RoomId, GameMap};
use combat::CombatState;
use platform;

const PAUSE_MS: u64 = 2500;
const CHEATING_FOOD_DIVISOR: i32 = 4;
const INITIAL_STRENGTH: i32 = 100;
const INITIAL_WEALTH: i32 = 75;
const STRENGTH_PER_FOOD: i32 = 5;
const TALLY_PER_MOVE: i32 = 1;
const STRENGTH_LOSS_PER_MOVE: i32 = 5;

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum GameMode {
  AskName,
  Primary,
  Inventory,
  EatFood,
  Combat(CombatState),
  Finished,

  #[cfg(debug_assertions)]
  Debug,
}

// We have our input callbacks have a GameState explicitly passed
// into them instead of expecting 'self' to be captured into their
// closure, because the latter leads to all kinds of lifetime
// headaches.
type InputCallback = Fn(&mut GameState, String);

pub struct GameState {
  pub map: GameMap,
  pub curr_mode: GameMode,
  pub shown_hint: bool,
  pub player_name: String,
  pub strength: i32,
  pub wealth: i32,
  pub food: i32,
  pub tally: i32,
  pub monsters_killed: i32,
  pub sword: bool,
  pub amulet: bool,
  pub axe: bool,
  pub suit: bool,
  pub light: bool,
  pub curr_room: RoomId,
  pub show_desc: bool,
  input_callback: Option<Rc<InputCallback>>,
}

impl GameState {
  pub fn new() -> Self {
    Self {
      map: GameMap::new(),
      player_name: String::from(""),
      curr_mode: GameMode::AskName,
      curr_room: RoomId::Entrance,
      shown_hint: false,
      strength: INITIAL_STRENGTH,
      wealth: INITIAL_WEALTH,
      food: 0,
      tally: 0,
      monsters_killed: 0,
      sword: false,
      amulet: false,
      axe: false,
      suit: false,
      light: false,
      show_desc: true,
      input_callback: None,
    }
  }

  pub fn read_input<F>(&mut self, cb: F)
      where F: 'static + Fn(&mut GameState, String) {
    assert!(self.input_callback.is_none(),
            "Program must not already be waiting for input");
    self.input_callback = Some(Rc::new(cb));
  }

  pub fn can_player_see(&self) -> bool {
    self.curr_room == RoomId::Entrance || self.light
  }

  pub fn set_mode(&mut self, mode: GameMode) {
    self.show_desc = true;
    self.curr_mode = mode;
  }

  pub fn print_wealth(&self) {
    print!("You have ");
    if self.wealth > 0 {
      println!("${}.", self.wealth);
    } else {
      println!("no money.");
    }
  }

  pub fn print_food(&self) {
    println!("Your provisions sack holds {} unit{} of food.",
             self.food, if self.food == 1 { "" } else { "s" });
  }

  fn get_score(&self) -> i32 {
    3  * self.tally +
    5  * self.strength +
    2  * self.wealth +
    1  * self.food +
    30 * self.monsters_killed
  }

  pub fn finish_game(&mut self) {
    self.curr_mode = GameMode::Finished;
    println!("Your score is {}.\n", self.get_score());
    println!("Farewell.");
  }

  pub fn is_finished(&self) -> bool {
    self.curr_mode == GameMode::Finished
  }

  pub fn accuse_player_of_cheating(&mut self) {
    println!("YOU HAVE TRIED TO CHEAT ME!");
    self.wealth = 0;
    self.light = false;
    self.axe = false;
    self.sword = false;
    self.food = self.food / CHEATING_FOOD_DIVISOR;
    self.amulet = false;
    self.suit = false;
    Self::pause();
  }

  pub fn pause() {
    platform::hide_prompt();
    platform::sleep(PAUSE_MS);
  }

  fn die(&mut self) {
    println!("You have died.........");
    Self::pause();
    self.finish_game();
  }

  fn tick_ask_name_mode(&mut self) {
    platform::show_prompt("What is your name, explorer? ");

    self.read_input(|state, input| {
      if input.len() == 0 {
        println!("Pardon me?");
      } else {
        platform::hide_prompt();
        state.player_name = input;
        state.set_mode(GameMode::Primary);
      }
    });
  }

  fn tick_eat_food_mode(&mut self) {
    if self.show_desc {
      self.print_food();
      println!("");
      self.show_desc = false;
    }

    platform::show_prompt("How many do you want to eat? ");

    platform::read_input().map(|input| {
      match input.parse::<i32>() {
        Ok(amount) => {
          if amount < 0 {
            println!("GIVE ME A POSITIVE INTEGER.");
          } else if amount == 0 {
            println!("Fine, be that way.");
            Self::pause();
            self.set_mode(GameMode::Primary);
          } else if amount > self.food {
            self.accuse_player_of_cheating();
            self.set_mode(GameMode::Primary);
          } else {
            platform::hide_prompt();
            println!("After some munching, you feel stronger.");
            self.food -= amount;
            self.strength += amount * STRENGTH_PER_FOOD;
            self.set_mode(GameMode::Primary);
            Self::pause();
          }
        },
        Err(_) => {
          println!("That does not even look like a number, {}.",
                   self.player_name);
        }
      }
    });
  }

  pub fn process_move(&mut self) {
    self.tally += TALLY_PER_MOVE;
    self.strength -= STRENGTH_LOSS_PER_MOVE;
  }

  pub fn tick(&mut self) {
    let mut input_cb: Option<Rc<InputCallback>> = None;

    if let Some(ref cb) = self.input_callback {
      input_cb = Some(cb.clone());
    }

    if let Some(ref cb) = input_cb {
      match platform::read_input() {
        Some(input) => {
          self.input_callback = None;
          cb(self, input);
        },
        None => {
          // We're probably running in the browser and there's currently
          // no input to process, so just return.
          return;
        }
      }
    }

    if self.strength < 1 { self.die() }

    match self.curr_mode {
      GameMode::AskName => { self.tick_ask_name_mode() },
      GameMode::Primary => { self.tick_primary_mode() },
      GameMode::Inventory => { self.tick_inventory_mode() },
      GameMode::EatFood => { self.tick_eat_food_mode() },
      GameMode::Combat(state) => { self.tick_combat_mode(&state) },
      GameMode::Finished => {},

      #[cfg(debug_assertions)]
      GameMode::Debug => { self.tick_debug_mode() },
    }
  }
}
<|endoftext|>


<commit_before>// Copyright (C) 2017, 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: MPL-2.0
//

use core::ops::Index;
use std::collections::btree_map::{Entry, Keys};
use std::collections::BTreeMap;
use std::io;
use std::str::FromStr;

#[derive(Debug, Serialize, PartialEq, Default)]
pub struct MetadataValue(BTreeMap<String, Vec<String>>);

impl FromStr for MetadataValue {
    type Err = io::Error;

    fn from_str(s: &str) -> Result<MetadataValue, io::Error> {
        let buf = s.to_string();

        let mut values = Vec::new();
        for line in buf.lines() {
            let v: Vec<_> = line.splitn(2, '=').map(|v| v.trim().to_string()).collect();
            if v.len() != 2 {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!(
                        "Invalid format for value '{:?}'. \
                         An <key>=<value> output is \
                         expected",
                        v
                    ),
                ));
            }

            values.push((v[0].clone(), v[1].clone()));
        }
        values.sort();

        let mut mv = MetadataValue::default();
        for (k, v) in values {
            // replace to use .and_modify once #44733 is closed. Code
            // is below:
            //   mv.entry(k)
            //     .and_modify(|e| e.push(v.clone()))
            //     .or_insert(vec![v]);
            match mv.entry(k) {
                Entry::Occupied(entry) => {
                    entry.into_mut().push(v.clone());
                }
                Entry::Vacant(entry) => {
                    entry.insert(vec![v]);
                }
            }
        }

        Ok(mv)
    }
}

impl MetadataValue {
    pub fn entry(&mut self, key: String) -> Entry<String, Vec<String>> {
        self.0.entry(key)
    }

    pub fn keys(&self) -> Keys<String, Vec<String>> {
        self.0.keys()
    }

    pub fn is_empty(&self) -> bool {
        self.0.len() == 0
    }

    pub fn len(&self) -> usize {
        self.0.len()
    }
}

impl<'a> Index<&'a str> for MetadataValue {
    type Output = Vec<String>;

    #[inline]
    fn index(&self, key: &str) -> &Vec<String> {
        self.0.get(key).expect("no entry found for key")
    }
}

#[test]
fn valid() {
    let v = MetadataValue::from_str("key1=v1\nkey=b\nnv=\nkey=a").unwrap();

    assert_eq!(v.keys().len(), 3);
    assert_eq!(v.keys().collect::<Vec<_>>(), ["key", "key1", "nv"]);
    assert_eq!(v["key1"], ["v1"]);
    assert_eq!(v["key"], ["a", "b"]);
    assert_eq!(v["nv"], [""]);
}

#[test]
fn invalid() {
    assert!(MetadataValue::from_str("\n").is_err());
    assert!(MetadataValue::from_str("key").is_err());
}
<commit_msg>firmware: Avoid making a copy of metadata value<commit_after>// Copyright (C) 2017, 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: MPL-2.0
//

use core::ops::Index;
use std::collections::btree_map::{Entry, Keys};
use std::collections::BTreeMap;
use std::io;
use std::str::FromStr;

#[derive(Debug, Serialize, PartialEq, Default)]
pub struct MetadataValue(BTreeMap<String, Vec<String>>);

impl FromStr for MetadataValue {
    type Err = io::Error;

    fn from_str(s: &str) -> Result<MetadataValue, io::Error> {
        let mut values = Vec::new();
        for line in s.lines() {
            let v: Vec<_> = line.splitn(2, '=').map(|v| v.trim().to_string()).collect();
            if v.len() != 2 {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!(
                        "Invalid format for value '{:?}'. \
                         An <key>=<value> output is \
                         expected",
                        v
                    ),
                ));
            }

            values.push((v[0].clone(), v[1].clone()));
        }
        values.sort();

        let mut mv = MetadataValue::default();
        for (k, v) in values {
            // replace to use .and_modify once #44733 is closed. Code
            // is below:
            //   mv.entry(k)
            //     .and_modify(|e| e.push(v.clone()))
            //     .or_insert(vec![v]);
            match mv.entry(k) {
                Entry::Occupied(entry) => {
                    entry.into_mut().push(v.clone());
                }
                Entry::Vacant(entry) => {
                    entry.insert(vec![v]);
                }
            }
        }

        Ok(mv)
    }
}

impl MetadataValue {
    pub fn entry(&mut self, key: String) -> Entry<String, Vec<String>> {
        self.0.entry(key)
    }

    pub fn keys(&self) -> Keys<String, Vec<String>> {
        self.0.keys()
    }

    pub fn is_empty(&self) -> bool {
        self.0.len() == 0
    }

    pub fn len(&self) -> usize {
        self.0.len()
    }
}

impl<'a> Index<&'a str> for MetadataValue {
    type Output = Vec<String>;

    #[inline]
    fn index(&self, key: &str) -> &Vec<String> {
        self.0.get(key).expect("no entry found for key")
    }
}

#[test]
fn valid() {
    let v = MetadataValue::from_str("key1=v1\nkey=b\nnv=\nkey=a").unwrap();

    assert_eq!(v.keys().len(), 3);
    assert_eq!(v.keys().collect::<Vec<_>>(), ["key", "key1", "nv"]);
    assert_eq!(v["key1"], ["v1"]);
    assert_eq!(v["key"], ["a", "b"]);
    assert_eq!(v["nv"], [""]);
}

#[test]
fn invalid() {
    assert!(MetadataValue::from_str("\n").is_err());
    assert!(MetadataValue::from_str("key").is_err());
}
<|endoftext|>


<commit_before>//! Contains information for keybindings.

use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::hash::{Hash, Hasher};

use rustwlc::xkb::{Keysym, NameFlags};
use rustwlc::types::*; // Need * for bitflags...

use super::layout::tree;
use super::lua;

/// Register default keypresses to a map
macro_rules! register_defaults {
    ( $map:expr; $($func:expr, $press:expr);+ ) => {
        $(
            let _ = $map.insert($press, Arc::new(Box::new($func)));
            )+
    }
}

/// Generate switch_workspace methods and register them in $map
macro_rules! gen_switch_workspace {
    ($map:expr; $($b:ident, $n:expr);+) => {
        $(fn $b() {
            trace!("Switching to workspace {}", $n);
            tree::switch_workspace(&$n.to_string())
                .expect("Could not switch to a work-space");
                }
          register_defaults!( $map; $b, keypress!("Alt", $n) );
          )+
    };
}

lazy_static! {
    static ref BINDINGS: RwLock<HashMap<KeyPress, KeyEvent>> = {
        let mut map = HashMap::<KeyPress, KeyEvent>::new();

        gen_switch_workspace!(map; switch_workspace_1, "1";
                              switch_workspace_2, "2";
                              switch_workspace_3, "3";
                              switch_workspace_4, "4";
                              switch_workspace_5, "5";
                              switch_workspace_6, "6";
                              switch_workspace_7, "7";
                              switch_workspace_8, "8";
                              switch_workspace_9, "9";
                              switch_workspace_0, "0");

        register_defaults! {
            map;
            quit_fn, keypress!("Alt", "Escape");
            terminal_fn, keypress!("Alt", "Return");
            dmenu_fn, keypress!("Alt", "d");
            pointer_fn, keypress!("Alt", "p")
        }

        RwLock::new(map)
    };
}

fn terminal_fn() {
    use std::process::Command;
    Command::new("sh")
        .arg("-c")
        .arg("weston-terminal")
        .spawn().unwrap();
}

fn dmenu_fn() {
    use std::process::Command;
    Command::new("sh")
        .arg("-c")
        .arg("dmenu_run")
        .spawn().unwrap();
}

fn pointer_fn() {
    use lua::LuaQuery;
    let code = "if wm == nil then print('wm table does not exist')\n\
                elseif wm.pointer == nil then print('wm.pointer table does not exist')\n\
                else\n\
                print('get_position is a func, preparing execution')
                local x, y = wm.pointer.get_position()\n\
                print('The cursor is at ' .. x .. ', ' .. y)\n\
                end".to_string();
    lua::send(LuaQuery::Execute(code)).unwrap();
}

fn quit_fn() {
    info!("handler: Esc keypress!");
    ::rustwlc::terminate();
}

#[derive(Eq, PartialEq, Clone, Debug)]
pub struct KeyPress {
    modifiers: KeyMod,
    keys: Vec<Keysym>
}

/// Parses a KeyMod from key names.
pub fn keymod_from_names(keys: Vec<&str>) -> Result<KeyMod, String> {
    let mut result = KeyMod::empty();
    for key in keys {
        match key.to_lowercase().as_str() {
            "shift"            => result = result | MOD_SHIFT,
            "control" | "ctrl" => result = result | MOD_CTRL,
            "alt"              => result = result | MOD_ALT,
            "mod2"             => result = result | MOD_MOD2,
            "mod3"             => result = result | MOD_MOD3,
            "mod4" | "super" | "logo" => result = result | MOD_MOD4,
            "mod5" | "5mod5me" => result = result | MOD_MOD5,
            err => return Err(format!("Invalid modifier: {}", err))
        }
    }
    return Ok(result);
}

impl KeyPress {
    /// Creates a new KeyPress struct from a list of modifier and key names
    pub fn from_key_names(mods: Vec<&str>, keys: Vec<&str>) -> Result<KeyPress, String> {
        keymod_from_names(mods).and_then(|mods| {
            let mut syms: Vec<Keysym> = Vec::with_capacity(keys.len());
            for name in keys {
                // Parse a keysym for each given key
                if let Some(sym) = Keysym::from_name(name.to_string(),
                                                     NameFlags::None) {
                    syms.push(sym);
                }
                // If lowercase cannot be parsed, try case insensitive
                else if let Some(sym) = Keysym::from_name(name.to_string(),
                                                          NameFlags::CaseInsensitive) {
                    syms.push(sym);
                }
                else {
                    return Err(format!("Invalid key: {}", name));
                }
            }
            // Sort and dedup to make sure hashes are the same
            syms.sort_by_key(|s| s.get_code());
            syms.dedup();
            return Ok(KeyPress { modifiers: mods, keys: syms });
        })
    }

    /// Creates a KeyPress from keys that are pressed at the moment
    pub fn new(mods: KeyMod, mut keys: Vec<Keysym>) -> KeyPress {
        // Sort and dedup to make sure hashes are the same
        keys.sort_by_key(|k| k.get_code());
        keys.dedup();

        KeyPress { modifiers: mods, keys: keys }
    }
}

impl Hash for KeyPress {
    fn hash<H: Hasher>(&self, hasher: &mut H) {
        hasher.write_u32(self.modifiers.bits());
        for ref sym in &self.keys {
            hasher.write_u32(sym.get_code());
        }
    }
}

/// The type of function a key press handler is.
pub type KeyEvent = Arc<Box<Fn() + Send + Sync>>;

/// Get a key mapping from the list.
pub fn get(key: &KeyPress) -> Option<KeyEvent> {
    let bindings = BINDINGS.read().unwrap();
    match bindings.get(key) {
        None => None,
        Some(val) => Some(val.clone())
    }
}

/// Register a new set of key mappings
#[allow(dead_code)]
pub fn register(values: Vec<(KeyPress, KeyEvent)>) {
    let mut bindings = BINDINGS.write().unwrap();
    for value in values {
        bindings.insert(value.0, value.1);
    }
}
<commit_msg>Remove unwraps from keys.rs<commit_after>//! Contains information for keybindings.

use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::hash::{Hash, Hasher};

use rustwlc::xkb::{Keysym, NameFlags};
use rustwlc::types::*; // Need * for bitflags...

use super::layout::tree;
use super::lua;

/// Register default keypresses to a map
macro_rules! register_defaults {
    ( $map:expr; $($func:expr, $press:expr);+ ) => {
        $(
            let _ = $map.insert($press, Arc::new(Box::new($func)));
            )+
    }
}

/// Generate switch_workspace methods and register them in $map
macro_rules! gen_switch_workspace {
    ($map:expr; $($b:ident, $n:expr);+) => {
        $(fn $b() {
            trace!("Switching to workspace {}", $n);
            tree::switch_workspace(&$n.to_string())
                .expect("Could not switch to a work-space");
                }
          register_defaults!( $map; $b, keypress!("Alt", $n) );
          )+
    };
}

lazy_static! {
    static ref BINDINGS: RwLock<HashMap<KeyPress, KeyEvent>> = {
        let mut map = HashMap::<KeyPress, KeyEvent>::new();

        gen_switch_workspace!(map; switch_workspace_1, "1";
                              switch_workspace_2, "2";
                              switch_workspace_3, "3";
                              switch_workspace_4, "4";
                              switch_workspace_5, "5";
                              switch_workspace_6, "6";
                              switch_workspace_7, "7";
                              switch_workspace_8, "8";
                              switch_workspace_9, "9";
                              switch_workspace_0, "0");

        register_defaults! {
            map;
            quit_fn, keypress!("Alt", "Escape");
            terminal_fn, keypress!("Alt", "Return");
            dmenu_fn, keypress!("Alt", "d");
            pointer_fn, keypress!("Alt", "p")
        }

        RwLock::new(map)
    };
}

fn terminal_fn() {
    use std::process::Command;
    Command::new("sh")
        .arg("-c")
        .arg("weston-terminal")
        .spawn().expect("Error launching terminal");
}

fn dmenu_fn() {
    use std::process::Command;
    Command::new("sh")
        .arg("-c")
        .arg("dmenu_run")
        .spawn().expect("Error launching terminal");
}

fn pointer_fn() {
    use lua::LuaQuery;
    let code = "if wm == nil then print('wm table does not exist')\n\
                elseif wm.pointer == nil then print('wm.pointer table does not exist')\n\
                else\n\
                print('get_position is a func, preparing execution')
                local x, y = wm.pointer.get_position()\n\
                print('The cursor is at ' .. x .. ', ' .. y)\n\
                end".to_string();
    lua::send(LuaQuery::Execute(code))
        .expect("Error telling Lua to get pointer coords");
}

fn quit_fn() {
    info!("handler: Esc keypress!");
    ::rustwlc::terminate();
}

#[derive(Eq, PartialEq, Clone, Debug)]
pub struct KeyPress {
    modifiers: KeyMod,
    keys: Vec<Keysym>
}

/// Parses a KeyMod from key names.
pub fn keymod_from_names(keys: Vec<&str>) -> Result<KeyMod, String> {
    let mut result = KeyMod::empty();
    for key in keys {
        match key.to_lowercase().as_str() {
            "shift"            => result = result | MOD_SHIFT,
            "control" | "ctrl" => result = result | MOD_CTRL,
            "alt"              => result = result | MOD_ALT,
            "mod2"             => result = result | MOD_MOD2,
            "mod3"             => result = result | MOD_MOD3,
            "mod4" | "super" | "logo" => result = result | MOD_MOD4,
            "mod5" | "5mod5me" => result = result | MOD_MOD5,
            err => return Err(format!("Invalid modifier: {}", err))
        }
    }
    return Ok(result);
}

impl KeyPress {
    /// Creates a new KeyPress struct from a list of modifier and key names
    pub fn from_key_names(mods: Vec<&str>, keys: Vec<&str>) -> Result<KeyPress, String> {
        keymod_from_names(mods).and_then(|mods| {
            let mut syms: Vec<Keysym> = Vec::with_capacity(keys.len());
            for name in keys {
                // Parse a keysym for each given key
                if let Some(sym) = Keysym::from_name(name.to_string(),
                                                     NameFlags::None) {
                    syms.push(sym);
                }
                // If lowercase cannot be parsed, try case insensitive
                else if let Some(sym) = Keysym::from_name(name.to_string(),
                                                          NameFlags::CaseInsensitive) {
                    syms.push(sym);
                }
                else {
                    return Err(format!("Invalid key: {}", name));
                }
            }
            // Sort and dedup to make sure hashes are the same
            syms.sort_by_key(|s| s.get_code());
            syms.dedup();
            return Ok(KeyPress { modifiers: mods, keys: syms });
        })
    }

    /// Creates a KeyPress from keys that are pressed at the moment
    pub fn new(mods: KeyMod, mut keys: Vec<Keysym>) -> KeyPress {
        // Sort and dedup to make sure hashes are the same
        keys.sort_by_key(|k| k.get_code());
        keys.dedup();

        KeyPress { modifiers: mods, keys: keys }
    }
}

impl Hash for KeyPress {
    fn hash<H: Hasher>(&self, hasher: &mut H) {
        hasher.write_u32(self.modifiers.bits());
        for ref sym in &self.keys {
            hasher.write_u32(sym.get_code());
        }
    }
}

/// The type of function a key press handler is.
pub type KeyEvent = Arc<Box<Fn() + Send + Sync>>;

/// Get a key mapping from the list.
pub fn get(key: &KeyPress) -> Option<KeyEvent> {
    let bindings = BINDINGS.read()
        .expect("Keybindings/get: unable to lock keybindings");
    match bindings.get(key) {
        None => None,
        Some(val) => Some(val.clone())
    }
}

/// Register a new set of key mappings
#[allow(dead_code)]
pub fn register(values: Vec<(KeyPress, KeyEvent)>) {
    let mut bindings = BINDINGS.write()
        .expect("Keybindings/register: unable to lock keybindings");
    for value in values {
        bindings.insert(value.0, value.1);
    }
}
<|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 dom::bindings::utils::{DOMString, ErrorResult, null_str_as_empty};
use dom::htmlelement::HTMLElement;
use dom::node::{ScriptView, AbstractNode};
use extra::url::Url;
use servo_util::geometry::to_px;
use layout_interface::{ContentBoxQuery, ContentBoxResponse};
use servo_net::image_cache_task;
use servo_net::image_cache_task::ImageCacheTask;
use servo_util::url::make_url;
use servo_util::tree::ElementLike;

pub struct HTMLImageElement {
    htmlelement: HTMLElement,
    image: Option<Url>,
}

impl HTMLImageElement {
    /// Makes the local `image` member match the status of the `src` attribute and starts
    /// prefetching the image. This method must be called after `src` is changed.
    pub fn update_image(&mut self, image_cache: ImageCacheTask, url: Option<Url>) {
        let elem = &mut self.htmlelement.element;
        let src_opt = elem.get_attr("src").map(|x| x.to_str());
        match src_opt {
            None => {}
            Some(src) => {
                let img_url = make_url(src, url);
                self.image = Some(img_url.clone());

                // inform the image cache to load this, but don't store a
                // handle.
                //
                // TODO (Issue #84): don't prefetch if we are within a
                // <noscript> tag.
                image_cache.send(image_cache_task::Prefetch(img_url));
            }
        }
    }

    pub fn AfterSetAttr(&mut self, name: &DOMString, _value: &DOMString) {
        let name = null_str_as_empty(name);
        if "src" == name {
            let doc = self.htmlelement.element.node.owner_doc;
            do doc.with_base |doc| {
                let window = doc.window;
                let url = window.page.url.map(|&(ref url, _)| url.clone());
                self.update_image(window.image_cache_task.clone(), url);
            }
        }
    }

    pub fn Alt(&self) -> DOMString {
        None
    }

    pub fn SetAlt(&mut self, _alt: &DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn Src(&self, _abstract_self: AbstractNode<ScriptView>) -> DOMString {
        None
    }

    pub fn SetSrc(&mut self,
                  abstract_self: AbstractNode<ScriptView>,
                  src: &DOMString) -> ErrorResult {
        let node = &mut self.htmlelement.element;
        node.set_attr(abstract_self,
                      &Some(~"src"),
                      &Some(null_str_as_empty(src)));
        Ok(())
    }

    pub fn CrossOrigin(&self) -> DOMString {
        None
    }

    pub fn SetCrossOrigin(&mut self, _cross_origin: &DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn UseMap(&self) -> DOMString {
        None
    }

    pub fn SetUseMap(&mut self, _use_map: &DOMString) -> ErrorResult {
        Ok(())
    }

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

    pub fn SetIsMap(&self, _is_map: bool) -> ErrorResult {
        Ok(())
    }

    pub fn Width(&self, abstract_self: AbstractNode<ScriptView>) -> u32 {
        let node = &self.htmlelement.element.node;
        let page = node.owner_doc.with_base(|doc| doc.window).page;
        let (port, chan) = stream();
        match page.query_layout(ContentBoxQuery(abstract_self, chan), port) {
            ContentBoxResponse(rect) => {
                to_px(rect.size.width) as u32
            }
        }
    }

    pub fn SetWidth(&mut self,
                    abstract_self: AbstractNode<ScriptView>,
                    width: u32) -> ErrorResult {
        let node = &mut self.htmlelement.element;
        node.set_attr(abstract_self,
                      &Some(~"width"),
                      &Some(width.to_str()));
        Ok(())
    }

    pub fn Height(&self, abstract_self: AbstractNode<ScriptView>) -> u32 {
        let node = &self.htmlelement.element.node;
        let page = node.owner_doc.with_base(|doc| doc.window).page;
        let (port, chan) = stream();
        match page.query_layout(ContentBoxQuery(abstract_self, chan), port) {
            ContentBoxResponse(rect) => {
                to_px(rect.size.height) as u32
            }
        }
    }

    pub fn SetHeight(&mut self,
                     abstract_self: AbstractNode<ScriptView>,
                     height: u32) -> ErrorResult {
        let node = &mut self.htmlelement.element;
        node.set_attr(abstract_self,
                      &Some(~"height"),
                      &Some(height.to_str()));
        Ok(())
    }

    pub fn NaturalWidth(&self) -> u32 {
        0
    }

    pub fn NaturalHeight(&self) -> u32 {
        0
    }

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

    pub fn Name(&self) -> DOMString {
        None
    }

    pub fn SetName(&mut self, _name: &DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn Align(&self) -> DOMString {
        None
    }

    pub fn SetAlign(&mut self, _align: &DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn Hspace(&self) -> u32 {
        0
    }

    pub fn SetHspace(&mut self, _hspace: u32) -> ErrorResult {
        Ok(())
    }

    pub fn Vspace(&self) -> u32 {
        0
    }

    pub fn SetVspace(&mut self, _vspace: u32) -> ErrorResult {
        Ok(())
    }

    pub fn LongDesc(&self) -> DOMString {
        None
    }

    pub fn SetLongDesc(&mut self, _longdesc: &DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn Border(&self) -> DOMString {
        None
    }

    pub fn SetBorder(&mut self, _border: &DOMString) -> ErrorResult {
        Ok(())
    }
}
<commit_msg>Failing for a blank line before the license, really?<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 dom::bindings::utils::{DOMString, ErrorResult, null_str_as_empty};
use dom::htmlelement::HTMLElement;
use dom::node::{ScriptView, AbstractNode};
use extra::url::Url;
use servo_util::geometry::to_px;
use layout_interface::{ContentBoxQuery, ContentBoxResponse};
use servo_net::image_cache_task;
use servo_net::image_cache_task::ImageCacheTask;
use servo_util::url::make_url;
use servo_util::tree::ElementLike;

pub struct HTMLImageElement {
    htmlelement: HTMLElement,
    image: Option<Url>,
}

impl HTMLImageElement {
    /// Makes the local `image` member match the status of the `src` attribute and starts
    /// prefetching the image. This method must be called after `src` is changed.
    pub fn update_image(&mut self, image_cache: ImageCacheTask, url: Option<Url>) {
        let elem = &mut self.htmlelement.element;
        let src_opt = elem.get_attr("src").map(|x| x.to_str());
        match src_opt {
            None => {}
            Some(src) => {
                let img_url = make_url(src, url);
                self.image = Some(img_url.clone());

                // inform the image cache to load this, but don't store a
                // handle.
                //
                // TODO (Issue #84): don't prefetch if we are within a
                // <noscript> tag.
                image_cache.send(image_cache_task::Prefetch(img_url));
            }
        }
    }

    pub fn AfterSetAttr(&mut self, name: &DOMString, _value: &DOMString) {
        let name = null_str_as_empty(name);
        if "src" == name {
            let doc = self.htmlelement.element.node.owner_doc;
            do doc.with_base |doc| {
                let window = doc.window;
                let url = window.page.url.map(|&(ref url, _)| url.clone());
                self.update_image(window.image_cache_task.clone(), url);
            }
        }
    }

    pub fn Alt(&self) -> DOMString {
        None
    }

    pub fn SetAlt(&mut self, _alt: &DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn Src(&self, _abstract_self: AbstractNode<ScriptView>) -> DOMString {
        None
    }

    pub fn SetSrc(&mut self,
                  abstract_self: AbstractNode<ScriptView>,
                  src: &DOMString) -> ErrorResult {
        let node = &mut self.htmlelement.element;
        node.set_attr(abstract_self,
                      &Some(~"src"),
                      &Some(null_str_as_empty(src)));
        Ok(())
    }

    pub fn CrossOrigin(&self) -> DOMString {
        None
    }

    pub fn SetCrossOrigin(&mut self, _cross_origin: &DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn UseMap(&self) -> DOMString {
        None
    }

    pub fn SetUseMap(&mut self, _use_map: &DOMString) -> ErrorResult {
        Ok(())
    }

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

    pub fn SetIsMap(&self, _is_map: bool) -> ErrorResult {
        Ok(())
    }

    pub fn Width(&self, abstract_self: AbstractNode<ScriptView>) -> u32 {
        let node = &self.htmlelement.element.node;
        let page = node.owner_doc.with_base(|doc| doc.window).page;
        let (port, chan) = stream();
        match page.query_layout(ContentBoxQuery(abstract_self, chan), port) {
            ContentBoxResponse(rect) => {
                to_px(rect.size.width) as u32
            }
        }
    }

    pub fn SetWidth(&mut self,
                    abstract_self: AbstractNode<ScriptView>,
                    width: u32) -> ErrorResult {
        let node = &mut self.htmlelement.element;
        node.set_attr(abstract_self,
                      &Some(~"width"),
                      &Some(width.to_str()));
        Ok(())
    }

    pub fn Height(&self, abstract_self: AbstractNode<ScriptView>) -> u32 {
        let node = &self.htmlelement.element.node;
        let page = node.owner_doc.with_base(|doc| doc.window).page;
        let (port, chan) = stream();
        match page.query_layout(ContentBoxQuery(abstract_self, chan), port) {
            ContentBoxResponse(rect) => {
                to_px(rect.size.height) as u32
            }
        }
    }

    pub fn SetHeight(&mut self,
                     abstract_self: AbstractNode<ScriptView>,
                     height: u32) -> ErrorResult {
        let node = &mut self.htmlelement.element;
        node.set_attr(abstract_self,
                      &Some(~"height"),
                      &Some(height.to_str()));
        Ok(())
    }

    pub fn NaturalWidth(&self) -> u32 {
        0
    }

    pub fn NaturalHeight(&self) -> u32 {
        0
    }

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

    pub fn Name(&self) -> DOMString {
        None
    }

    pub fn SetName(&mut self, _name: &DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn Align(&self) -> DOMString {
        None
    }

    pub fn SetAlign(&mut self, _align: &DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn Hspace(&self) -> u32 {
        0
    }

    pub fn SetHspace(&mut self, _hspace: u32) -> ErrorResult {
        Ok(())
    }

    pub fn Vspace(&self) -> u32 {
        0
    }

    pub fn SetVspace(&mut self, _vspace: u32) -> ErrorResult {
        Ok(())
    }

    pub fn LongDesc(&self) -> DOMString {
        None
    }

    pub fn SetLongDesc(&mut self, _longdesc: &DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn Border(&self) -> DOMString {
        None
    }

    pub fn SetBorder(&mut self, _border: &DOMString) -> ErrorResult {
        Ok(())
    }
}
<|endoftext|>