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

use std::ffi::OsStr;
use std::fmt;
use std::fs;
use std::io;
use std::io::Read;
use std::path::PathBuf;
use std::str::FromStr;

use sha2::{Digest, Sha256};

use serde::{de, Deserialize, Deserializer};

use super::traits::{Error, Resource, ResourceState};

use util::PathExt;

/// A file resource. This can actually be a file *or* directory.
///
/// TODO: Split the directory portion out into a "glob" resource whose state
/// changes when the list of matched files changes.
#[derive(Serialize, Ord, PartialOrd, Eq, PartialEq, Hash, Clone)]
pub struct FilePath {
    path: PathBuf,
}

impl FilePath {
    pub fn new(path: PathBuf) -> FilePath {
        FilePath { path: path.normalize() }
    }

    /// Assumes this resource is a regular file and returns its checksum.
    fn file_state(&self) -> Result<ResourceState, Error> {
        let mut hasher = Sha256::default();

        let mut f = match fs::File::open(&self.path) {
            Ok(f) => Ok(f),
            Err(err) => {
                match err.kind() {
                    io::ErrorKind::NotFound => {
                        return Ok(ResourceState::Missing);
                    }
                    _ => Err(err),
                }
            }
        }?;

        const BUF_SIZE: usize = 16384;

        let mut buf = [0u8; BUF_SIZE];

        loop {
            let n = f.read(&mut buf)?;

            if n == 0 || n < BUF_SIZE {
                break;
            }

            hasher.input(&buf[0..n]);
        }

        Ok(ResourceState::Checksum(hasher.result()))
    }

    /// Assumes this resource is a directory and returns the checksum of its
    /// file contents.
    fn dir_state(&self) -> Result<ResourceState, Error> {
        let mut hasher = Sha256::default();

        let mut names = vec![];

        for entry in fs::read_dir(&self.path)? {
            names.push(entry?.file_name());
        }

        // The order in which files are listed is not guaranteed to be sorted.
        // Whether or not it is sorted depends on the file system
        // implementation. Thus, we sort them to eliminate that potential
        // source of non-determinism.
        names.sort();

        for name in names {
            if let Some(name) = name.to_str() {
                hasher.input(name.as_bytes());
            }
        }

        Ok(ResourceState::Checksum(hasher.result()))
    }
}

impl<'a, T: ?Sized + AsRef<OsStr>> From<&'a T> for FilePath {
    fn from(s: &'a T) -> FilePath {
        FilePath { path: PathBuf::from(s.as_ref()), }
    }
}

impl FromStr for FilePath {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(FilePath::new(PathBuf::from(s)))
    }
}

impl fmt::Display for FilePath {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.path)
    }
}

impl fmt::Debug for FilePath {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.path)
    }
}

// Derserialize a `FilePath` from a string.
impl<'de> Deserialize<'de> for FilePath {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: Deserializer<'de>
    {
        let s = String::deserialize(deserializer)?;
        FromStr::from_str(&s).map_err(de::Error::custom)
    }
}

impl Resource for FilePath {
    /// If a file, the checksum is of the contents of the file. If a directory,
    /// the checksum is of the sorted list of directory entries. Thus, if a file
    /// is added or removed from a directory, the checksum changes.
    fn state(&self) -> Result<ResourceState, Error> {
        if self.path.is_dir() {
            self.dir_state()
        } else {
            // Assume its a file even if its not. It'll error out if there are
            // problems reading it.
            self.file_state()
        }
    }

    /// If a file, simply deletes the file. If a directory, deletes the
    /// directory if it is empty.
    fn delete(&self) -> Result<(), Error> {
        let metadata = match fs::metadata(&self.path) {
            Ok(metadata) => Ok(metadata),
            Err(err) => {
                match err.kind() {
                    io::ErrorKind::NotFound => {
                        return Ok(());
                    }
                    _ => Err(err),
                }
            }
        }?;

        if metadata.is_dir() {
            // Ignore errors for directory deletion. It's not usually a problem
            // if a directory fails to get deleted. Directory deletion can fail
            // for a number of reasons:
            //  - A user may have created a file inside of it.
            //  - An untracked output may have been created inside of it.
            //  - On Windows, someone may have a lock on the directory.
            let _ = fs::remove_dir(&self.path);
            Ok(())
        } else {
            // Assume its a file even if its not. It'll error out if there are
            // problems deleting it.
            fs::remove_file(&self.path)
        }
    }
}

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

    #[test]
    fn test_normalized() {
        assert_eq!(FilePath::new(PathBuf::from("./foo/..//bar/")),
                   FilePath::new(PathBuf::from("bar")));
    }
}
<commit_msg>Prefer using FilePath::new<commit_after>// Copyright (c) 2017 Jason White
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use std::ffi::OsStr;
use std::fmt;
use std::fs;
use std::io;
use std::io::Read;
use std::path::PathBuf;
use std::str::FromStr;

use sha2::{Digest, Sha256};

use serde::{de, Deserialize, Deserializer};

use super::traits::{Error, Resource, ResourceState};

use util::PathExt;

/// A file resource. This can actually be a file *or* directory.
///
/// TODO: Split the directory portion out into a "glob" resource whose state
/// changes when the list of matched files changes.
#[derive(Serialize, Ord, PartialOrd, Eq, PartialEq, Hash, Clone)]
pub struct FilePath {
    path: PathBuf,
}

impl FilePath {
    pub fn new(path: PathBuf) -> FilePath {
        FilePath { path: path.normalize() }
    }

    /// Assumes this resource is a regular file and returns its checksum.
    fn file_state(&self) -> Result<ResourceState, Error> {
        let mut hasher = Sha256::default();

        let mut f = match fs::File::open(&self.path) {
            Ok(f) => Ok(f),
            Err(err) => {
                match err.kind() {
                    io::ErrorKind::NotFound => {
                        return Ok(ResourceState::Missing);
                    }
                    _ => Err(err),
                }
            }
        }?;

        const BUF_SIZE: usize = 16384;

        let mut buf = [0u8; BUF_SIZE];

        loop {
            let n = f.read(&mut buf)?;

            if n == 0 || n < BUF_SIZE {
                break;
            }

            hasher.input(&buf[0..n]);
        }

        Ok(ResourceState::Checksum(hasher.result()))
    }

    /// Assumes this resource is a directory and returns the checksum of its
    /// file contents.
    fn dir_state(&self) -> Result<ResourceState, Error> {
        let mut hasher = Sha256::default();

        let mut names = vec![];

        for entry in fs::read_dir(&self.path)? {
            names.push(entry?.file_name());
        }

        // The order in which files are listed is not guaranteed to be sorted.
        // Whether or not it is sorted depends on the file system
        // implementation. Thus, we sort them to eliminate that potential
        // source of non-determinism.
        names.sort();

        for name in names {
            if let Some(name) = name.to_str() {
                hasher.input(name.as_bytes());
            }
        }

        Ok(ResourceState::Checksum(hasher.result()))
    }
}

impl<'a, T: ?Sized + AsRef<OsStr>> From<&'a T> for FilePath {
    fn from(s: &'a T) -> FilePath {
        FilePath::new(PathBuf::from(s.as_ref()))
    }
}

impl FromStr for FilePath {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(FilePath::new(PathBuf::from(s)))
    }
}

impl fmt::Display for FilePath {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.path)
    }
}

impl fmt::Debug for FilePath {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.path)
    }
}

// Derserialize a `FilePath` from a string.
impl<'de> Deserialize<'de> for FilePath {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: Deserializer<'de>
    {
        let s = String::deserialize(deserializer)?;
        FromStr::from_str(&s).map_err(de::Error::custom)
    }
}

impl Resource for FilePath {
    /// If a file, the checksum is of the contents of the file. If a directory,
    /// the checksum is of the sorted list of directory entries. Thus, if a file
    /// is added or removed from a directory, the checksum changes.
    fn state(&self) -> Result<ResourceState, Error> {
        if self.path.is_dir() {
            self.dir_state()
        } else {
            // Assume its a file even if its not. It'll error out if there are
            // problems reading it.
            self.file_state()
        }
    }

    /// If a file, simply deletes the file. If a directory, deletes the
    /// directory if it is empty.
    fn delete(&self) -> Result<(), Error> {
        let metadata = match fs::metadata(&self.path) {
            Ok(metadata) => Ok(metadata),
            Err(err) => {
                match err.kind() {
                    io::ErrorKind::NotFound => {
                        return Ok(());
                    }
                    _ => Err(err),
                }
            }
        }?;

        if metadata.is_dir() {
            // Ignore errors for directory deletion. It's not usually a problem
            // if a directory fails to get deleted. Directory deletion can fail
            // for a number of reasons:
            //  - A user may have created a file inside of it.
            //  - An untracked output may have been created inside of it.
            //  - On Windows, someone may have a lock on the directory.
            let _ = fs::remove_dir(&self.path);
            Ok(())
        } else {
            // Assume its a file even if its not. It'll error out if there are
            // problems deleting it.
            fs::remove_file(&self.path)
        }
    }
}

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

    #[test]
    fn test_normalized() {
        assert_eq!(FilePath::new(PathBuf::from("./foo/..//bar/")),
                   FilePath::new(PathBuf::from("bar")));
    }
}
<|endoftext|>


<commit_before>use cases::snakecase::to_snake_case;
use cases::sentencecase::to_sentence_case;
use string::pluralize::to_plural;

/// Converts a `String` to `table-case` `String`
///
/// #Examples
/// ```
/// use inflector::cases::tablecase::to_table_case;
/// let mock_string: String = "foo-bar".to_string();
/// let expected_string: String = "foo_bars".to_string();
/// let asserted_string: String = to_table_case(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
///
/// ```
/// use inflector::cases::tablecase::to_table_case;
/// let mock_string: String = "FOO_BAR".to_string();
/// let expected_string: String = "foo_bars".to_string();
/// let asserted_string: String = to_table_case(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
///
/// ```
/// use inflector::cases::tablecase::to_table_case;
/// let mock_string: String = "foo_bar".to_string();
/// let expected_string: String = "foo_bars".to_string();
/// let asserted_string: String = to_table_case(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
///
/// ```
/// use inflector::cases::tablecase::to_table_case;
/// let mock_string: String = "Foo Bar".to_string();
/// let expected_string: String = "foo_bars".to_string();
/// let asserted_string: String = to_table_case(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
///
/// ```
/// use inflector::cases::tablecase::to_table_case;
/// let mock_string: String = "Foo bar".to_string();
/// let expected_string: String = "foo_bars".to_string();
/// let asserted_string: String = to_table_case(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
///
/// ```
/// use inflector::cases::tablecase::to_table_case;
/// let mock_string: String = "FooBar".to_string();
/// let expected_string: String = "foo_bars".to_string();
/// let asserted_string: String = to_table_case(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
///
/// ```
/// use inflector::cases::tablecase::to_table_case;
/// let mock_string: String = "fooBar".to_string();
/// let expected_string: String = "foo_bars".to_string();
/// let asserted_string: String = to_table_case(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
pub fn to_table_case(non_table_case_string: String) -> String {
    let sentenceable_string: String = to_sentence_case(non_table_case_string.clone());
    let words: Vec<&str> = sentenceable_string.split(' ').collect();
    let mut sentence: String = "".to_string();
    for (word_index, _) in words.iter().enumerate().take((words.len() - 1)) {
        if word_index == 0 {
            sentence = words[word_index].to_string();
        } else {
            sentence = format!("{} {}", sentence, words[word_index]);
        }
    }
    sentence = format!("{} {}",
                       sentence,
                       to_plural(words[words.len() - 1].to_string()));
    to_snake_case(sentence)
}

/// Determines if a `String` is `table-case`
///
/// #Examples
///
/// ```
///     use inflector::cases::tablecase::is_table_case;
///     let mock_string: String = "foo_bar_strings".to_string();
///     let asserted_bool: bool = is_table_case(mock_string);
///     assert!(asserted_bool == true);
/// ```
///
/// ```
///     use inflector::cases::tablecase::is_table_case;
///     let mock_string: String = "foo-bar-string-that-is-really-really-long".to_string();
///     let asserted_bool: bool = is_table_case(mock_string);
///     assert!(asserted_bool == false);
/// ```
///
/// ```
///     use inflector::cases::tablecase::is_table_case;
///     let mock_string: String = "FooBarIsAReallyReallyLongString".to_string();
///     let asserted_bool: bool = is_table_case(mock_string);
///     assert!(asserted_bool == false);
/// ```
///
/// ```
///     use inflector::cases::tablecase::is_table_case;
///     let mock_string: String = "fooBarIsAReallyReallyLongString".to_string();
///     let asserted_bool: bool = is_table_case(mock_string);
///     assert!(asserted_bool == false);
/// ```
///
/// ```
///     use inflector::cases::tablecase::is_table_case;
///     let mock_string: String = "FOO_BAR_STRING_THAT_IS_REALLY_REALLY_LONG".to_string();
///     let asserted_bool: bool = is_table_case(mock_string);
///     assert!(asserted_bool == false);
/// ```
///
/// ```
///     use inflector::cases::tablecase::is_table_case;
///     let mock_string: String = "foo_bar_string_that_is_really_really_long".to_string();
///     let asserted_bool: bool = is_table_case(mock_string);
///     assert!(asserted_bool == false);
/// ```
///
/// ```
///     use inflector::cases::tablecase::is_table_case;
///     let mock_string: String = "Foo bar string that is really really long".to_string();
///     let asserted_bool: bool = is_table_case(mock_string);
///     assert!(asserted_bool == false);
/// ```
///
/// ```
///     use inflector::cases::tablecase::is_table_case;
///     let mock_string: String = "Foo Bar Is A Really Really Long String".to_string();
///     let asserted_bool: bool = is_table_case(mock_string);
///     assert!(asserted_bool == false);
/// ```
pub fn is_table_case(test_string: String) -> bool {
    test_string.clone() == to_table_case(test_string)
}
<commit_msg>Added table case benchmarks<commit_after>use cases::snakecase::to_snake_case;
use cases::sentencecase::to_sentence_case;
use string::pluralize::to_plural;

/// Converts a `String` to `table-case` `String`
///
/// #Examples
/// ```
/// use inflector::cases::tablecase::to_table_case;
/// let mock_string: String = "foo-bar".to_string();
/// let expected_string: String = "foo_bars".to_string();
/// let asserted_string: String = to_table_case(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
///
/// ```
/// use inflector::cases::tablecase::to_table_case;
/// let mock_string: String = "FOO_BAR".to_string();
/// let expected_string: String = "foo_bars".to_string();
/// let asserted_string: String = to_table_case(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
///
/// ```
/// use inflector::cases::tablecase::to_table_case;
/// let mock_string: String = "foo_bar".to_string();
/// let expected_string: String = "foo_bars".to_string();
/// let asserted_string: String = to_table_case(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
///
/// ```
/// use inflector::cases::tablecase::to_table_case;
/// let mock_string: String = "Foo Bar".to_string();
/// let expected_string: String = "foo_bars".to_string();
/// let asserted_string: String = to_table_case(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
///
/// ```
/// use inflector::cases::tablecase::to_table_case;
/// let mock_string: String = "Foo bar".to_string();
/// let expected_string: String = "foo_bars".to_string();
/// let asserted_string: String = to_table_case(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
///
/// ```
/// use inflector::cases::tablecase::to_table_case;
/// let mock_string: String = "FooBar".to_string();
/// let expected_string: String = "foo_bars".to_string();
/// let asserted_string: String = to_table_case(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
///
/// ```
/// use inflector::cases::tablecase::to_table_case;
/// let mock_string: String = "fooBar".to_string();
/// let expected_string: String = "foo_bars".to_string();
/// let asserted_string: String = to_table_case(mock_string);
/// assert!(asserted_string == expected_string);
/// ```
pub fn to_table_case(non_table_case_string: String) -> String {
    let sentenceable_string: String = to_sentence_case(non_table_case_string.clone());
    let words: Vec<&str> = sentenceable_string.split(' ').collect();
    let mut sentence: String = "".to_string();
    for (word_index, _) in words.iter().enumerate().take((words.len() - 1)) {
        if word_index == 0 {
            sentence = words[word_index].to_string();
        } else {
            sentence = format!("{} {}", sentence, words[word_index]);
        }
    }
    sentence = format!("{} {}",
                       sentence,
                       to_plural(words[words.len() - 1].to_string()));
    to_snake_case(sentence)
}
/// Determines if a `String` is `table-case`
///
/// #Examples
///
/// ```
///     use inflector::cases::tablecase::is_table_case;
///     let mock_string: String = "foo_bar_strings".to_string();
///     let asserted_bool: bool = is_table_case(mock_string);
///     assert!(asserted_bool == true);
/// ```
///
/// ```
///     use inflector::cases::tablecase::is_table_case;
///     let mock_string: String = "foo-bar-string-that-is-really-really-long".to_string();
///     let asserted_bool: bool = is_table_case(mock_string);
///     assert!(asserted_bool == false);
/// ```
///
/// ```
///     use inflector::cases::tablecase::is_table_case;
///     let mock_string: String = "FooBarIsAReallyReallyLongString".to_string();
///     let asserted_bool: bool = is_table_case(mock_string);
///     assert!(asserted_bool == false);
/// ```
///
/// ```
///     use inflector::cases::tablecase::is_table_case;
///     let mock_string: String = "fooBarIsAReallyReallyLongString".to_string();
///     let asserted_bool: bool = is_table_case(mock_string);
///     assert!(asserted_bool == false);
/// ```
///
/// ```
///     use inflector::cases::tablecase::is_table_case;
///     let mock_string: String = "FOO_BAR_STRING_THAT_IS_REALLY_REALLY_LONG".to_string();
///     let asserted_bool: bool = is_table_case(mock_string);
///     assert!(asserted_bool == false);
/// ```
///
/// ```
///     use inflector::cases::tablecase::is_table_case;
///     let mock_string: String = "foo_bar_string_that_is_really_really_long".to_string();
///     let asserted_bool: bool = is_table_case(mock_string);
///     assert!(asserted_bool == false);
/// ```
///
/// ```
///     use inflector::cases::tablecase::is_table_case;
///     let mock_string: String = "Foo bar string that is really really long".to_string();
///     let asserted_bool: bool = is_table_case(mock_string);
///     assert!(asserted_bool == false);
/// ```
///
/// ```
///     use inflector::cases::tablecase::is_table_case;
///     let mock_string: String = "Foo Bar Is A Really Really Long String".to_string();
///     let asserted_bool: bool = is_table_case(mock_string);
///     assert!(asserted_bool == false);
/// ```
pub fn is_table_case(test_string: String) -> bool {
    test_string.clone() == to_table_case(test_string)
}

#[cfg(test)]
mod tests {
    extern crate test;
    use self::test::Bencher;

    #[bench]
    fn bench_table_case(b: &mut Bencher) {
        b.iter(|| super::to_table_case("Foo bar".to_string()));
    }

    #[bench]
    fn bench_is_table_case(b: &mut Bencher) {
        b.iter(|| super::is_table_case("Foo bar".to_string()));
    }
}
<|endoftext|>


<commit_before>//! Extended attribute support for Darwin and Linux systems.

#![allow(trivial_casts)]  // for ARM

use std::cmp::Ordering;
use std::io;
use std::path::Path;

pub const ENABLED: bool = cfg!(any(target_os = "macos", target_os = "linux"));

pub trait FileAttributes {
    fn attributes(&self) -> io::Result<Vec<Attribute>>;
    fn symlink_attributes(&self) -> io::Result<Vec<Attribute>>;
}

#[cfg(any(target_os = "macos", target_os = "linux"))]
impl FileAttributes for Path {
    fn attributes(&self) -> io::Result<Vec<Attribute>> {
        list_attrs(&lister::Lister::new(FollowSymlinks::Yes), self)
    }

    fn symlink_attributes(&self) -> io::Result<Vec<Attribute>> {
        list_attrs(&lister::Lister::new(FollowSymlinks::No), self)
    }
}

#[cfg(not(any(target_os = "macos", target_os = "linux")))]
impl FileAttributes for Path {
    fn attributes(&self) -> io::Result<Vec<Attribute>> {
        Ok(Vec::new())
    }

    fn symlink_attributes(&self) -> io::Result<Vec<Attribute>> {
        Ok(Vec::new())
    }
}

/// Attributes which can be passed to `Attribute::list_with_flags`
#[cfg(any(target_os = "macos", target_os = "linux"))]
#[derive(Copy, Clone)]
pub enum FollowSymlinks {
    Yes,
    No,
}

/// Extended attribute
#[derive(Debug, Clone)]
pub struct Attribute {
    pub name: String,
    pub size: usize,
}

#[cfg(any(target_os = "macos", target_os = "linux"))]
pub fn list_attrs(lister: &lister::Lister, path: &Path) -> io::Result<Vec<Attribute>> {
    use std::ffi::CString;

    let c_path = match path.to_str().and_then(|s| CString::new(s).ok()) {
        Some(cstring) => cstring,
        None => {
            return Err(io::Error::new(io::ErrorKind::Other, "Error: path somehow contained a NUL?"));
        }
    };

    let bufsize = lister.listxattr_first(&c_path);
    match bufsize.cmp(&0) {
        Ordering::Less     => return Err(io::Error::last_os_error()),
        Ordering::Equal    => return Ok(Vec::new()),
        Ordering::Greater  => {},
    }

    let mut buf = vec![0_u8; bufsize as usize];
    let err = lister.listxattr_second(&c_path, &mut buf, bufsize);

    match err.cmp(&0) {
        Ordering::Less     => return Err(io::Error::last_os_error()),
        Ordering::Equal    => return Ok(Vec::new()),
        Ordering::Greater  => {},
    }

    let mut names = Vec::new();
    if err > 0 {
        // End indices of the attribute names
        // the buffer contains 0-terminated c-strings
        let idx = buf.iter().enumerate().filter_map(|(i, v)|
            if *v == 0 { Some(i) } else { None }
        );
        let mut start = 0;

        for end in idx {
            let c_end = end + 1; // end of the c-string (including 0)
            let size = lister.getxattr(&c_path, &buf[start..c_end]);

            if size > 0 {
                names.push(Attribute {
                    name: lister.translate_attribute_name(&buf[start..end]),
                    size: size as usize,
                });
            }

            start = c_end;
        }
    }

    Ok(names)
}

#[cfg(target_os = "macos")]
mod lister {
    use super::FollowSymlinks;
    use libc::{c_int, size_t, ssize_t, c_char, c_void};
    use std::ffi::CString;
    use std::ptr;

    extern "C" {
        fn listxattr(
            path: *const c_char,
            namebuf: *mut c_char,
            size: size_t,
            options: c_int,
        ) -> ssize_t;

        fn getxattr(
            path: *const c_char,
            name: *const c_char,
            value: *mut c_void,
            size: size_t,
            position: u32,
            options: c_int,
        ) -> ssize_t;
    }

    pub struct Lister {
        c_flags: c_int,
    }

    impl Lister {
        pub fn new(do_follow: FollowSymlinks) -> Self {
            let c_flags: c_int = match do_follow {
                FollowSymlinks::Yes  => 0x0001,
                FollowSymlinks::No   => 0x0000,
            };

            Self { c_flags }
        }

        pub fn translate_attribute_name(&self, input: &[u8]) -> String {
            unsafe { std::str::from_utf8_unchecked(input).into() }
        }

        pub fn listxattr_first(&self, c_path: &CString) -> ssize_t {
            unsafe {
                listxattr(
                    c_path.as_ptr(),
                    ptr::null_mut(),
                    0,
                    self.c_flags,
                )
            }
        }

        pub fn listxattr_second(&self, c_path: &CString, buf: &mut Vec<u8>, bufsize: ssize_t) -> ssize_t {
            unsafe {
                listxattr(
                    c_path.as_ptr(),
                    buf.as_mut_ptr() as *mut c_char,
                    bufsize as size_t,
                    self.c_flags,
                )
            }
        }

        pub fn getxattr(&self, c_path: &CString, buf: &[u8]) -> ssize_t {
            unsafe {
                getxattr(
                    c_path.as_ptr(),
                    buf.as_ptr() as *const c_char,
                    ptr::null_mut(),
                    0,
                    0,
                    self.c_flags,
                )
            }
        }
    }
}

#[cfg(target_os = "linux")]
mod lister {
    use std::ffi::CString;
    use libc::{size_t, ssize_t, c_char, c_void};
    use super::FollowSymlinks;
    use std::ptr;

    extern "C" {
        fn listxattr(
            path: *const c_char,
            list: *mut c_char,
            size: size_t,
        ) -> ssize_t;

        fn llistxattr(
            path: *const c_char,
            list: *mut c_char,
            size: size_t,
        ) -> ssize_t;

        fn getxattr(
            path: *const c_char,
            name: *const c_char,
            value: *mut c_void,
            size: size_t,
        ) -> ssize_t;

        fn lgetxattr(
            path: *const c_char,
            name: *const c_char,
            value: *mut c_void,
            size: size_t,
        ) -> ssize_t;
    }

    pub struct Lister {
        follow_symlinks: FollowSymlinks,
    }

    impl Lister {
        pub fn new(follow_symlinks: FollowSymlinks) -> Lister {
            Lister { follow_symlinks }
        }

        pub fn translate_attribute_name(&self, input: &[u8]) -> String {
            String::from_utf8_lossy(input).into_owned()
        }

        pub fn listxattr_first(&self, c_path: &CString) -> ssize_t {
            let listxattr = match self.follow_symlinks {
                FollowSymlinks::Yes  => listxattr,
                FollowSymlinks::No   => llistxattr,
            };

            unsafe {
                listxattr(
                    c_path.as_ptr().cast(),
                    ptr::null_mut(),
                    0,
                )
            }
        }

        pub fn listxattr_second(&self, c_path: &CString, buf: &mut Vec<u8>, bufsize: ssize_t) -> ssize_t {
            let listxattr = match self.follow_symlinks {
                FollowSymlinks::Yes  => listxattr,
                FollowSymlinks::No   => llistxattr,
            };

            unsafe {
                listxattr(
                    c_path.as_ptr().cast(),
                    buf.as_mut_ptr().cast::<i8>(),
                    bufsize as size_t,
                )
            }
        }

        pub fn getxattr(&self, c_path: &CString, buf: &[u8]) -> ssize_t {
            let getxattr = match self.follow_symlinks {
                FollowSymlinks::Yes  => getxattr,
                FollowSymlinks::No   => lgetxattr,
            };

            unsafe {
                getxattr(
                    c_path.as_ptr().cast(),
                    buf.as_ptr().cast::<i8>(),
                    ptr::null_mut(),
                    0,
                )
            }
        }
    }
}
<commit_msg>Fix build on aarch64 and armv7<commit_after>//! Extended attribute support for Darwin and Linux systems.

#![allow(trivial_casts)]  // for ARM

use std::cmp::Ordering;
use std::io;
use std::path::Path;

pub const ENABLED: bool = cfg!(any(target_os = "macos", target_os = "linux"));

pub trait FileAttributes {
    fn attributes(&self) -> io::Result<Vec<Attribute>>;
    fn symlink_attributes(&self) -> io::Result<Vec<Attribute>>;
}

#[cfg(any(target_os = "macos", target_os = "linux"))]
impl FileAttributes for Path {
    fn attributes(&self) -> io::Result<Vec<Attribute>> {
        list_attrs(&lister::Lister::new(FollowSymlinks::Yes), self)
    }

    fn symlink_attributes(&self) -> io::Result<Vec<Attribute>> {
        list_attrs(&lister::Lister::new(FollowSymlinks::No), self)
    }
}

#[cfg(not(any(target_os = "macos", target_os = "linux")))]
impl FileAttributes for Path {
    fn attributes(&self) -> io::Result<Vec<Attribute>> {
        Ok(Vec::new())
    }

    fn symlink_attributes(&self) -> io::Result<Vec<Attribute>> {
        Ok(Vec::new())
    }
}

/// Attributes which can be passed to `Attribute::list_with_flags`
#[cfg(any(target_os = "macos", target_os = "linux"))]
#[derive(Copy, Clone)]
pub enum FollowSymlinks {
    Yes,
    No,
}

/// Extended attribute
#[derive(Debug, Clone)]
pub struct Attribute {
    pub name: String,
    pub size: usize,
}

#[cfg(any(target_os = "macos", target_os = "linux"))]
pub fn list_attrs(lister: &lister::Lister, path: &Path) -> io::Result<Vec<Attribute>> {
    use std::ffi::CString;

    let c_path = match path.to_str().and_then(|s| CString::new(s).ok()) {
        Some(cstring) => cstring,
        None => {
            return Err(io::Error::new(io::ErrorKind::Other, "Error: path somehow contained a NUL?"));
        }
    };

    let bufsize = lister.listxattr_first(&c_path);
    match bufsize.cmp(&0) {
        Ordering::Less     => return Err(io::Error::last_os_error()),
        Ordering::Equal    => return Ok(Vec::new()),
        Ordering::Greater  => {},
    }

    let mut buf = vec![0_u8; bufsize as usize];
    let err = lister.listxattr_second(&c_path, &mut buf, bufsize);

    match err.cmp(&0) {
        Ordering::Less     => return Err(io::Error::last_os_error()),
        Ordering::Equal    => return Ok(Vec::new()),
        Ordering::Greater  => {},
    }

    let mut names = Vec::new();
    if err > 0 {
        // End indices of the attribute names
        // the buffer contains 0-terminated c-strings
        let idx = buf.iter().enumerate().filter_map(|(i, v)|
            if *v == 0 { Some(i) } else { None }
        );
        let mut start = 0;

        for end in idx {
            let c_end = end + 1; // end of the c-string (including 0)
            let size = lister.getxattr(&c_path, &buf[start..c_end]);

            if size > 0 {
                names.push(Attribute {
                    name: lister.translate_attribute_name(&buf[start..end]),
                    size: size as usize,
                });
            }

            start = c_end;
        }
    }

    Ok(names)
}

#[cfg(target_os = "macos")]
mod lister {
    use super::FollowSymlinks;
    use libc::{c_int, size_t, ssize_t, c_char, c_void};
    use std::ffi::CString;
    use std::ptr;

    extern "C" {
        fn listxattr(
            path: *const c_char,
            namebuf: *mut c_char,
            size: size_t,
            options: c_int,
        ) -> ssize_t;

        fn getxattr(
            path: *const c_char,
            name: *const c_char,
            value: *mut c_void,
            size: size_t,
            position: u32,
            options: c_int,
        ) -> ssize_t;
    }

    pub struct Lister {
        c_flags: c_int,
    }

    impl Lister {
        pub fn new(do_follow: FollowSymlinks) -> Self {
            let c_flags: c_int = match do_follow {
                FollowSymlinks::Yes  => 0x0001,
                FollowSymlinks::No   => 0x0000,
            };

            Self { c_flags }
        }

        pub fn translate_attribute_name(&self, input: &[u8]) -> String {
            unsafe { std::str::from_utf8_unchecked(input).into() }
        }

        pub fn listxattr_first(&self, c_path: &CString) -> ssize_t {
            unsafe {
                listxattr(
                    c_path.as_ptr(),
                    ptr::null_mut(),
                    0,
                    self.c_flags,
                )
            }
        }

        pub fn listxattr_second(&self, c_path: &CString, buf: &mut Vec<u8>, bufsize: ssize_t) -> ssize_t {
            unsafe {
                listxattr(
                    c_path.as_ptr(),
                    buf.as_mut_ptr() as *mut c_char,
                    bufsize as size_t,
                    self.c_flags,
                )
            }
        }

        pub fn getxattr(&self, c_path: &CString, buf: &[u8]) -> ssize_t {
            unsafe {
                getxattr(
                    c_path.as_ptr(),
                    buf.as_ptr() as *const c_char,
                    ptr::null_mut(),
                    0,
                    0,
                    self.c_flags,
                )
            }
        }
    }
}

#[cfg(target_os = "linux")]
mod lister {
    use std::ffi::CString;
    use libc::{size_t, ssize_t, c_char, c_void};
    use super::FollowSymlinks;
    use std::ptr;

    extern "C" {
        fn listxattr(
            path: *const c_char,
            list: *mut c_char,
            size: size_t,
        ) -> ssize_t;

        fn llistxattr(
            path: *const c_char,
            list: *mut c_char,
            size: size_t,
        ) -> ssize_t;

        fn getxattr(
            path: *const c_char,
            name: *const c_char,
            value: *mut c_void,
            size: size_t,
        ) -> ssize_t;

        fn lgetxattr(
            path: *const c_char,
            name: *const c_char,
            value: *mut c_void,
            size: size_t,
        ) -> ssize_t;
    }

    pub struct Lister {
        follow_symlinks: FollowSymlinks,
    }

    impl Lister {
        pub fn new(follow_symlinks: FollowSymlinks) -> Lister {
            Lister { follow_symlinks }
        }

        pub fn translate_attribute_name(&self, input: &[u8]) -> String {
            String::from_utf8_lossy(input).into_owned()
        }

        pub fn listxattr_first(&self, c_path: &CString) -> ssize_t {
            let listxattr = match self.follow_symlinks {
                FollowSymlinks::Yes  => listxattr,
                FollowSymlinks::No   => llistxattr,
            };

            unsafe {
                listxattr(
                    c_path.as_ptr().cast(),
                    ptr::null_mut(),
                    0,
                )
            }
        }

        pub fn listxattr_second(&self, c_path: &CString, buf: &mut Vec<u8>, bufsize: ssize_t) -> ssize_t {
            let listxattr = match self.follow_symlinks {
                FollowSymlinks::Yes  => listxattr,
                FollowSymlinks::No   => llistxattr,
            };

            unsafe {
                listxattr(
                    c_path.as_ptr().cast(),
                    buf.as_mut_ptr().cast(),
                    bufsize as size_t,
                )
            }
        }

        pub fn getxattr(&self, c_path: &CString, buf: &[u8]) -> ssize_t {
            let getxattr = match self.follow_symlinks {
                FollowSymlinks::Yes  => getxattr,
                FollowSymlinks::No   => lgetxattr,
            };

            unsafe {
                getxattr(
                    c_path.as_ptr().cast(),
                    buf.as_ptr().cast(),
                    ptr::null_mut(),
                    0,
                )
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::path::{Path, PathBuf};

use anyhow::Result;
use askama::Template;
use structopt::StructOpt;

#[derive(StructOpt, Debug)]
struct Cli {
    /// The output directory
    #[structopt(short = "o", long = "out", default_value = "out")]
    out: String,
    /// The module tree depth
    #[structopt(short = "d", long = "depth", default_value = "4")]
    depth: u32,
    /// The module tree width at each level
    #[structopt(short = "b", long = "branches", default_value = "4")]
    branches: u32,
}

struct Module {
    children: Vec<Module>,
    // e.g. "a2_a1_a3"
    name: String,
    // e.g. "a1"
    short_name: String,
    // e.g. "a2/a1"
    dir: Option<PathBuf>,
}

impl Module {
    fn new(name: String, short_name: String, dir: Option<PathBuf>) -> Module {
        Module {
            name,
            short_name,
            dir,
            children: vec![],
        }
    }

    fn expand_recurse(&mut self, depth: u32, option: &Cli) {
        if depth == option.depth {
            return;
        }
        let dir = match &self.dir {
            Some(dir) => dir.join(&self.short_name),
            None => PathBuf::from(&self.short_name),
        };
        for index in 0..option.branches {
            let short_name = format!("a{index}");
            let name = format!("{}_{short_name}", self.name);
            let mut module = Module::new(name, short_name, Some(dir.clone()));
            module.expand_recurse(depth + 1, option);
            self.children.push(module);
        }
    }

    fn filename(&self) -> String {
        format!("{}.mjs", self.name)
    }

    fn full_path(&self) -> String {
        match &self.dir {
            Some(dir) => dir.join(self.filename()).display().to_string(),
            None => self.filename(),
        }
    }

    fn relative_path_from_parent(&self) -> String {
        match &self.dir {
            Some(dir) => Path::new(dir.file_name().unwrap())
                .join(self.filename())
                .display()
                .to_string(),
            None => self.filename(),
        }
    }

    fn export_function_name(&self) -> String {
        format!("f_{}", self.name)
    }

    fn function_definition(&self) -> String {
        let mut ops = self
            .children
            .iter()
            .map(|child| format!("{}()", child.export_function_name()))
            .collect::<Vec<_>>();
        ops.push("a".to_string());
        let res = ops.join(" + ");
        format!(
            r#"export function {}() {{
    let a = 1;
    return {res};
}}
"#,
            self.export_function_name()
        )
    }

    fn import_me(&self) -> String {
        format!(
            r#"import {{ {} }} from "./{}""#,
            self.export_function_name(),
            self.relative_path_from_parent()
        )
    }

    fn export(&self, mut builder: webbundle::Builder, option: &Cli) -> Result<webbundle::Builder> {
        match &self.dir {
            Some(dir) => log::debug!("{}", dir.join(self.filename()).display()),
            None => log::debug!("{}", self.filename()),
        };
        let t = ModuleTemplate {
            imports: self
                .children
                .iter()
                .map(|child| child.import_me())
                .collect(),
            function_definition: self.function_definition(),
        };

        let output_dir = match &self.dir {
            Some(dir) => PathBuf::from(&option.out).join(dir),
            None => PathBuf::from(&option.out),
        };

        std::fs::create_dir_all(&output_dir)?;

        let file = PathBuf::from(&option.out).join(self.full_path());
        std::fs::write(file, t.render().unwrap())?;

        builder = builder.exchange((self.full_path(), t.render().unwrap().into_bytes()).into());

        for child in &self.children {
            builder = child.export(builder, option)?;
        }
        Ok(builder)
    }
}

struct Benchmark {
    start_module: Module,
}

impl Benchmark {
    fn new(option: &Cli) -> Benchmark {
        let mut start_module = Module::new("a0".to_string(), "a0".to_string(), None);
        start_module.expand_recurse(0, option);
        Benchmark { start_module }
    }

    fn build(&self, option: &Cli) -> Result<()> {
        self.build_modules(option)?;
        self.build_html(option)
    }

    fn build_modules(&self, option: &Cli) -> Result<()> {
        // Build modules
        let builder = webbundle::Bundle::builder().version(webbundle::Version::VersionB2);
        let builder = self.start_module.export(builder, option)?;

        // Build webbundle
        let webbundle = builder.build()?;
        println!("Build {} modules", webbundle.exchanges().len());
        std::fs::create_dir_all(&option.out)?;
        let f = std::fs::File::create(PathBuf::from(&option.out).join("webbundle.wbn"))?;
        webbundle.write_to(f)?;
        Ok(())
    }

    fn build_html(&self, option: &Cli) -> Result<()> {
        self.build_unbundled_html(option)?;
        self.build_webbundle_html(option)?;
        self.build_index_html(option)
    }

    fn build_unbundled_html(&self, option: &Cli) -> Result<()> {
        let t = BenchmarkTemplate {
            headers: "".to_string(),
            info: format!("option: {option:#?}"),
            modules: vec![],
            start_module: self.start_module.full_path(),
            start_func: self.start_module.export_function_name(),
        };

        std::fs::create_dir_all(&option.out)?;
        let file = PathBuf::from(&option.out).join("unbundled.html");
        std::fs::write(file, t.render().unwrap())?;
        Ok(())
    }

    fn build_webbundle_html(&self, option: &Cli) -> Result<()> {
        let t = BenchmarkTemplate {
            headers: r#"<script type="webbundle"> {{ "source": "webbundle.wbn", "scopes": ["."] }} </script>"#.to_string(),
            info: format!("option: {option:#?}"),
            modules: vec![],
            start_module: self.start_module.full_path(),
            start_func: self.start_module.export_function_name(),
        };

        std::fs::create_dir_all(&option.out)?;
        let file = PathBuf::from(&option.out).join("webbundle.html");
        std::fs::write(file, t.render().unwrap())?;
        Ok(())
    }

    fn build_index_html(&self, option: &Cli) -> Result<()> {
        let t = IndexTemplate {
            info: format!("option: {option:#?}"),
            benchmarks: vec!["unbundled".to_string(), "webbundle".to_string()],
        };

        std::fs::create_dir_all(&option.out)?;
        let file = PathBuf::from(&option.out).join("index.html");
        std::fs::write(file, t.render().unwrap())?;
        Ok(())
    }
}

#[derive(Template)]
#[template(path = "module.html")]
struct ModuleTemplate {
    imports: Vec<String>,
    function_definition: String,
}

#[derive(Template)]
#[template(path = "benchmark.html")]
struct BenchmarkTemplate {
    headers: String,
    info: String,
    modules: Vec<String>,
    start_module: String,
    start_func: String,
}

#[derive(Template)]
#[template(path = "index.html")]
struct IndexTemplate {
    info: String,
    benchmarks: Vec<String>,
}

fn main() -> Result<()> {
    env_logger::init();
    let args = Cli::from_args();
    let benchmark = Benchmark::new(&args);
    benchmark.build(&args)
}
<commit_msg>Fix syntax error in WebBundle rule<commit_after>use std::path::{Path, PathBuf};

use anyhow::Result;
use askama::Template;
use structopt::StructOpt;

#[derive(StructOpt, Debug)]
struct Cli {
    /// The output directory
    #[structopt(short = "o", long = "out", default_value = "out")]
    out: String,
    /// The module tree depth
    #[structopt(short = "d", long = "depth", default_value = "4")]
    depth: u32,
    /// The module tree width at each level
    #[structopt(short = "b", long = "branches", default_value = "4")]
    branches: u32,
}

struct Module {
    children: Vec<Module>,
    // e.g. "a2_a1_a3"
    name: String,
    // e.g. "a1"
    short_name: String,
    // e.g. "a2/a1"
    dir: Option<PathBuf>,
}

impl Module {
    fn new(name: String, short_name: String, dir: Option<PathBuf>) -> Module {
        Module {
            name,
            short_name,
            dir,
            children: vec![],
        }
    }

    fn expand_recurse(&mut self, depth: u32, option: &Cli) {
        if depth == option.depth {
            return;
        }
        let dir = match &self.dir {
            Some(dir) => dir.join(&self.short_name),
            None => PathBuf::from(&self.short_name),
        };
        for index in 0..option.branches {
            let short_name = format!("a{index}");
            let name = format!("{}_{short_name}", self.name);
            let mut module = Module::new(name, short_name, Some(dir.clone()));
            module.expand_recurse(depth + 1, option);
            self.children.push(module);
        }
    }

    fn filename(&self) -> String {
        format!("{}.mjs", self.name)
    }

    fn full_path(&self) -> String {
        match &self.dir {
            Some(dir) => dir.join(self.filename()).display().to_string(),
            None => self.filename(),
        }
    }

    fn relative_path_from_parent(&self) -> String {
        match &self.dir {
            Some(dir) => Path::new(dir.file_name().unwrap())
                .join(self.filename())
                .display()
                .to_string(),
            None => self.filename(),
        }
    }

    fn export_function_name(&self) -> String {
        format!("f_{}", self.name)
    }

    fn function_definition(&self) -> String {
        let mut ops = self
            .children
            .iter()
            .map(|child| format!("{}()", child.export_function_name()))
            .collect::<Vec<_>>();
        ops.push("a".to_string());
        let res = ops.join(" + ");
        format!(
            r#"export function {}() {{
    let a = 1;
    return {res};
}}
"#,
            self.export_function_name()
        )
    }

    fn import_me(&self) -> String {
        format!(
            r#"import {{ {} }} from "./{}""#,
            self.export_function_name(),
            self.relative_path_from_parent()
        )
    }

    fn export(&self, mut builder: webbundle::Builder, option: &Cli) -> Result<webbundle::Builder> {
        match &self.dir {
            Some(dir) => log::debug!("{}", dir.join(self.filename()).display()),
            None => log::debug!("{}", self.filename()),
        };
        let t = ModuleTemplate {
            imports: self
                .children
                .iter()
                .map(|child| child.import_me())
                .collect(),
            function_definition: self.function_definition(),
        };

        let output_dir = match &self.dir {
            Some(dir) => PathBuf::from(&option.out).join(dir),
            None => PathBuf::from(&option.out),
        };

        std::fs::create_dir_all(&output_dir)?;

        let file = PathBuf::from(&option.out).join(self.full_path());
        std::fs::write(file, t.render().unwrap())?;

        builder = builder.exchange((self.full_path(), t.render().unwrap().into_bytes()).into());

        for child in &self.children {
            builder = child.export(builder, option)?;
        }
        Ok(builder)
    }
}

struct Benchmark {
    start_module: Module,
}

impl Benchmark {
    fn new(option: &Cli) -> Benchmark {
        let mut start_module = Module::new("a0".to_string(), "a0".to_string(), None);
        start_module.expand_recurse(0, option);
        Benchmark { start_module }
    }

    fn build(&self, option: &Cli) -> Result<()> {
        self.build_modules(option)?;
        self.build_html(option)
    }

    fn build_modules(&self, option: &Cli) -> Result<()> {
        // Build modules
        let builder = webbundle::Bundle::builder().version(webbundle::Version::VersionB2);
        let builder = self.start_module.export(builder, option)?;

        // Build webbundle
        let webbundle = builder.build()?;
        println!("Build {} modules", webbundle.exchanges().len());
        std::fs::create_dir_all(&option.out)?;
        let f = std::fs::File::create(PathBuf::from(&option.out).join("webbundle.wbn"))?;
        webbundle.write_to(f)?;
        Ok(())
    }

    fn build_html(&self, option: &Cli) -> Result<()> {
        self.build_unbundled_html(option)?;
        self.build_webbundle_html(option)?;
        self.build_index_html(option)
    }

    fn build_unbundled_html(&self, option: &Cli) -> Result<()> {
        let t = BenchmarkTemplate {
            headers: "".to_string(),
            info: format!("option: {option:#?}"),
            modules: vec![],
            start_module: self.start_module.full_path(),
            start_func: self.start_module.export_function_name(),
        };

        std::fs::create_dir_all(&option.out)?;
        let file = PathBuf::from(&option.out).join("unbundled.html");
        std::fs::write(file, t.render().unwrap())?;
        Ok(())
    }

    fn build_webbundle_html(&self, option: &Cli) -> Result<()> {
        let t = BenchmarkTemplate {
            headers: r#"<script type="webbundle"> { "source": "webbundle.wbn", "scopes": ["."] } </script>"#.to_string(),
            info: format!("option: {option:#?}"),
            modules: vec![],
            start_module: self.start_module.full_path(),
            start_func: self.start_module.export_function_name(),
        };

        std::fs::create_dir_all(&option.out)?;
        let file = PathBuf::from(&option.out).join("webbundle.html");
        std::fs::write(file, t.render().unwrap())?;
        Ok(())
    }

    fn build_index_html(&self, option: &Cli) -> Result<()> {
        let t = IndexTemplate {
            info: format!("option: {option:#?}"),
            benchmarks: vec!["unbundled".to_string(), "webbundle".to_string()],
        };

        std::fs::create_dir_all(&option.out)?;
        let file = PathBuf::from(&option.out).join("index.html");
        std::fs::write(file, t.render().unwrap())?;
        Ok(())
    }
}

#[derive(Template)]
#[template(path = "module.html")]
struct ModuleTemplate {
    imports: Vec<String>,
    function_definition: String,
}

#[derive(Template)]
#[template(path = "benchmark.html")]
struct BenchmarkTemplate {
    headers: String,
    info: String,
    modules: Vec<String>,
    start_module: String,
    start_func: String,
}

#[derive(Template)]
#[template(path = "index.html")]
struct IndexTemplate {
    info: String,
    benchmarks: Vec<String>,
}

fn main() -> Result<()> {
    env_logger::init();
    let args = Cli::from_args();
    let benchmark = Benchmark::new(&args);
    benchmark.build(&args)
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use std::net::TcpStream;

use parking_lot::RwLock;
use super::{Client, Command};
use errors::*;
use conn::{
    ConnectionOpts,
    Connection,
    ReqlConnection,
    TlsCfg,
    Session,
};
use ::{Result, set_config, config, Pool};
use r2d2::{self, ManageConnection, Config, PooledConnection as PConn};

#[derive(Debug)]
pub struct PooledConnection(PConn<ConnectionManager>);

lazy_static! {
    static ref POOL: RwLock<Option<Vec<r2d2::Pool<ConnectionManager>>>> = RwLock::new(None);
}

impl Client<(), ()>
{
    pub fn connection(self) -> Client<Config<Connection, Error>, ConnectionOpts>
    {
        let opts = ConnectionOpts::default();
        let cfg = Config::default();
        Client {
            cmd: Command(cfg, Some(opts)),
            errors: self.errors,
        }
    }
}

impl Client<Config<Connection, Error>, ConnectionOpts>
{
    pub fn pool_config(mut self, arg: Config<Connection, Error>) -> Self {
        self.cmd.0 = arg;
        self
    }

    pub fn servers(mut self, arg: Vec<&'static str>) -> Self {
        let mut opts = self.cmd.opts();
        opts.set_servers(arg);
        self.cmd.1 = Some(opts);
        self
    }

    pub fn db(mut self, arg: &'static str) -> Self {
        let mut opts = self.cmd.opts();
        opts.set_db(arg);
        self.cmd.1 = Some(opts);
        self
    }

    pub fn user(mut self, arg: &'static str) -> Self {
        let mut opts = self.cmd.opts();
        opts.set_user(arg);
        self.cmd.1 = Some(opts);
        self
    }

    pub fn password(mut self, arg: &'static str) -> Self {
        let mut opts = self.cmd.opts();
        opts.set_password(arg);
        self.cmd.1 = Some(opts);
        self
    }

    pub fn retries(mut self, arg: u8) -> Self {
        let mut opts = self.cmd.opts();
        opts.set_retries(arg);
        self.cmd.1 = Some(opts);
        self
    }

    pub fn tls(mut self, arg: Option<TlsCfg>) -> Self {
        let mut opts = self.cmd.opts();
        opts.set_tls(arg);
        self.cmd.1 = Some(opts);
        self
    }

    pub fn connect(self) -> Result<Pool> {
        let opts = self.cmd.opts();
        set_config(opts.clone());
        let mut pools: Vec<r2d2::Pool<ConnectionManager>> = Vec::new();
        for server in opts.servers() {
            let manager = ConnectionManager(server);
            let config = self.clone_pool_config();
            let new_pool = r2d2::Pool::new(config, manager)?;
            pools.push(new_pool);
        }
        set_pool(pools);
        Ok(Pool)
    }

    fn clone_pool_config(&self) -> Config<Connection, Error>
    {
        let ref cfg = self.cmd.0;
        Config::builder()
            .pool_size(cfg.pool_size())
            .min_idle(cfg.min_idle())
            .helper_threads(cfg.helper_threads())
            .test_on_check_out(cfg.test_on_check_out())
            .initialization_fail_fast(cfg.initialization_fail_fast())
            .max_lifetime(cfg.max_lifetime())
            .idle_timeout(cfg.idle_timeout())
            .connection_timeout(cfg.connection_timeout())
            .build()
    }
}

pub struct ConnectionManager(&'static str);

impl ManageConnection for ConnectionManager {
    type Connection = Connection;
    type Error = Error;

    fn connect(&self) -> Result<Connection> {
        let opts = config().read();
        Connection::new(self.0, &opts)
    }

    fn is_valid(&self, mut conn: &mut Connection) -> Result<()> {
        conn.is_valid()
    }

    fn has_broken(&self, conn: &mut Connection) -> bool {
        if conn.broken() {
            return true;
        }
        match conn.stream().take_error() {
            Ok(error) => {
                if error.is_some() {
                    return true;
                }
            }
            Err(_) => {
                return true;
            }
        }
        false
    }
}

impl ReqlConnection for PooledConnection {
    fn stream(&mut self) -> &mut TcpStream {
        self.0.stream()
    }

    fn incr_token(&mut self) -> &mut Self {
        self.0.incr_token();
        self
    }

    fn token(&self) -> u64 {
        self.0.token()
    }

    fn set_broken(&mut self, b: bool) -> &mut Self {
        self.0.set_broken(b);
        self
    }

    fn broken(&self) -> bool {
        self.0.broken()
    }
}

impl Session for Pool {
    type Connection = PooledConnection;

    fn get(&self) -> Result<PooledConnection> {
        let cfg = config().read();
        let pool = pool().read();
        match *pool {
            Some(ref pool) => {
                let msg = String::from("Failed to get a connection.");
                let mut last_error = err!(ConnectionError::Other(msg));
                macro_rules! return_conn {
                    ($e:expr) => {{
                        match $e {
                            Ok(mut conn) => {
                                conn.incr_token();
                                return Ok(PooledConnection(conn));
                            },
                            Err(error) => last_error = err!(error),
                        }
                    }}
                }
                let mut num_retries = cfg.retries();
                while num_retries > 0 {
                    let mut least_connections = 0;
                    let mut least_connected_server = 0;
                    let mut most_idle = 0;
                    let mut most_idle_server = 0;
                    for (i, p) in pool.iter().enumerate() {
                        let state = p.state();
                        if least_connections == 0 || least_connections > state.connections {
                            least_connections = state.connections;
                            least_connected_server = i
                        }
                        if most_idle == 0 || most_idle < state.idle_connections {
                            most_idle = state.idle_connections;
                            most_idle_server = i
                        }
                    }
                    if most_idle > 0 {
                        return_conn!(pool[most_idle_server].get());
                    } else if least_connections > 0 {
                        return_conn!(pool[least_connected_server].get());
                    } else {
                        let msg = String::from("All servers are currently down.");
                        last_error = err!(ConnectionError::Other(msg));
                    }
                    num_retries -= 1;
                }
                return last_error;
            }
            None => {
                let msg = String::from("Your connection pool is not initialised. \
                                   Use `r.connection().connect()` to initialise the pool \
                                   before trying to send any connections to the database. \
                                   This is typically done in the `main` function.");
                return err!(ConnectionError::Other(msg));
            }
        }
    }
}

fn pool() -> &'static RwLock<Option<Vec<r2d2::Pool<ConnectionManager>>>> {
    &POOL
}

fn set_pool(p: Vec<r2d2::Pool<ConnectionManager>>) {
    let mut pool = POOL.write();
    *pool = Some(p);
}
<commit_msg>Make servers separately configurable<commit_after>#![allow(dead_code)]

use std::net::TcpStream;

use parking_lot::RwLock;
use super::{Client, Command};
use errors::*;
use conn::{
    ConnectionOpts,
    Connection,
    ReqlConnection,
    TlsCfg,
    Session,
};
use ::{Result, set_config, config, Pool};
use r2d2::{self, ManageConnection, Config, PooledConnection as PConn};

#[derive(Debug)]
pub struct PooledConnection(PConn<ConnectionManager>);

lazy_static! {
    static ref POOL: RwLock<Option<Vec<r2d2::Pool<ConnectionManager>>>> = RwLock::new(None);
}

impl Client<(), ()>
{
    pub fn connection(self) -> Client<ConnectionOpts, Vec<Server>>
    {
        let opts = ConnectionOpts::default();
        Client {
            cmd: Command(opts, None),
            errors: self.errors,
        }
    }
}

#[derive(Debug)]
pub struct Server {
    host: &'static str,
    cfg: Config<Connection, Error>,
}

impl Server {
    pub fn new(arg: &'static str) -> Server {
        Server {
            host: arg,
            cfg: Config::default(),
        }
    }

    pub fn config(mut self, arg: Config<Connection, Error>) -> Server {
        self.cfg = arg;
        self
    }
}

impl Client<ConnectionOpts, Vec<Server>>
{
    pub fn servers(mut self, arg: Vec<Server>) -> Self {
        let s: Vec<_> = arg.iter().map(|s| s.host).collect();
        self.cmd.0.set_servers(s);
        self.cmd.1 = Some(arg);
        self
    }

    pub fn db(mut self, arg: &'static str) -> Self {
        self.cmd.0.set_db(arg);
        self
    }

    pub fn user(mut self, arg: &'static str) -> Self {
        self.cmd.0.set_user(arg);
        self
    }

    pub fn password(mut self, arg: &'static str) -> Self {
        self.cmd.0.set_password(arg);
        self
    }

    pub fn retries(mut self, arg: u8) -> Self {
        self.cmd.0.set_retries(arg);
        self
    }

    pub fn tls(mut self, arg: Option<TlsCfg>) -> Self {
        self.cmd.0.set_tls(arg);
        self
    }

    pub fn connect(self) -> Result<Pool> {
        set_config(self.cmd.0);
        let mut pools: Vec<r2d2::Pool<ConnectionManager>> = Vec::new();
        let servers = match self.cmd.1 {
            Some(servers) => servers,
            None => {
                let server = Server {
                    host: {
                        let opts = ConnectionOpts::default();
                        opts.servers()[0]
                    },
                    cfg: Config::default(),
                };
                vec![server]
            },
        };
        for server in servers {
            let manager = ConnectionManager(server.host);
            let pool = r2d2::Pool::new(server.cfg, manager)?;
            pools.push(pool);
        }
        set_pool(pools);
        Ok(Pool)
    }
}

pub struct ConnectionManager(&'static str);

impl ManageConnection for ConnectionManager {
    type Connection = Connection;
    type Error = Error;

    fn connect(&self) -> Result<Connection> {
        let opts = config().read();
        Connection::new(self.0, &opts)
    }

    fn is_valid(&self, mut conn: &mut Connection) -> Result<()> {
        conn.is_valid()
    }

    fn has_broken(&self, conn: &mut Connection) -> bool {
        if conn.broken() {
            return true;
        }
        match conn.stream().take_error() {
            Ok(error) => {
                if error.is_some() {
                    return true;
                }
            }
            Err(_) => {
                return true;
            }
        }
        false
    }
}

impl ReqlConnection for PooledConnection {
    fn stream(&mut self) -> &mut TcpStream {
        self.0.stream()
    }

    fn incr_token(&mut self) -> &mut Self {
        self.0.incr_token();
        self
    }

    fn token(&self) -> u64 {
        self.0.token()
    }

    fn set_broken(&mut self, b: bool) -> &mut Self {
        self.0.set_broken(b);
        self
    }

    fn broken(&self) -> bool {
        self.0.broken()
    }
}

impl Session for Pool {
    type Connection = PooledConnection;

    fn get(&self) -> Result<PooledConnection> {
        let cfg = config().read();
        let pool = pool().read();
        match *pool {
            Some(ref pool) => {
                let msg = String::from("Failed to get a connection.");
                let mut last_error = err!(ConnectionError::Other(msg));
                macro_rules! return_conn {
                    ($e:expr) => {{
                        match $e {
                            Ok(mut conn) => {
                                conn.incr_token();
                                return Ok(PooledConnection(conn));
                            },
                            Err(error) => last_error = err!(error),
                        }
                    }}
                }
                let mut num_retries = cfg.retries();
                while num_retries > 0 {
                    let mut least_connections = 0;
                    let mut least_connected_server = 0;
                    let mut most_idle = 0;
                    let mut most_idle_server = 0;
                    for (i, p) in pool.iter().enumerate() {
                        let state = p.state();
                        if least_connections == 0 || least_connections > state.connections {
                            least_connections = state.connections;
                            least_connected_server = i
                        }
                        if most_idle == 0 || most_idle < state.idle_connections {
                            most_idle = state.idle_connections;
                            most_idle_server = i
                        }
                    }
                    if most_idle > 0 {
                        return_conn!(pool[most_idle_server].get());
                    } else if least_connections > 0 {
                        return_conn!(pool[least_connected_server].get());
                    } else {
                        let msg = String::from("All servers are currently down.");
                        last_error = err!(ConnectionError::Other(msg));
                    }
                    num_retries -= 1;
                }
                return last_error;
            }
            None => {
                let msg = String::from("Your connection pool is not initialised. \
                                   Use `r.connection().connect()` to initialise the pool \
                                   before trying to send any connections to the database. \
                                   This is typically done in the `main` function.");
                return err!(ConnectionError::Other(msg));
            }
        }
    }
}

fn pool() -> &'static RwLock<Option<Vec<r2d2::Pool<ConnectionManager>>>> {
    &POOL
}

fn set_pool(p: Vec<r2d2::Pool<ConnectionManager>>) {
    let mut pool = POOL.write();
    *pool = Some(p);
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-cross-compile

#![feature(rustc_private)]

extern crate syntax;

use syntax::ast::*;
use syntax::attr::*;
use syntax::ast;
use syntax::codemap::{FilePathMapping, FileName};
use syntax::parse;
use syntax::parse::{ParseSess, PResult};
use syntax::parse::new_parser_from_source_str;
use syntax::parse::parser::Parser;
use syntax::parse::token;
use syntax::ptr::P;
use syntax::str::char_at;
use syntax::parse::attr::*;
use syntax::print::pprust;
use std::fmt;

// Copied out of syntax::util::parser_testing

pub fn string_to_parser<'a>(ps: &'a ParseSess, source_str: String) -> Parser<'a> {
    new_parser_from_source_str(ps, FileName::Custom("bogofile".to_owned()), source_str)
}

fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> PResult<'a, T> where
    F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>,
{
    let mut p = string_to_parser(&ps, s);
    let x = f(&mut p);

    if ps.span_diagnostic.has_errors() || p.token != token::Eof {
        if let Err(mut e) = x {
            e.cancel();
        }
        return Err(p.fatal("parse error"));
    }

    x
}

fn expr<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, P<ast::Expr>> {
    with_error_checking_parse(s.to_string(), ps, |p| {
        p.parse_expr()
    })
}

fn stmt<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, ast::Stmt> {
    with_error_checking_parse(s.to_string(), ps, |p| {
        p.parse_stmt().map(|s| s.unwrap())
    })
}

fn attr<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, ast::Attribute> {
    with_error_checking_parse(s.to_string(), ps, |p| {
        p.parse_attribute(true)
    })
}

fn str_compare<T, F: Fn(&T) -> String>(e: &str, expected: &[T], actual: &[T], f: F) {
    let expected: Vec<_> = expected.iter().map(|e| f(e)).collect();
    let actual: Vec<_> = actual.iter().map(|e| f(e)).collect();

    if expected != actual {
        panic!("parsed `{}` as {:?}, expected {:?}", e, actual, expected);
    }
}

fn check_expr_attrs(es: &str, expected: &[&str]) {
    let ps = ParseSess::new(FilePathMapping::empty());
    let e = expr(es, &ps).expect("parse error");
    let actual = &e.attrs;
    str_compare(es,
                &expected.iter().map(|r| attr(r, &ps).unwrap()).collect::<Vec<_>>(),
                &actual,
                pprust::attribute_to_string);
}

fn check_stmt_attrs(es: &str, expected: &[&str]) {
    let ps = ParseSess::new(FilePathMapping::empty());
    let e = stmt(es, &ps).expect("parse error");
    let actual = e.node.attrs();
    str_compare(es,
                &expected.iter().map(|r| attr(r, &ps).unwrap()).collect::<Vec<_>>(),
                actual,
                pprust::attribute_to_string);
}

fn reject_expr_parse(es: &str) {
    let ps = ParseSess::new(FilePathMapping::empty());
    match expr(es, &ps) {
        Ok(_) => panic!("parser did not reject `{}`", es),
        Err(mut e) => e.cancel(),
    };
}

fn reject_stmt_parse(es: &str) {
    let ps = ParseSess::new(FilePathMapping::empty());
    match stmt(es, &ps) {
        Ok(_) => panic!("parser did not reject `{}`", es),
        Err(mut e) => e.cancel(),
    };
}

fn main() {
    syntax::with_globals(|| run());
}

fn run() {
    let both = &["#[attr]", "#![attr]"];
    let outer = &["#[attr]"];
    let none = &[];

    check_expr_attrs("#[attr] box 0", outer);
    reject_expr_parse("box #![attr] 0");

    check_expr_attrs("#[attr] [#![attr]]", both);
    check_expr_attrs("#[attr] [#![attr] 0]", both);
    check_expr_attrs("#[attr] [#![attr] 0; 0]", both);
    check_expr_attrs("#[attr] [#![attr] 0, 0, 0]", both);
    reject_expr_parse("[#[attr]]");

    check_expr_attrs("#[attr] foo()", outer);
    check_expr_attrs("#[attr] x.foo()", outer);
    reject_expr_parse("foo#[attr]()");
    reject_expr_parse("foo(#![attr])");
    reject_expr_parse("x.foo(#![attr])");
    reject_expr_parse("x.#[attr]foo()");
    reject_expr_parse("x.#![attr]foo()");

    check_expr_attrs("#[attr] (#![attr])", both);
    check_expr_attrs("#[attr] (#![attr] #[attr] 0,)", both);
    check_expr_attrs("#[attr] (#![attr] #[attr] 0, 0)", both);

    check_expr_attrs("#[attr] 0 + #[attr] 0", none);
    check_expr_attrs("#[attr] 0 / #[attr] 0", none);
    check_expr_attrs("#[attr] 0 & #[attr] 0", none);
    check_expr_attrs("#[attr] 0 % #[attr] 0", none);
    check_expr_attrs("#[attr] (0 + 0)", outer);
    reject_expr_parse("0 + #![attr] 0");

    check_expr_attrs("#[attr] !0", outer);
    check_expr_attrs("#[attr] -0", outer);
    reject_expr_parse("!#![attr] 0");
    reject_expr_parse("-#![attr] 0");

    check_expr_attrs("#[attr] false", outer);
    check_expr_attrs("#[attr] 0", outer);
    check_expr_attrs("#[attr] 'c'", outer);

    check_expr_attrs("#[attr] x as Y", none);
    check_expr_attrs("#[attr] (x as Y)", outer);
    reject_expr_parse("x #![attr] as Y");

    reject_expr_parse("#[attr] if false {}");
    reject_expr_parse("if false #[attr] {}");
    reject_expr_parse("if false {#![attr]}");
    reject_expr_parse("if false {} #[attr] else {}");
    reject_expr_parse("if false {} else #[attr] {}");
    reject_expr_parse("if false {} else {#![attr]}");
    reject_expr_parse("if false {} else #[attr] if true {}");
    reject_expr_parse("if false {} else if true #[attr] {}");
    reject_expr_parse("if false {} else if true {#![attr]}");

    reject_expr_parse("#[attr] if let Some(false) = false {}");
    reject_expr_parse("if let Some(false) = false #[attr] {}");
    reject_expr_parse("if let Some(false) = false {#![attr]}");
    reject_expr_parse("if let Some(false) = false {} #[attr] else {}");
    reject_expr_parse("if let Some(false) = false {} else #[attr] {}");
    reject_expr_parse("if let Some(false) = false {} else {#![attr]}");
    reject_expr_parse("if let Some(false) = false {} else #[attr] if let Some(false) = true {}");
    reject_expr_parse("if let Some(false) = false {} else if let Some(false) = true #[attr] {}");
    reject_expr_parse("if let Some(false) = false {} else if let Some(false) = true {#![attr]}");

    check_expr_attrs("#[attr] while true {#![attr]}", both);

    check_expr_attrs("#[attr] while let Some(false) = true {#![attr]}", both);

    check_expr_attrs("#[attr] for x in y {#![attr]}", both);

    check_expr_attrs("#[attr] loop {#![attr]}", both);

    check_expr_attrs("#[attr] match true {#![attr] #[attr] _ => false}", both);

    check_expr_attrs("#[attr]      || #[attr] foo", outer);
    check_expr_attrs("#[attr] move || #[attr] foo", outer);
    check_expr_attrs("#[attr]      || #[attr] { #![attr] foo }", outer);
    check_expr_attrs("#[attr] move || #[attr] { #![attr] foo }", outer);
    check_expr_attrs("#[attr]      || { #![attr] foo }", outer);
    check_expr_attrs("#[attr] move || { #![attr] foo }", outer);
    reject_expr_parse("|| #![attr] foo");
    reject_expr_parse("move || #![attr] foo");
    reject_expr_parse("|| #![attr] {foo}");
    reject_expr_parse("move || #![attr] {foo}");

    check_expr_attrs("#[attr] { #![attr] }", both);
    check_expr_attrs("#[attr] { #![attr] let _ = (); }", both);
    check_expr_attrs("#[attr] { #![attr] let _ = (); foo }", both);

    check_expr_attrs("#[attr] x = y", none);
    check_expr_attrs("#[attr] (x = y)", outer);

    check_expr_attrs("#[attr] x += y", none);
    check_expr_attrs("#[attr] (x += y)", outer);

    check_expr_attrs("#[attr] foo.bar", outer);
    check_expr_attrs("(#[attr] foo).bar", none);

    check_expr_attrs("#[attr] foo.0", outer);
    check_expr_attrs("(#[attr] foo).0", none);

    check_expr_attrs("#[attr] foo[bar]", outer);
    check_expr_attrs("(#[attr] foo)[bar]", none);

    check_expr_attrs("#[attr] 0..#[attr] 0", none);
    check_expr_attrs("#[attr] 0..", none);
    reject_expr_parse("#[attr] ..#[attr] 0");
    reject_expr_parse("#[attr] ..");

    check_expr_attrs("#[attr] (0..0)", outer);
    check_expr_attrs("#[attr] (0..)", outer);
    check_expr_attrs("#[attr] (..0)", outer);
    check_expr_attrs("#[attr] (..)", outer);

    check_expr_attrs("#[attr] foo::bar::baz", outer);

    check_expr_attrs("#[attr] &0", outer);
    check_expr_attrs("#[attr] &mut 0", outer);
    check_expr_attrs("#[attr] & #[attr] 0", outer);
    check_expr_attrs("#[attr] &mut #[attr] 0", outer);
    reject_expr_parse("#[attr] &#![attr] 0");
    reject_expr_parse("#[attr] &mut #![attr] 0");

    check_expr_attrs("#[attr] break", outer);
    check_expr_attrs("#[attr] continue", outer);
    check_expr_attrs("#[attr] return", outer);

    check_expr_attrs("#[attr] foo!()", outer);
    check_expr_attrs("#[attr] foo!(#![attr])", outer);
    check_expr_attrs("#[attr] foo![]", outer);
    check_expr_attrs("#[attr] foo![#![attr]]", outer);
    check_expr_attrs("#[attr] foo!{}", outer);
    check_expr_attrs("#[attr] foo!{#![attr]}", outer);

    check_expr_attrs("#[attr] Foo { #![attr] bar: baz }", both);
    check_expr_attrs("#[attr] Foo { #![attr] ..foo }", both);
    check_expr_attrs("#[attr] Foo { #![attr] bar: baz, ..foo }", both);

    check_expr_attrs("#[attr] (#![attr] 0)", both);

    // Look at statements in their natural habitat...
    check_expr_attrs("{
        #[attr] let _ = 0;
        #[attr] 0;
        #[attr] foo!();
        #[attr] foo!{}
        #[attr] foo![];
    }", none);

    check_stmt_attrs("#[attr] let _ = 0", outer);
    check_stmt_attrs("#[attr] 0",         outer);
    check_stmt_attrs("#[attr] {#![attr]}", both);
    check_stmt_attrs("#[attr] foo!()",    outer);
    check_stmt_attrs("#[attr] foo![]",    outer);
    check_stmt_attrs("#[attr] foo!{}",    outer);

    reject_stmt_parse("#[attr] #![attr] let _ = 0");
    reject_stmt_parse("#[attr] #![attr] 0");
    reject_stmt_parse("#[attr] #![attr] foo!()");
    reject_stmt_parse("#[attr] #![attr] foo![]");
    reject_stmt_parse("#[attr] #![attr] foo!{}");

    // FIXME: Allow attributes in pattern constexprs?
    // would require parens in patterns to allow disambiguation...

    reject_expr_parse("match 0 {
        0..=#[attr] 10 => ()
    }");
    reject_expr_parse("match 0 {
        0..=#[attr] -10 => ()
    }");
    reject_expr_parse("match 0 {
        0..=-#[attr] 10 => ()
    }");
    reject_expr_parse("match 0 {
        0..=#[attr] FOO => ()
    }");

    // make sure we don't catch this bug again...
    reject_expr_parse("{
        fn foo() {
            #[attr];
        }
    }");
    reject_expr_parse("{
        fn foo() {
            #[attr]
        }
    }");
}
<commit_msg>driveby status update to 2015 comment about parens in patterns<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-cross-compile

#![feature(rustc_private)]

extern crate syntax;

use syntax::ast::*;
use syntax::attr::*;
use syntax::ast;
use syntax::codemap::{FilePathMapping, FileName};
use syntax::parse;
use syntax::parse::{ParseSess, PResult};
use syntax::parse::new_parser_from_source_str;
use syntax::parse::parser::Parser;
use syntax::parse::token;
use syntax::ptr::P;
use syntax::str::char_at;
use syntax::parse::attr::*;
use syntax::print::pprust;
use std::fmt;

// Copied out of syntax::util::parser_testing

pub fn string_to_parser<'a>(ps: &'a ParseSess, source_str: String) -> Parser<'a> {
    new_parser_from_source_str(ps, FileName::Custom("bogofile".to_owned()), source_str)
}

fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> PResult<'a, T> where
    F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>,
{
    let mut p = string_to_parser(&ps, s);
    let x = f(&mut p);

    if ps.span_diagnostic.has_errors() || p.token != token::Eof {
        if let Err(mut e) = x {
            e.cancel();
        }
        return Err(p.fatal("parse error"));
    }

    x
}

fn expr<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, P<ast::Expr>> {
    with_error_checking_parse(s.to_string(), ps, |p| {
        p.parse_expr()
    })
}

fn stmt<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, ast::Stmt> {
    with_error_checking_parse(s.to_string(), ps, |p| {
        p.parse_stmt().map(|s| s.unwrap())
    })
}

fn attr<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, ast::Attribute> {
    with_error_checking_parse(s.to_string(), ps, |p| {
        p.parse_attribute(true)
    })
}

fn str_compare<T, F: Fn(&T) -> String>(e: &str, expected: &[T], actual: &[T], f: F) {
    let expected: Vec<_> = expected.iter().map(|e| f(e)).collect();
    let actual: Vec<_> = actual.iter().map(|e| f(e)).collect();

    if expected != actual {
        panic!("parsed `{}` as {:?}, expected {:?}", e, actual, expected);
    }
}

fn check_expr_attrs(es: &str, expected: &[&str]) {
    let ps = ParseSess::new(FilePathMapping::empty());
    let e = expr(es, &ps).expect("parse error");
    let actual = &e.attrs;
    str_compare(es,
                &expected.iter().map(|r| attr(r, &ps).unwrap()).collect::<Vec<_>>(),
                &actual,
                pprust::attribute_to_string);
}

fn check_stmt_attrs(es: &str, expected: &[&str]) {
    let ps = ParseSess::new(FilePathMapping::empty());
    let e = stmt(es, &ps).expect("parse error");
    let actual = e.node.attrs();
    str_compare(es,
                &expected.iter().map(|r| attr(r, &ps).unwrap()).collect::<Vec<_>>(),
                actual,
                pprust::attribute_to_string);
}

fn reject_expr_parse(es: &str) {
    let ps = ParseSess::new(FilePathMapping::empty());
    match expr(es, &ps) {
        Ok(_) => panic!("parser did not reject `{}`", es),
        Err(mut e) => e.cancel(),
    };
}

fn reject_stmt_parse(es: &str) {
    let ps = ParseSess::new(FilePathMapping::empty());
    match stmt(es, &ps) {
        Ok(_) => panic!("parser did not reject `{}`", es),
        Err(mut e) => e.cancel(),
    };
}

fn main() {
    syntax::with_globals(|| run());
}

fn run() {
    let both = &["#[attr]", "#![attr]"];
    let outer = &["#[attr]"];
    let none = &[];

    check_expr_attrs("#[attr] box 0", outer);
    reject_expr_parse("box #![attr] 0");

    check_expr_attrs("#[attr] [#![attr]]", both);
    check_expr_attrs("#[attr] [#![attr] 0]", both);
    check_expr_attrs("#[attr] [#![attr] 0; 0]", both);
    check_expr_attrs("#[attr] [#![attr] 0, 0, 0]", both);
    reject_expr_parse("[#[attr]]");

    check_expr_attrs("#[attr] foo()", outer);
    check_expr_attrs("#[attr] x.foo()", outer);
    reject_expr_parse("foo#[attr]()");
    reject_expr_parse("foo(#![attr])");
    reject_expr_parse("x.foo(#![attr])");
    reject_expr_parse("x.#[attr]foo()");
    reject_expr_parse("x.#![attr]foo()");

    check_expr_attrs("#[attr] (#![attr])", both);
    check_expr_attrs("#[attr] (#![attr] #[attr] 0,)", both);
    check_expr_attrs("#[attr] (#![attr] #[attr] 0, 0)", both);

    check_expr_attrs("#[attr] 0 + #[attr] 0", none);
    check_expr_attrs("#[attr] 0 / #[attr] 0", none);
    check_expr_attrs("#[attr] 0 & #[attr] 0", none);
    check_expr_attrs("#[attr] 0 % #[attr] 0", none);
    check_expr_attrs("#[attr] (0 + 0)", outer);
    reject_expr_parse("0 + #![attr] 0");

    check_expr_attrs("#[attr] !0", outer);
    check_expr_attrs("#[attr] -0", outer);
    reject_expr_parse("!#![attr] 0");
    reject_expr_parse("-#![attr] 0");

    check_expr_attrs("#[attr] false", outer);
    check_expr_attrs("#[attr] 0", outer);
    check_expr_attrs("#[attr] 'c'", outer);

    check_expr_attrs("#[attr] x as Y", none);
    check_expr_attrs("#[attr] (x as Y)", outer);
    reject_expr_parse("x #![attr] as Y");

    reject_expr_parse("#[attr] if false {}");
    reject_expr_parse("if false #[attr] {}");
    reject_expr_parse("if false {#![attr]}");
    reject_expr_parse("if false {} #[attr] else {}");
    reject_expr_parse("if false {} else #[attr] {}");
    reject_expr_parse("if false {} else {#![attr]}");
    reject_expr_parse("if false {} else #[attr] if true {}");
    reject_expr_parse("if false {} else if true #[attr] {}");
    reject_expr_parse("if false {} else if true {#![attr]}");

    reject_expr_parse("#[attr] if let Some(false) = false {}");
    reject_expr_parse("if let Some(false) = false #[attr] {}");
    reject_expr_parse("if let Some(false) = false {#![attr]}");
    reject_expr_parse("if let Some(false) = false {} #[attr] else {}");
    reject_expr_parse("if let Some(false) = false {} else #[attr] {}");
    reject_expr_parse("if let Some(false) = false {} else {#![attr]}");
    reject_expr_parse("if let Some(false) = false {} else #[attr] if let Some(false) = true {}");
    reject_expr_parse("if let Some(false) = false {} else if let Some(false) = true #[attr] {}");
    reject_expr_parse("if let Some(false) = false {} else if let Some(false) = true {#![attr]}");

    check_expr_attrs("#[attr] while true {#![attr]}", both);

    check_expr_attrs("#[attr] while let Some(false) = true {#![attr]}", both);

    check_expr_attrs("#[attr] for x in y {#![attr]}", both);

    check_expr_attrs("#[attr] loop {#![attr]}", both);

    check_expr_attrs("#[attr] match true {#![attr] #[attr] _ => false}", both);

    check_expr_attrs("#[attr]      || #[attr] foo", outer);
    check_expr_attrs("#[attr] move || #[attr] foo", outer);
    check_expr_attrs("#[attr]      || #[attr] { #![attr] foo }", outer);
    check_expr_attrs("#[attr] move || #[attr] { #![attr] foo }", outer);
    check_expr_attrs("#[attr]      || { #![attr] foo }", outer);
    check_expr_attrs("#[attr] move || { #![attr] foo }", outer);
    reject_expr_parse("|| #![attr] foo");
    reject_expr_parse("move || #![attr] foo");
    reject_expr_parse("|| #![attr] {foo}");
    reject_expr_parse("move || #![attr] {foo}");

    check_expr_attrs("#[attr] { #![attr] }", both);
    check_expr_attrs("#[attr] { #![attr] let _ = (); }", both);
    check_expr_attrs("#[attr] { #![attr] let _ = (); foo }", both);

    check_expr_attrs("#[attr] x = y", none);
    check_expr_attrs("#[attr] (x = y)", outer);

    check_expr_attrs("#[attr] x += y", none);
    check_expr_attrs("#[attr] (x += y)", outer);

    check_expr_attrs("#[attr] foo.bar", outer);
    check_expr_attrs("(#[attr] foo).bar", none);

    check_expr_attrs("#[attr] foo.0", outer);
    check_expr_attrs("(#[attr] foo).0", none);

    check_expr_attrs("#[attr] foo[bar]", outer);
    check_expr_attrs("(#[attr] foo)[bar]", none);

    check_expr_attrs("#[attr] 0..#[attr] 0", none);
    check_expr_attrs("#[attr] 0..", none);
    reject_expr_parse("#[attr] ..#[attr] 0");
    reject_expr_parse("#[attr] ..");

    check_expr_attrs("#[attr] (0..0)", outer);
    check_expr_attrs("#[attr] (0..)", outer);
    check_expr_attrs("#[attr] (..0)", outer);
    check_expr_attrs("#[attr] (..)", outer);

    check_expr_attrs("#[attr] foo::bar::baz", outer);

    check_expr_attrs("#[attr] &0", outer);
    check_expr_attrs("#[attr] &mut 0", outer);
    check_expr_attrs("#[attr] & #[attr] 0", outer);
    check_expr_attrs("#[attr] &mut #[attr] 0", outer);
    reject_expr_parse("#[attr] &#![attr] 0");
    reject_expr_parse("#[attr] &mut #![attr] 0");

    check_expr_attrs("#[attr] break", outer);
    check_expr_attrs("#[attr] continue", outer);
    check_expr_attrs("#[attr] return", outer);

    check_expr_attrs("#[attr] foo!()", outer);
    check_expr_attrs("#[attr] foo!(#![attr])", outer);
    check_expr_attrs("#[attr] foo![]", outer);
    check_expr_attrs("#[attr] foo![#![attr]]", outer);
    check_expr_attrs("#[attr] foo!{}", outer);
    check_expr_attrs("#[attr] foo!{#![attr]}", outer);

    check_expr_attrs("#[attr] Foo { #![attr] bar: baz }", both);
    check_expr_attrs("#[attr] Foo { #![attr] ..foo }", both);
    check_expr_attrs("#[attr] Foo { #![attr] bar: baz, ..foo }", both);

    check_expr_attrs("#[attr] (#![attr] 0)", both);

    // Look at statements in their natural habitat...
    check_expr_attrs("{
        #[attr] let _ = 0;
        #[attr] 0;
        #[attr] foo!();
        #[attr] foo!{}
        #[attr] foo![];
    }", none);

    check_stmt_attrs("#[attr] let _ = 0", outer);
    check_stmt_attrs("#[attr] 0",         outer);
    check_stmt_attrs("#[attr] {#![attr]}", both);
    check_stmt_attrs("#[attr] foo!()",    outer);
    check_stmt_attrs("#[attr] foo![]",    outer);
    check_stmt_attrs("#[attr] foo!{}",    outer);

    reject_stmt_parse("#[attr] #![attr] let _ = 0");
    reject_stmt_parse("#[attr] #![attr] 0");
    reject_stmt_parse("#[attr] #![attr] foo!()");
    reject_stmt_parse("#[attr] #![attr] foo![]");
    reject_stmt_parse("#[attr] #![attr] foo!{}");

    // FIXME: Allow attributes in pattern constexprs?
    // would require parens in patterns to allow disambiguation...
    // —which is now available under the `pattern_parentheses` feature gate
    // (tracking issue #51087)

    reject_expr_parse("match 0 {
        0..=#[attr] 10 => ()
    }");
    reject_expr_parse("match 0 {
        0..=#[attr] -10 => ()
    }");
    reject_expr_parse("match 0 {
        0..=-#[attr] 10 => ()
    }");
    reject_expr_parse("match 0 {
        0..=#[attr] FOO => ()
    }");

    // make sure we don't catch this bug again...
    reject_expr_parse("{
        fn foo() {
            #[attr];
        }
    }");
    reject_expr_parse("{
        fn foo() {
            #[attr]
        }
    }");
}
<|endoftext|>


<commit_before>use index::btree::external::ExtNode;
use index::btree::node::read_unchecked;
use index::btree::node::NodeData;
use index::btree::BPlusTree;
use index::btree::NodeCellRef;
use index::id_from_key;
use index::EntryKey;
use index::Slice;
use serde_json;
use std::fmt::Debug;
use std::fs::File;
use std::io::Write;
use itertools::Itertools;

#[derive(Serialize, Deserialize)]
struct DebugNode {
    keys: Vec<String>,
    nodes: Vec<DebugNode>,
    id: Option<String>,
    next: Option<String>,
    prev: Option<String>,
    len: usize,
    is_external: bool,
    bound: String,
}

pub fn dump_tree<KS, PS>(tree: &BPlusTree<KS, PS>, f: &str)
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    debug!("dumping {}", f);
    let debug_root = cascading_dump_node::<KS, PS>(&tree.get_root());
    let json = serde_json::to_string_pretty(&debug_root).unwrap();
    let mut file = File::create(f).unwrap();
    file.write_all(json.as_bytes());
}

fn cascading_dump_node<KS, PS>(node: &NodeCellRef) -> DebugNode
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    if node.is_default() {
        return DebugNode {
            keys: vec![String::from("<ERROR!!! DEFAULT NODE!!!>")],
            nodes: vec![],
            id: None,
            next: None,
            prev: None,
            len: 0,
            is_external: false,
            bound: "-".to_string(),
        };
    }
    let node = read_unchecked(&*node);
    match &*node {
        &NodeData::External(ref node) => {
            let node: &ExtNode<KS, PS> = node;
            let keys = node.keys.as_slice_immute()[..node.len]
                .iter()
                .map(|key| {
                    let id = id_from_key(key);
                    format!("{}\t{:?}", id.lower, key)
                })
                .collect();
            return DebugNode {
                keys,
                nodes: vec![],
                id: Some(format!("{:?}", node.id)),
                next: Some(format!(
                    "{:?}",
                    read_unchecked::<KS, PS>(&node.next).ext_id()
                )),
                prev: Some(format!(
                    "{:?}",
                    read_unchecked::<KS, PS>(&node.prev).ext_id()
                )),
                len: node.len,
                is_external: true,
                bound: format!("{:?}", node.right_bound),
            };
        }
        &NodeData::Internal(ref innode) => {
            let len = innode.len;
            let keys = innode.keys.as_slice_immute()[..node.len()]
                .iter()
                .map(|key| format!("{:?}", key))
                .collect();
            let nodes = innode.ptrs.as_slice_immute()[..node.len() + 1]
                .iter()
                .cloned()
                .collect_vec()
                .into_iter()
                .map(|node_ref| cascading_dump_node::<KS, PS>(&node_ref))
                .collect();
            return DebugNode {
                keys,
                nodes,
                id: None,
                next: Some(innode.right.to_string::<KS, PS>()),
                prev: None,
                len,
                is_external: false,
                bound: format!("{:?}", innode.right_bound),
            };
        }
        &NodeData::None => {
            return DebugNode {
                keys: vec![String::from("<NOT FOUND>")],
                nodes: vec![],
                id: None,
                next: None,
                prev: None,
                len: 0,
                is_external: false,
                bound: "-".to_string(),
            };
        }
        &NodeData::Empty(ref n) => {
            return DebugNode {
                keys: vec![String::from("<EMPTY>")],
                nodes: vec![],
                id: None,
                next: Some(n.right.to_string::<KS, PS>()),
                prev: None,
                len: 0,
                is_external: false,
                bound: "-".to_string(),
            };
        }
    }
}
<commit_msg>make comment<commit_after>use index::btree::external::ExtNode;
use index::btree::node::read_unchecked;
use index::btree::node::NodeData;
use index::btree::BPlusTree;
use index::btree::NodeCellRef;
use index::id_from_key;
use index::EntryKey;
use index::Slice;
use serde_json;
use std::fmt::Debug;
use std::fs::File;
use std::io::Write;
use itertools::Itertools;

#[derive(Serialize, Deserialize)]
struct DebugNode {
    keys: Vec<String>,
    nodes: Vec<DebugNode>,
    id: Option<String>,
    next: Option<String>,
    prev: Option<String>,
    len: usize,
    is_external: bool,
    bound: String,
}

pub fn dump_tree<KS, PS>(tree: &BPlusTree<KS, PS>, f: &str)
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    debug!("dumping {}", f);
    let debug_root = cascading_dump_node::<KS, PS>(&tree.get_root());
    let json = serde_json::to_string_pretty(&debug_root).unwrap();
    let mut file = File::create(f).unwrap();
    file.write_all(json.as_bytes());
}

fn cascading_dump_node<KS, PS>(node: &NodeCellRef) -> DebugNode
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    if node.is_default() {
        return DebugNode {
            keys: vec![String::from("<ERROR!!! DEFAULT NODE!!!>")],
            nodes: vec![],
            id: None,
            next: None,
            prev: None,
            len: 0,
            is_external: false,
            bound: "-".to_string(),
        };
    }
    let node = read_unchecked(&*node);
    match &*node {
        &NodeData::External(ref node) => {
            let node: &ExtNode<KS, PS> = node;
            let keys = node.keys.as_slice_immute()[..node.len]
                .iter()
                .map(|key| {
                    let id = id_from_key(key);
                    format!("{}\t{:?}", id.lower, key)
                })
                .collect();
            return DebugNode {
                keys,
                nodes: vec![],
                id: Some(format!("{:?}", node.id)),
                next: Some(format!(
                    "{:?}",
                    read_unchecked::<KS, PS>(&node.next).ext_id()
                )),
                prev: Some(format!(
                    "{:?}",
                    read_unchecked::<KS, PS>(&node.prev).ext_id()
                )),
                len: node.len,
                is_external: true,
                bound: format!("{:?}", node.right_bound),
            };
        }
        &NodeData::Internal(ref innode) => {
            let len = innode.len;
            let keys = innode.keys.as_slice_immute()[..node.len()]
                .iter()
                .map(|key| format!("{:?}", key))
                .collect();
            let nodes = innode.ptrs.as_slice_immute()[..node.len() + 1]
                .iter()
                .cloned()
                .collect_vec()  // clone all ptrs before cascading dump
                .into_iter()    // or the NodeCellRef may be reclaimed in the middle of dump
                .map(|node_ref| cascading_dump_node::<KS, PS>(&node_ref))
                .collect();
            return DebugNode {
                keys,
                nodes,
                id: None,
                next: Some(innode.right.to_string::<KS, PS>()),
                prev: None,
                len,
                is_external: false,
                bound: format!("{:?}", innode.right_bound),
            };
        }
        &NodeData::None => {
            return DebugNode {
                keys: vec![String::from("<NOT FOUND>")],
                nodes: vec![],
                id: None,
                next: None,
                prev: None,
                len: 0,
                is_external: false,
                bound: "-".to_string(),
            };
        }
        &NodeData::Empty(ref n) => {
            return DebugNode {
                keys: vec![String::from("<EMPTY>")],
                nodes: vec![],
                id: None,
                next: Some(n.right.to_string::<KS, PS>()),
                prev: None,
                len: 0,
                is_external: false,
                bound: "-".to_string(),
            };
        }
    }
}
<|endoftext|>