<commit_before>#![ allow (unused_parens) ]

extern crate num_cpus;
extern crate rzbackup;

#[ macro_use ]
extern crate clap;

use clap::App;
use clap::Arg;
use clap::ArgMatches;

use std::process;

use rzbackup::Repository;
use rzbackup::RepositoryConfig;

struct DefaultArgumentStringValues {
	max_uncompressed_memory_cache_entries: String,
	max_compressed_memory_cache_entries: String,
	max_compressed_filesystem_cache_entries: String,
	max_threads: String,
	filesystem_cache_path: String,
}

fn build_default_argument_string_values (
) -> DefaultArgumentStringValues {

	DefaultArgumentStringValues {

		max_uncompressed_memory_cache_entries:
			rzbackup::MAX_UNCOMPRESSED_MEMORY_CACHE_ENTRIES.to_string (),

		max_compressed_memory_cache_entries:
			rzbackup::MAX_COMPRESSED_MEMORY_CACHE_ENTRIES.to_string (),

		max_compressed_filesystem_cache_entries:
			rzbackup::MAX_COMPRESSED_FILESYSTEM_CACHE_ENTRIES.to_string (),

		max_threads:
			num_cpus::get ().to_string (),

		filesystem_cache_path:
			rzbackup::FILESYSTEM_CACHE_PATH.to_string (),

	}

}

fn main () {

	let default_argument_string_values =
		build_default_argument_string_values ();

	let clap_app =
		build_clap_app (
			& default_argument_string_values);

	let argument_matches =
		clap_app.get_matches ();

	let listen_address =
		argument_matches.value_of (
			"listen-address",
		).unwrap ();

	let repository_path =
		argument_matches.value_of (
			"repository-path",
		).unwrap ();

	let password_file_path =
		argument_matches.value_of (
			"password-file");

	let repository_config =
		build_repository_config (
			& argument_matches);

	let repository = match (

		Repository::open (
			repository_config,
			repository_path,
			password_file_path,
		)

	) {

		Ok (repository) =>
			repository,

		Err (error) => {

			println! (
				"Error opening repository: {}",
				error);

			process::exit (1);

		},

	};

	println! (
		"RZBackup startup complete");

	match rzbackup::run_server (
		repository,
		listen_address) {

		Ok (_) =>
			(),

		Err (error) => {

			println! (
				"RZBackup server encountered error: {}",
				error);

			process::exit (1);

		},

	};

	println! (
		"RZBackup server terminating");

}

fn build_repository_config (
	argument_matches: & ArgMatches,
) -> RepositoryConfig {

	RepositoryConfig {

		max_uncompressed_memory_cache_entries:
			value_t! (
				argument_matches,
				"max-uncompressed-memory-cache-entries",
				usize
			).unwrap_or_else (
				|error|
				error.exit ()
			),

		max_compressed_memory_cache_entries:
			value_t! (
				argument_matches,
				"max-compressed-memory-cache-entries",
				usize
			).unwrap_or_else (
				|error|
				error.exit ()
			),

		max_compressed_filesystem_cache_entries:
			value_t! (
				argument_matches,
				"max-compressed-filesystem-cache-entries",
				usize
			).unwrap_or_else (
				|error|
				error.exit ()
			),

		max_threads:
			value_t! (
				argument_matches,
				"max-threads",
				usize
			).unwrap_or_else (
				|error|
				error.exit ()
			),

		filesystem_cache_path:
			argument_matches.value_of (
				"filesystem-cache-path",
			).unwrap ().to_owned (),

	}

}

fn build_clap_app <'a, 'b> (
	defaults: & 'a DefaultArgumentStringValues,
) -> App <'a, 'b> {

	App::new (
		"rzbackup-server")

	.version (
		rzbackup::VERSION)

	.author (
		"James Pharaoh <james@wellbehavedsoftware.com>")

	.about (
		"Server component of partial ZBackup clone")

	.arg (
		Arg::with_name (
			"repository-path",
		).long (
			"repository-path",
		).value_name (
			"REPOSITORY",
		).help (
			"Path of the ZBackup repository to serve backups from.",
		).required (
			true,
		).takes_value (
			true,
		)
	)

	.arg (
		Arg::with_name (
			"password-file",
		).long (
			"password-file",
		).value_name (
			"PASSWORD-FILE",
		).help (
			"Path to the file containing encryption password, must be \
			present to access an encrypted repository and absent if the \
			repository is not encrypted.",
		).required (
			false,
		).takes_value (
			true,
		)
	)

	.arg (
		Arg::with_name (
			"listen-address",
		).long (
			"listen-address",
		).value_name (
			"ADDRESS:PORT",
		).help (
			"Address to listen on, in host:port or ip:port format.",
		).required (
			false,
		).default_value (
			"localhost:4152",
		)
	)

	.arg (
		Arg::with_name (
			"max-uncompressed-memory-cache-entries",
		).long (
			"max-uncompressed-memory-cache-entries",
		).value_name (
			"ENTRIES",
		).help (
			"Size of high-speed, fairly-high-cost, in memory cache of \
			compressed cache entries."
		).required (
			false,
		).default_value (
			& defaults.max_uncompressed_memory_cache_entries,
		)
	)

	.arg (
		Arg::with_name (
			"max-compressed-memory-cache-entries",
		).long (
			"max-compressed-memory-cache-entries",
		).value_name (
			"ENTRIES",
		).help (
			"Size of high-speed, fairly-high-cost, in memory cache of \
			compressed cache entries."
		).required (
			false,
		).default_value (
			& defaults.max_compressed_memory_cache_entries,
		)
	)

	.arg (
		Arg::with_name (
			"max-compressed-filesystem-cache-entries",
		).long (
			"max-compressed-filesystem-cache-entries",
		).value_name (
			"ENTRIES",
		).help (
			"Size of lower-speed, relatively low-cost, on disk compressed \
			cache entries."
		).required (
			false,
		).default_value (
			& defaults.max_compressed_filesystem_cache_entries,
		)
	)

	.arg (
		Arg::with_name (
			"max-threads",
		).long (
			"max-threads",
		).value_name (
			"THREADS",
		).help (
			"Number of worker threads to execute. The default value is \
			determined by the number of CPU threads reported by the operating \
			system.",
		).required (
			false,
		).default_value (
			& defaults.max_threads,
		)
	)

	.arg (
		Arg::with_name (
			"filesystem-cache-path",
		).long (
			"filesystem-cache-path",
		).value_name (
			"PATH",
		).help (
			"Location of the filesystem cache. This will be used to store \
			chunks which have been decompressed, typically from slow but \
			efficient LZMA compressed bundles, and recompressed using LZO, \
			then saved to disk and managed individually using an LRU cache \
			algorithm."
		).required (
			false,
		).default_value (
			& defaults.filesystem_cache_path,
		)
	)

}

// ex: noet ts=4 filetype=rust
<commit_msg>improve server exit handling<commit_after>#![ allow (unused_parens) ]

extern crate num_cpus;
extern crate rzbackup;

#[ macro_use ]
extern crate clap;

use clap::App;
use clap::Arg;
use clap::ArgMatches;

use std::process;

use rzbackup::Repository;
use rzbackup::RepositoryConfig;

struct DefaultArgumentStringValues {
	max_uncompressed_memory_cache_entries: String,
	max_compressed_memory_cache_entries: String,
	max_compressed_filesystem_cache_entries: String,
	max_threads: String,
	filesystem_cache_path: String,
}

fn build_default_argument_string_values (
) -> DefaultArgumentStringValues {

	DefaultArgumentStringValues {

		max_uncompressed_memory_cache_entries:
			rzbackup::MAX_UNCOMPRESSED_MEMORY_CACHE_ENTRIES.to_string (),

		max_compressed_memory_cache_entries:
			rzbackup::MAX_COMPRESSED_MEMORY_CACHE_ENTRIES.to_string (),

		max_compressed_filesystem_cache_entries:
			rzbackup::MAX_COMPRESSED_FILESYSTEM_CACHE_ENTRIES.to_string (),

		max_threads:
			num_cpus::get ().to_string (),

		filesystem_cache_path:
			rzbackup::FILESYSTEM_CACHE_PATH.to_string (),

	}

}

fn main () {

	process::exit (
		main_real (),
	);

}

fn main_real (
) -> i32 {

	let default_argument_string_values =
		build_default_argument_string_values ();

	let clap_app =
		build_clap_app (
			& default_argument_string_values);

	let argument_matches =
		clap_app.get_matches ();

	let listen_address =
		argument_matches.value_of (
			"listen-address",
		).unwrap ();

	let repository_path =
		argument_matches.value_of (
			"repository-path",
		).unwrap ();

	let password_file_path =
		argument_matches.value_of (
			"password-file");

	let repository_config =
		build_repository_config (
			& argument_matches);

	let repository = match (

		Repository::open (
			repository_config,
			repository_path,
			password_file_path,
		)

	) {

		Ok (repository) =>
			repository,

		Err (error) => {

			println! (
				"Error opening repository: {}",
				error);

			return 1;

		},

	};

	println! (
		"RZBackup startup complete");

	match rzbackup::run_server (
		repository,
		listen_address) {

		Ok (_) =>
			(),

		Err (error) => {

			println! (
				"RZBackup server encountered error: {}",
				error);

			return 1;

		},

	};

	println! (
		"RZBackup server terminating normally");

	0

}

fn build_repository_config (
	argument_matches: & ArgMatches,
) -> RepositoryConfig {

	RepositoryConfig {

		max_uncompressed_memory_cache_entries:
			value_t! (
				argument_matches,
				"max-uncompressed-memory-cache-entries",
				usize
			).unwrap_or_else (
				|error|
				error.exit ()
			),

		max_compressed_memory_cache_entries:
			value_t! (
				argument_matches,
				"max-compressed-memory-cache-entries",
				usize
			).unwrap_or_else (
				|error|
				error.exit ()
			),

		max_compressed_filesystem_cache_entries:
			value_t! (
				argument_matches,
				"max-compressed-filesystem-cache-entries",
				usize
			).unwrap_or_else (
				|error|
				error.exit ()
			),

		max_threads:
			value_t! (
				argument_matches,
				"max-threads",
				usize
			).unwrap_or_else (
				|error|
				error.exit ()
			),

		filesystem_cache_path:
			argument_matches.value_of (
				"filesystem-cache-path",
			).unwrap ().to_owned (),

	}

}

fn build_clap_app <'a, 'b> (
	defaults: & 'a DefaultArgumentStringValues,
) -> App <'a, 'b> {

	App::new (
		"rzbackup-server")

	.version (
		rzbackup::VERSION)

	.author (
		"James Pharaoh <james@wellbehavedsoftware.com>")

	.about (
		"Server component of partial ZBackup clone")

	.arg (
		Arg::with_name (
			"repository-path",
		).long (
			"repository-path",
		).value_name (
			"REPOSITORY",
		).help (
			"Path of the ZBackup repository to serve backups from.",
		).required (
			true,
		).takes_value (
			true,
		)
	)

	.arg (
		Arg::with_name (
			"password-file",
		).long (
			"password-file",
		).value_name (
			"PASSWORD-FILE",
		).help (
			"Path to the file containing encryption password, must be \
			present to access an encrypted repository and absent if the \
			repository is not encrypted.",
		).required (
			false,
		).takes_value (
			true,
		)
	)

	.arg (
		Arg::with_name (
			"listen-address",
		).long (
			"listen-address",
		).value_name (
			"ADDRESS:PORT",
		).help (
			"Address to listen on, in host:port or ip:port format.",
		).required (
			false,
		).default_value (
			"localhost:4152",
		)
	)

	.arg (
		Arg::with_name (
			"max-uncompressed-memory-cache-entries",
		).long (
			"max-uncompressed-memory-cache-entries",
		).value_name (
			"ENTRIES",
		).help (
			"Size of high-speed, fairly-high-cost, in memory cache of \
			compressed cache entries."
		).required (
			false,
		).default_value (
			& defaults.max_uncompressed_memory_cache_entries,
		)
	)

	.arg (
		Arg::with_name (
			"max-compressed-memory-cache-entries",
		).long (
			"max-compressed-memory-cache-entries",
		).value_name (
			"ENTRIES",
		).help (
			"Size of high-speed, fairly-high-cost, in memory cache of \
			compressed cache entries."
		).required (
			false,
		).default_value (
			& defaults.max_compressed_memory_cache_entries,
		)
	)

	.arg (
		Arg::with_name (
			"max-compressed-filesystem-cache-entries",
		).long (
			"max-compressed-filesystem-cache-entries",
		).value_name (
			"ENTRIES",
		).help (
			"Size of lower-speed, relatively low-cost, on disk compressed \
			cache entries."
		).required (
			false,
		).default_value (
			& defaults.max_compressed_filesystem_cache_entries,
		)
	)

	.arg (
		Arg::with_name (
			"max-threads",
		).long (
			"max-threads",
		).value_name (
			"THREADS",
		).help (
			"Number of worker threads to execute. The default value is \
			determined by the number of CPU threads reported by the operating \
			system.",
		).required (
			false,
		).default_value (
			& defaults.max_threads,
		)
	)

	.arg (
		Arg::with_name (
			"filesystem-cache-path",
		).long (
			"filesystem-cache-path",
		).value_name (
			"PATH",
		).help (
			"Location of the filesystem cache. This will be used to store \
			chunks which have been decompressed, typically from slow but \
			efficient LZMA compressed bundles, and recompressed using LZO, \
			then saved to disk and managed individually using an LRU cache \
			algorithm."
		).required (
			false,
		).default_value (
			& defaults.filesystem_cache_path,
		)
	)

}

// ex: noet ts=4 filetype=rust
<|endoftext|>


<commit_before>//! Store XML element attributes and their values.

use std::mem;
use std::slice;
use std::str;

use markup5ever::{namespace_url, LocalName, Namespace, Prefix, QualName};
use string_cache::DefaultAtom;

use crate::util::{opt_utf8_cstr, utf8_cstr};

/// Type used to store attribute values.
///
/// Attribute values are often repeated in an SVG file, so we intern them using the
/// string_cache crate.
pub type AttributeValue = DefaultAtom;

/// Iterable wrapper for libxml2's representation of attribute/value.
///
/// See the [`new_from_xml2_attributes`] function for information.
///
/// [`new_from_xml2_attributes`]: #method.new_from_xml2_attributes
#[derive(Clone)]
pub struct Attributes(Vec<(QualName, AttributeValue)>);

/// Iterator from `Attributes.iter`.
pub struct AttributesIter<'a>(slice::Iter<'a, (QualName, AttributeValue)>);

impl Attributes {
    /// Creates an iterable `Attributes` from the C array of borrowed C strings.
    ///
    /// With libxml2's SAX parser, the caller's startElementNsSAX2Func
    /// callback gets passed a `xmlChar **` for attributes, which
    /// comes in groups of (localname/prefix/URI/value_start/value_end).
    /// In those, localname/prefix/URI are NUL-terminated strings;
    /// value_start and value_end point to the start-inclusive and
    /// end-exclusive bytes in the attribute's value.
    ///
    /// This function is unsafe because the caller must guarantee the following:
    ///
    /// * `attrs` is a valid pointer, with (n_attributes * 5) elements.
    ///
    /// * All strings are valid UTF-8.
    ///
    /// The lifetime of the `Attributes` should be considered the same as the lifetime of the
    /// `attrs` array, as the `Attributes` does not copy the strings - it directly stores pointers
    /// into that array's strings.
    pub unsafe fn new_from_xml2_attributes(
        n_attributes: usize,
        attrs: *const *const libc::c_char,
    ) -> Attributes {
        let mut array = Vec::new();

        if n_attributes > 0 && !attrs.is_null() {
            for attr in slice::from_raw_parts(attrs, n_attributes * 5).chunks_exact(5) {
                let localname = attr[0];
                let prefix = attr[1];
                let uri = attr[2];
                let value_start = attr[3];
                let value_end = attr[4];

                assert!(!localname.is_null());

                let localname = utf8_cstr(localname);

                let prefix = opt_utf8_cstr(prefix);
                let uri = opt_utf8_cstr(uri);
                let qual_name = QualName::new(
                    prefix.map(Prefix::from),
                    uri.map(Namespace::from)
                        .unwrap_or_else(|| namespace_url!("")),
                    LocalName::from(localname),
                );

                if !value_start.is_null() && !value_end.is_null() {
                    assert!(value_end >= value_start);

                    // FIXME: ptr::offset_from() is nightly-only.
                    // We'll do the computation of the length by hand.
                    let start: usize = mem::transmute(value_start);
                    let end: usize = mem::transmute(value_end);
                    let len = end - start;

                    let value_slice = slice::from_raw_parts(value_start as *const u8, len);
                    let value_str = str::from_utf8_unchecked(value_slice);
                    let value_atom = DefaultAtom::from(value_str);

                    array.push((qual_name, value_atom));
                }
            }
        }

        Attributes(array)
    }

    /// Returns the number of attributes.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Creates an iterator that yields `(QualName, &'a str)` tuples.
    pub fn iter(&self) -> AttributesIter<'_> {
        AttributesIter(self.0.iter())
    }
}

impl<'a> Iterator for AttributesIter<'a> {
    type Item = (QualName, &'a str);

    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|(a, v)| (a.clone(), v.as_ref()))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use markup5ever::{expanded_name, local_name, namespace_url, ns};
    use std::ffi::CString;
    use std::ptr;

    #[test]
    fn empty_attributes() {
        let map = unsafe { Attributes::new_from_xml2_attributes(0, ptr::null()) };
        assert_eq!(map.len(), 0);
    }

    #[test]
    fn attributes_with_namespaces() {
        let attrs = [
            (
                CString::new("href").unwrap(),
                Some(CString::new("xlink").unwrap()),
                Some(CString::new("http://www.w3.org/1999/xlink").unwrap()),
                CString::new("1").unwrap(),
            ),
            (
                CString::new("ry").unwrap(),
                None,
                None,
                CString::new("2").unwrap(),
            ),
            (
                CString::new("d").unwrap(),
                None,
                None,
                CString::new("").unwrap(),
            ),
        ];

        let mut v: Vec<*const libc::c_char> = Vec::new();

        for (localname, prefix, uri, val) in &attrs {
            v.push(localname.as_ptr() as *const libc::c_char);
            v.push(
                prefix
                    .as_ref()
                    .map(|p: &CString| p.as_ptr())
                    .unwrap_or_else(|| ptr::null()) as *const libc::c_char,
            );
            v.push(
                uri.as_ref()
                    .map(|p: &CString| p.as_ptr())
                    .unwrap_or_else(|| ptr::null()) as *const libc::c_char,
            );

            let val_start = val.as_ptr() as *const libc::c_char;
            let val_end = unsafe { val_start.offset(val.as_bytes().len() as isize) };
            v.push(val_start); // value_start
            v.push(val_end); // value_end
        }

        let attrs = unsafe { Attributes::new_from_xml2_attributes(3, v.as_ptr()) };

        let mut had_href: bool = false;
        let mut had_ry: bool = false;
        let mut had_d: bool = false;

        for (a, v) in attrs.iter() {
            match a.expanded() {
                expanded_name!(xlink "href") => {
                    assert!(v == "1");
                    had_href = true;
                }

                expanded_name!("", "ry") => {
                    assert!(v == "2");
                    had_ry = true;
                }

                expanded_name!("", "d") => {
                    assert!(v == "");
                    had_d = true;
                }

                _ => unreachable!(),
            }
        }

        assert!(had_href);
        assert!(had_ry);
        assert!(had_d);
    }
}
<commit_msg>Use simple pointer casts instead of transmute::<usize>(ptr)<commit_after>//! Store XML element attributes and their values.

use std::slice;
use std::str;

use markup5ever::{namespace_url, LocalName, Namespace, Prefix, QualName};
use string_cache::DefaultAtom;

use crate::util::{opt_utf8_cstr, utf8_cstr};

/// Type used to store attribute values.
///
/// Attribute values are often repeated in an SVG file, so we intern them using the
/// string_cache crate.
pub type AttributeValue = DefaultAtom;

/// Iterable wrapper for libxml2's representation of attribute/value.
///
/// See the [`new_from_xml2_attributes`] function for information.
///
/// [`new_from_xml2_attributes`]: #method.new_from_xml2_attributes
#[derive(Clone)]
pub struct Attributes(Vec<(QualName, AttributeValue)>);

/// Iterator from `Attributes.iter`.
pub struct AttributesIter<'a>(slice::Iter<'a, (QualName, AttributeValue)>);

impl Attributes {
    /// Creates an iterable `Attributes` from the C array of borrowed C strings.
    ///
    /// With libxml2's SAX parser, the caller's startElementNsSAX2Func
    /// callback gets passed a `xmlChar **` for attributes, which
    /// comes in groups of (localname/prefix/URI/value_start/value_end).
    /// In those, localname/prefix/URI are NUL-terminated strings;
    /// value_start and value_end point to the start-inclusive and
    /// end-exclusive bytes in the attribute's value.
    ///
    /// This function is unsafe because the caller must guarantee the following:
    ///
    /// * `attrs` is a valid pointer, with (n_attributes * 5) elements.
    ///
    /// * All strings are valid UTF-8.
    ///
    /// The lifetime of the `Attributes` should be considered the same as the lifetime of the
    /// `attrs` array, as the `Attributes` does not copy the strings - it directly stores pointers
    /// into that array's strings.
    pub unsafe fn new_from_xml2_attributes(
        n_attributes: usize,
        attrs: *const *const libc::c_char,
    ) -> Attributes {
        let mut array = Vec::new();

        if n_attributes > 0 && !attrs.is_null() {
            for attr in slice::from_raw_parts(attrs, n_attributes * 5).chunks_exact(5) {
                let localname = attr[0];
                let prefix = attr[1];
                let uri = attr[2];
                let value_start = attr[3];
                let value_end = attr[4];

                assert!(!localname.is_null());

                let localname = utf8_cstr(localname);

                let prefix = opt_utf8_cstr(prefix);
                let uri = opt_utf8_cstr(uri);
                let qual_name = QualName::new(
                    prefix.map(Prefix::from),
                    uri.map(Namespace::from)
                        .unwrap_or_else(|| namespace_url!("")),
                    LocalName::from(localname),
                );

                if !value_start.is_null() && !value_end.is_null() {
                    assert!(value_end >= value_start);

                    // FIXME: ptr::offset_from() is nightly-only.
                    // We'll do the computation of the length by hand.
                    let start = value_start as usize;
                    let end = value_end as usize;
                    let len = end - start;

                    let value_slice = slice::from_raw_parts(value_start as *const u8, len);
                    let value_str = str::from_utf8_unchecked(value_slice);
                    let value_atom = DefaultAtom::from(value_str);

                    array.push((qual_name, value_atom));
                }
            }
        }

        Attributes(array)
    }

    /// Returns the number of attributes.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Creates an iterator that yields `(QualName, &'a str)` tuples.
    pub fn iter(&self) -> AttributesIter<'_> {
        AttributesIter(self.0.iter())
    }
}

impl<'a> Iterator for AttributesIter<'a> {
    type Item = (QualName, &'a str);

    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|(a, v)| (a.clone(), v.as_ref()))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use markup5ever::{expanded_name, local_name, namespace_url, ns};
    use std::ffi::CString;
    use std::ptr;

    #[test]
    fn empty_attributes() {
        let map = unsafe { Attributes::new_from_xml2_attributes(0, ptr::null()) };
        assert_eq!(map.len(), 0);
    }

    #[test]
    fn attributes_with_namespaces() {
        let attrs = [
            (
                CString::new("href").unwrap(),
                Some(CString::new("xlink").unwrap()),
                Some(CString::new("http://www.w3.org/1999/xlink").unwrap()),
                CString::new("1").unwrap(),
            ),
            (
                CString::new("ry").unwrap(),
                None,
                None,
                CString::new("2").unwrap(),
            ),
            (
                CString::new("d").unwrap(),
                None,
                None,
                CString::new("").unwrap(),
            ),
        ];

        let mut v: Vec<*const libc::c_char> = Vec::new();

        for (localname, prefix, uri, val) in &attrs {
            v.push(localname.as_ptr() as *const libc::c_char);
            v.push(
                prefix
                    .as_ref()
                    .map(|p: &CString| p.as_ptr())
                    .unwrap_or_else(|| ptr::null()) as *const libc::c_char,
            );
            v.push(
                uri.as_ref()
                    .map(|p: &CString| p.as_ptr())
                    .unwrap_or_else(|| ptr::null()) as *const libc::c_char,
            );

            let val_start = val.as_ptr() as *const libc::c_char;
            let val_end = unsafe { val_start.offset(val.as_bytes().len() as isize) };
            v.push(val_start); // value_start
            v.push(val_end); // value_end
        }

        let attrs = unsafe { Attributes::new_from_xml2_attributes(3, v.as_ptr()) };

        let mut had_href: bool = false;
        let mut had_ry: bool = false;
        let mut had_d: bool = false;

        for (a, v) in attrs.iter() {
            match a.expanded() {
                expanded_name!(xlink "href") => {
                    assert!(v == "1");
                    had_href = true;
                }

                expanded_name!("", "ry") => {
                    assert!(v == "2");
                    had_ry = true;
                }

                expanded_name!("", "d") => {
                    assert!(v == "");
                    had_d = true;
                }

                _ => unreachable!(),
            }
        }

        assert!(had_href);
        assert!(had_ry);
        assert!(had_d);
    }
}
<|endoftext|>


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

//! These are tests concerning the business logic of transactions in the cryptocurrency service.
//! See `api.rs` for tests focused on the API of the service.
//!
//! Note how business logic tests use `TestKit::create_block*` methods to send transactions,
//! the service schema to make assertions about the storage state.

extern crate exonum;
extern crate exonum_cryptocurrency as cryptocurrency;
#[macro_use]
extern crate exonum_testkit;
extern crate rand;

use exonum::blockchain::Transaction;
use exonum::crypto;
use exonum_testkit::{TestKit, TestKitBuilder};

// Import data types used in tests from the crate where the service is defined.
use cryptocurrency::{CurrencySchema, CurrencyService, TxCreateWallet, TxTransfer, Wallet};

fn init_testkit() -> TestKit {
    TestKitBuilder::validator()
        .with_service(CurrencyService)
        .create()
}

#[test]
fn test_create_wallet() {
    let mut testkit = init_testkit();
    let (pubkey, key) = crypto::gen_keypair();
    testkit.create_block_with_transactions(txvec![
        TxCreateWallet::new(&pubkey, "Alice", &key),
    ]);

    // Check that the user indeed is persisted by the service
    let wallet = {
        let snapshot = testkit.snapshot();
        CurrencySchema::new(&snapshot).wallet(&pubkey).expect(
            "No wallet persisted",
        )
    };
    assert_eq!(*wallet.pub_key(), pubkey);
    assert_eq!(wallet.name(), "Alice");
    assert_eq!(wallet.balance(), 100);
}

#[test]
fn test_transfer() {
    let mut testkit = init_testkit();
    let (alice_pubkey, alice_key) = crypto::gen_keypair();
    let (bob_pubkey, bob_key) = crypto::gen_keypair();
    testkit.create_block_with_transactions(txvec![
        TxCreateWallet::new(&alice_pubkey, "Alice", &alice_key),
        TxCreateWallet::new(&bob_pubkey, "Bob", &bob_key),
        TxTransfer::new(&alice_pubkey, &bob_pubkey, /* amount */ 10, /* seed */ 0, &alice_key),
    ]);

    let wallets = {
        let snapshot = testkit.snapshot();
        let schema = CurrencySchema::new(&snapshot);
        (schema.wallet(&alice_pubkey), schema.wallet(&bob_pubkey))
    };
    if let (Some(alice_wallet), Some(bob_wallet)) = wallets {
        assert_eq!(alice_wallet.balance(), 90);
        assert_eq!(bob_wallet.balance(), 110);
    } else {
        panic!("Wallets not persisted");
    }
}

#[test]
fn test_transfer_from_nonexisting_wallet() {
    let mut testkit = init_testkit();
    let (alice_pubkey, alice_key) = crypto::gen_keypair();
    let (bob_pubkey, bob_key) = crypto::gen_keypair();
    testkit.create_block_with_transactions(txvec![
        TxCreateWallet::new(&bob_pubkey, "Bob", &bob_key),
        TxTransfer::new(&alice_pubkey, &bob_pubkey, /* amount */ 10, /* seed */ 0, &alice_key),
    ]);

    let wallets = {
        let snapshot = testkit.snapshot();
        let schema = CurrencySchema::new(&snapshot);
        (schema.wallet(&alice_pubkey), schema.wallet(&bob_pubkey))
    };
    if let (None, Some(bob_wallet)) = wallets {
        assert_eq!(bob_wallet.balance(), 100);
    } else {
        panic!("Wallets not persisted");
    }
}

#[test]
fn test_transfer_to_nonexisting_wallet() {
    let mut testkit = init_testkit();
    let (alice_pubkey, alice_key) = crypto::gen_keypair();
    let (bob_pubkey, bob_key) = crypto::gen_keypair();
    testkit.create_block_with_transactions(txvec![
        TxCreateWallet::new(&alice_pubkey, "Alice", &alice_key),
        TxTransfer::new(&alice_pubkey, &bob_pubkey, /* amount */ 10, /* seed */ 0, &alice_key),
        // Although Bob's wallet is created, this occurs after the transfer is executed
        TxCreateWallet::new(&bob_pubkey, "Bob", &bob_key),
    ]);

    let wallets = {
        let snapshot = testkit.snapshot();
        let schema = CurrencySchema::new(&snapshot);
        (schema.wallet(&alice_pubkey), schema.wallet(&bob_pubkey))
    };
    if let (Some(alice_wallet), Some(bob_wallet)) = wallets {
        assert_eq!(alice_wallet.balance(), 100);
        assert_eq!(bob_wallet.balance(), 100);
    } else {
        panic!("Wallets not persisted");
    }
}

#[test]
fn test_transfer_overcharge() {
    let mut testkit = init_testkit();
    let (alice_pubkey, alice_key) = crypto::gen_keypair();
    let (bob_pubkey, bob_key) = crypto::gen_keypair();
    testkit.create_block_with_transactions(txvec![
        TxCreateWallet::new(&alice_pubkey, "Alice", &alice_key),
        TxCreateWallet::new(&bob_pubkey, "Bob", &bob_key),
        TxTransfer::new(&alice_pubkey, &bob_pubkey, /* amount */ 150, /* seed */ 0, &alice_key),
    ]);

    // The transfer amount is greater than what Alice has at her disposal, so
    // the transfer should fail.
    let wallets = {
        let snapshot = testkit.snapshot();
        let schema = CurrencySchema::new(&snapshot);
        (schema.wallet(&alice_pubkey), schema.wallet(&bob_pubkey))
    };
    if let (Some(alice_wallet), Some(bob_wallet)) = wallets {
        assert_eq!(alice_wallet.balance(), 100);
        assert_eq!(bob_wallet.balance(), 100);
    } else {
        panic!("Wallets not persisted");
    }
}

#[test]
fn test_transfers_in_single_block() {
    let mut testkit = init_testkit();
    let (alice_pubkey, alice_key) = crypto::gen_keypair();
    let (bob_pubkey, bob_key) = crypto::gen_keypair();
    testkit.create_block_with_transactions(txvec![
        TxCreateWallet::new(&alice_pubkey, "Alice", &alice_key),
        TxCreateWallet::new(&bob_pubkey, "Bob", &bob_key),
    ]);

    let tx_a_to_b = TxTransfer::new(
        &alice_pubkey,
        &bob_pubkey,
        90, // amount
        0, // seed
        &alice_key,
    );
    let tx_b_to_a = TxTransfer::new(
        &bob_pubkey,
        &alice_pubkey,
        120, // amount
        0, // seed
        &bob_key,
    );

    {
        // See what happens if transactions are applied in an "incorrect" order.
        // We use `TestKit::probe_all()` method for this.

        let snapshot = testkit.probe_all(txvec![tx_b_to_a.clone(), tx_a_to_b.clone()]);
        let schema = CurrencySchema::new(&snapshot);
        assert_eq!(schema.wallet(&alice_pubkey).map(|w| w.balance()), Some(10));
        assert_eq!(schema.wallet(&bob_pubkey).map(|w| w.balance()), Some(190));
    }

    testkit.create_block_with_transactions(txvec![tx_a_to_b, tx_b_to_a]);
    let snapshot = testkit.snapshot();
    let schema = CurrencySchema::new(&snapshot);
    assert_eq!(schema.wallet(&alice_pubkey).map(|w| w.balance()), Some(130));
    assert_eq!(schema.wallet(&bob_pubkey).map(|w| w.balance()), Some(70));
}

/// Generate random transactions to perform [fuzz testing][fuzz] of the service. The service
/// should maintain invariants under all circumstances; e.g., the total amount of tokens
/// in existence should depend only on the number of registered wallets.
///
/// [fuzz]: https://en.wikipedia.org/wiki/Fuzzing
#[test]
fn test_fuzz_transfers() {
    use std::collections::BTreeSet;
    use std::iter::FromIterator;
    use rand::Rng;

    const BLOCKS: usize = 50; // number of blocks to create
    const MAX_TRANSACTIONS: usize = 20; // maximum number of transactions in a block

    let mut rng = rand::thread_rng();

    let mut testkit = init_testkit();
    let alice_keys = crypto::gen_keypair();
    let bob_keys = crypto::gen_keypair();
    let keys = &[alice_keys.clone(), bob_keys.clone()];
    testkit.create_block_with_transactions(txvec![
        TxCreateWallet::new(&alice_keys.0, "Alice", &alice_keys.1),
        TxCreateWallet::new(&bob_keys.0, "Bob", &bob_keys.1),
    ]);

    for _ in 0..BLOCKS {
        let n_txs = rng.gen_range(0, MAX_TRANSACTIONS); // number of transactions in the block

        let txs: Vec<Box<Transaction>> = (0..n_txs)
            .map(|_| {
                let (sender, receiver) = (rng.choose(keys).unwrap(), rng.choose(keys).unwrap());
                let amount = rng.gen_range(0, 250);
                TxTransfer::new(&sender.0, &receiver.0, amount, rng.next_u64(), &sender.1).into()
            })
            .collect();

        testkit.create_block_with_transactions(txs);

        // Test invariants that should be maintained during fuzz testing
        let schema = CurrencySchema::new(testkit.snapshot());
        let wallets = schema.wallets();
        let wallets: Vec<_> = wallets.values().collect();
        // There must be 2 wallets in the storage
        assert_eq!(wallets.len(), 2);
        // These wallets should belong to Alice and Bob
        assert_eq!(
            BTreeSet::from_iter(wallets.iter().map(Wallet::pub_key)),
            BTreeSet::from_iter(vec![&alice_keys.0, &bob_keys.0])
        );
        // The total amount of funds should equal 200, no matter which transactions were executed
        assert_eq!(wallets.iter().fold(0, |acc, w| acc + w.balance()), 200);
    }
}
<commit_msg>Fix Clippy warnings<commit_after>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! These are tests concerning the business logic of transactions in the cryptocurrency service.
//! See `api.rs` for tests focused on the API of the service.
//!
//! Note how business logic tests use `TestKit::create_block*` methods to send transactions,
//! the service schema to make assertions about the storage state.

extern crate exonum;
extern crate exonum_cryptocurrency as cryptocurrency;
#[macro_use]
extern crate exonum_testkit;
extern crate rand;

use exonum::blockchain::Transaction;
use exonum::crypto;
use exonum_testkit::{TestKit, TestKitBuilder};

// Import data types used in tests from the crate where the service is defined.
use cryptocurrency::{CurrencySchema, CurrencyService, TxCreateWallet, TxTransfer, Wallet};

fn init_testkit() -> TestKit {
    TestKitBuilder::validator()
        .with_service(CurrencyService)
        .create()
}

#[test]
fn test_create_wallet() {
    let mut testkit = init_testkit();
    let (pubkey, key) = crypto::gen_keypair();
    testkit.create_block_with_transactions(txvec![
        TxCreateWallet::new(&pubkey, "Alice", &key),
    ]);

    // Check that the user indeed is persisted by the service
    let wallet = {
        let snapshot = testkit.snapshot();
        CurrencySchema::new(&snapshot).wallet(&pubkey).expect(
            "No wallet persisted",
        )
    };
    assert_eq!(*wallet.pub_key(), pubkey);
    assert_eq!(wallet.name(), "Alice");
    assert_eq!(wallet.balance(), 100);
}

#[test]
fn test_transfer() {
    let mut testkit = init_testkit();
    let (alice_pubkey, alice_key) = crypto::gen_keypair();
    let (bob_pubkey, bob_key) = crypto::gen_keypair();
    testkit.create_block_with_transactions(txvec![
        TxCreateWallet::new(&alice_pubkey, "Alice", &alice_key),
        TxCreateWallet::new(&bob_pubkey, "Bob", &bob_key),
        TxTransfer::new(&alice_pubkey, &bob_pubkey, /* amount */ 10, /* seed */ 0, &alice_key),
    ]);

    let wallets = {
        let snapshot = testkit.snapshot();
        let schema = CurrencySchema::new(&snapshot);
        (schema.wallet(&alice_pubkey), schema.wallet(&bob_pubkey))
    };
    if let (Some(alice_wallet), Some(bob_wallet)) = wallets {
        assert_eq!(alice_wallet.balance(), 90);
        assert_eq!(bob_wallet.balance(), 110);
    } else {
        panic!("Wallets not persisted");
    }
}

#[test]
fn test_transfer_from_nonexisting_wallet() {
    let mut testkit = init_testkit();
    let (alice_pubkey, alice_key) = crypto::gen_keypair();
    let (bob_pubkey, bob_key) = crypto::gen_keypair();
    testkit.create_block_with_transactions(txvec![
        TxCreateWallet::new(&bob_pubkey, "Bob", &bob_key),
        TxTransfer::new(&alice_pubkey, &bob_pubkey, /* amount */ 10, /* seed */ 0, &alice_key),
    ]);

    let wallets = {
        let snapshot = testkit.snapshot();
        let schema = CurrencySchema::new(&snapshot);
        (schema.wallet(&alice_pubkey), schema.wallet(&bob_pubkey))
    };
    if let (None, Some(bob_wallet)) = wallets {
        assert_eq!(bob_wallet.balance(), 100);
    } else {
        panic!("Wallets not persisted");
    }
}

#[test]
fn test_transfer_to_nonexisting_wallet() {
    let mut testkit = init_testkit();
    let (alice_pubkey, alice_key) = crypto::gen_keypair();
    let (bob_pubkey, bob_key) = crypto::gen_keypair();
    testkit.create_block_with_transactions(txvec![
        TxCreateWallet::new(&alice_pubkey, "Alice", &alice_key),
        TxTransfer::new(&alice_pubkey, &bob_pubkey, /* amount */ 10, /* seed */ 0, &alice_key),
        // Although Bob's wallet is created, this occurs after the transfer is executed
        TxCreateWallet::new(&bob_pubkey, "Bob", &bob_key),
    ]);

    let wallets = {
        let snapshot = testkit.snapshot();
        let schema = CurrencySchema::new(&snapshot);
        (schema.wallet(&alice_pubkey), schema.wallet(&bob_pubkey))
    };
    if let (Some(alice_wallet), Some(bob_wallet)) = wallets {
        assert_eq!(alice_wallet.balance(), 100);
        assert_eq!(bob_wallet.balance(), 100);
    } else {
        panic!("Wallets not persisted");
    }
}

#[test]
fn test_transfer_overcharge() {
    let mut testkit = init_testkit();
    let (alice_pubkey, alice_key) = crypto::gen_keypair();
    let (bob_pubkey, bob_key) = crypto::gen_keypair();
    testkit.create_block_with_transactions(txvec![
        TxCreateWallet::new(&alice_pubkey, "Alice", &alice_key),
        TxCreateWallet::new(&bob_pubkey, "Bob", &bob_key),
        TxTransfer::new(&alice_pubkey, &bob_pubkey, /* amount */ 150, /* seed */ 0, &alice_key),
    ]);

    // The transfer amount is greater than what Alice has at her disposal, so
    // the transfer should fail.
    let wallets = {
        let snapshot = testkit.snapshot();
        let schema = CurrencySchema::new(&snapshot);
        (schema.wallet(&alice_pubkey), schema.wallet(&bob_pubkey))
    };
    if let (Some(alice_wallet), Some(bob_wallet)) = wallets {
        assert_eq!(alice_wallet.balance(), 100);
        assert_eq!(bob_wallet.balance(), 100);
    } else {
        panic!("Wallets not persisted");
    }
}

#[test]
fn test_transfers_in_single_block() {
    let mut testkit = init_testkit();
    let (alice_pubkey, alice_key) = crypto::gen_keypair();
    let (bob_pubkey, bob_key) = crypto::gen_keypair();
    testkit.create_block_with_transactions(txvec![
        TxCreateWallet::new(&alice_pubkey, "Alice", &alice_key),
        TxCreateWallet::new(&bob_pubkey, "Bob", &bob_key),
    ]);

    let tx_a_to_b = TxTransfer::new(
        &alice_pubkey,
        &bob_pubkey,
        90, // amount
        0, // seed
        &alice_key,
    );
    let tx_b_to_a = TxTransfer::new(
        &bob_pubkey,
        &alice_pubkey,
        120, // amount
        0, // seed
        &bob_key,
    );

    {
        // See what happens if transactions are applied in an "incorrect" order.
        // We use `TestKit::probe_all()` method for this.

        let snapshot = testkit.probe_all(txvec![tx_b_to_a.clone(), tx_a_to_b.clone()]);
        let schema = CurrencySchema::new(&snapshot);
        assert_eq!(schema.wallet(&alice_pubkey).map(|w| w.balance()), Some(10));
        assert_eq!(schema.wallet(&bob_pubkey).map(|w| w.balance()), Some(190));
    }

    testkit.create_block_with_transactions(txvec![tx_a_to_b, tx_b_to_a]);
    let snapshot = testkit.snapshot();
    let schema = CurrencySchema::new(&snapshot);
    assert_eq!(schema.wallet(&alice_pubkey).map(|w| w.balance()), Some(130));
    assert_eq!(schema.wallet(&bob_pubkey).map(|w| w.balance()), Some(70));
}

/// Generate random transactions to perform [fuzz testing][fuzz] of the service. The service
/// should maintain invariants under all circumstances; e.g., the total amount of tokens
/// in existence should depend only on the number of registered wallets.
///
/// [fuzz]: https://en.wikipedia.org/wiki/Fuzzing
#[test]
fn test_fuzz_transfers() {
    use std::collections::BTreeSet;
    use std::iter::FromIterator;
    use rand::Rng;

    const BLOCKS: usize = 50; // number of blocks to create
    const MAX_TRANSACTIONS: usize = 20; // maximum number of transactions in a block

    let mut rng = rand::thread_rng();

    let mut testkit = init_testkit();
    let alice_keys = crypto::gen_keypair();
    let bob_keys = crypto::gen_keypair();
    let keys = &[alice_keys.clone(), bob_keys.clone()];
    testkit.create_block_with_transactions(txvec![
        TxCreateWallet::new(&alice_keys.0, "Alice", &alice_keys.1),
        TxCreateWallet::new(&bob_keys.0, "Bob", &bob_keys.1),
    ]);

    for _ in 0..BLOCKS {
        let n_txs = rng.gen_range(0, MAX_TRANSACTIONS); // number of transactions in the block

        let txs: Vec<Box<Transaction>> = (0..n_txs)
            .map(|_| {
                let (sender, receiver) = (rng.choose(keys).unwrap(), rng.choose(keys).unwrap());
                let amount = rng.gen_range(0, 250);
                TxTransfer::new(&sender.0, &receiver.0, amount, rng.next_u64(), &sender.1).into()
            })
            .collect();

        testkit.create_block_with_transactions(txs);

        // Test invariants that should be maintained during fuzz testing
        let schema = CurrencySchema::new(testkit.snapshot());
        let wallets = schema.wallets();
        let wallets: Vec<_> = wallets.values().collect();
        // There must be 2 wallets in the storage
        assert_eq!(wallets.len(), 2);
        // These wallets should belong to Alice and Bob
        assert_eq!(
            BTreeSet::from_iter(wallets.iter().map(Wallet::pub_key)),
            BTreeSet::from_iter(vec![&alice_keys.0, &bob_keys.0])
        );
        // The total amount of funds should equal 200, no matter which transactions were executed
        assert_eq!(wallets.iter().map(|w| w.balance()).sum::<u64>(), 200);
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(feature = "cargo-clippy", allow(blacklisted_name))]

use std::borrow::Cow;

use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote, TokenStreamExt};

struct X;

impl quote::ToTokens for X {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        tokens.append(Ident::new("X", Span::call_site()));
    }
}

#[test]
fn test_quote_impl() {
    let tokens = quote! {
        impl<'a, T: ToTokens> ToTokens for &'a T {
            fn to_tokens(&self, tokens: &mut TokenStream) {
                (**self).to_tokens(tokens)
            }
        }
    };

    let expected = concat!(
        "impl < 'a , T : ToTokens > ToTokens for & 'a T { ",
        "fn to_tokens ( & self , tokens : & mut TokenStream ) { ",
        "( * * self ) . to_tokens ( tokens ) ",
        "} ",
        "}"
    );

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_substitution() {
    let x = X;
    let tokens = quote!(#x <#x> (#x) [#x] {#x});

    let expected = "X < X > ( X ) [ X ] { X }";

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_iter() {
    let primes = &[X, X, X, X];

    assert_eq!("X X X X", quote!(#(#primes)*).to_string());

    assert_eq!("X , X , X , X ,", quote!(#(#primes,)*).to_string());

    assert_eq!("X , X , X , X", quote!(#(#primes),*).to_string());
}

#[test]
fn test_advanced() {
    let generics = quote!( <'a, T> );

    let where_clause = quote!( where T: Serialize );

    let field_ty = quote!(String);

    let item_ty = quote!(Cow<'a, str>);

    let path = quote!(SomeTrait::serialize_with);

    let value = quote!(self.x);

    let tokens = quote! {
        struct SerializeWith #generics #where_clause {
            value: &'a #field_ty,
            phantom: ::std::marker::PhantomData<#item_ty>,
        }

        impl #generics ::serde::Serialize for SerializeWith #generics #where_clause {
            fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
                where S: ::serde::Serializer
            {
                #path(self.value, s)
            }
        }

        SerializeWith {
            value: #value,
            phantom: ::std::marker::PhantomData::<#item_ty>,
        }
    };

    let expected = concat!(
        "struct SerializeWith < 'a , T > where T : Serialize { ",
        "value : & 'a String , ",
        "phantom : :: std :: marker :: PhantomData < Cow < 'a , str > > , ",
        "} ",
        "impl < 'a , T > :: serde :: Serialize for SerializeWith < 'a , T > where T : Serialize { ",
        "fn serialize < S > ( & self , s : & mut S ) -> Result < ( ) , S :: Error > ",
        "where S : :: serde :: Serializer ",
        "{ ",
        "SomeTrait :: serialize_with ( self . value , s ) ",
        "} ",
        "} ",
        "SerializeWith { ",
        "value : self . x , ",
        "phantom : :: std :: marker :: PhantomData :: < Cow < 'a , str > > , ",
        "}"
    );

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_integer() {
    let ii8 = -1i8;
    let ii16 = -1i16;
    let ii32 = -1i32;
    let ii64 = -1i64;
    let ii128 = -1i128;
    let iisize = -1isize;
    let uu8 = 1u8;
    let uu16 = 1u16;
    let uu32 = 1u32;
    let uu64 = 1u64;
    let uu128 = 1u128;
    let uusize = 1usize;

    let tokens = quote! {
        #ii8 #ii16 #ii32 #ii64 #ii128 #iisize
        #uu8 #uu16 #uu32 #uu64 #uu128 #uusize
    };
    let expected = "-1i8 -1i16 -1i32 -1i64 -1i128 -1isize 1u8 1u16 1u32 1u64 1u128 1usize";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_floating() {
    let e32 = 2.345f32;

    let e64 = 2.345f64;

    let tokens = quote! {
        #e32
        #e64
    };
    let expected = concat!("2.345f32 2.345f64");
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_char() {
    let zero = '\0';
    let pound = '#';
    let quote = '"';
    let apost = '\'';
    let newline = '\n';
    let heart = '\u{2764}';

    let tokens = quote! {
        #zero #pound #quote #apost #newline #heart
    };
    let expected = "'\\u{0}' '#' '\"' '\\'' '\\n' '\\u{2764}'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_str() {
    let s = "\0 a 'b \" c";
    let tokens = quote!(#s);
    let expected = "\"\\u{0} a 'b \\\" c\"";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_string() {
    let s = "\0 a 'b \" c".to_string();
    let tokens = quote!(#s);
    let expected = "\"\\u{0} a 'b \\\" c\"";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_ident() {
    let foo = Ident::new("Foo", Span::call_site());
    let bar = Ident::new(&format!("Bar{}", 7), Span::call_site());
    let tokens = quote!(struct #foo; enum #bar {});
    let expected = "struct Foo ; enum Bar7 { }";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_duplicate() {
    let ch = 'x';

    let tokens = quote!(#ch #ch);

    let expected = "'x' 'x'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_fancy_repetition() {
    let foo = vec!["a", "b"];
    let bar = vec![true, false];

    let tokens = quote! {
        #(#foo: #bar),*
    };

    let expected = r#""a" : true , "b" : false"#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_nested_fancy_repetition() {
    let nested = vec![vec!['a', 'b', 'c'], vec!['x', 'y', 'z']];

    let tokens = quote! {
        #(
            #(#nested)*
        ),*
    };

    let expected = "'a' 'b' 'c' , 'x' 'y' 'z'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_duplicate_name_repetition() {
    let foo = &["a", "b"];

    let tokens = quote! {
        #(#foo: #foo),*
        #(#foo: #foo),*
    };

    let expected = r#""a" : "a" , "b" : "b" "a" : "a" , "b" : "b""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_duplicate_name_repetition_no_copy() {
    let foo = vec!["a".to_owned(), "b".to_owned()];

    let tokens = quote! {
        #(#foo: #foo),*
    };

    let expected = r#""a" : "a" , "b" : "b""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_variable_name_conflict() {
    // The implementation of `#(...),*` uses the variable `_i` but it should be
    // fine, if a little confusing when debugging.
    let _i = vec!['a', 'b'];
    let tokens = quote! { #(#_i),* };
    let expected = "'a' , 'b'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_nonrep_in_repetition() {
    let rep = vec!["a", "b"];
    let nonrep = "c";

    let tokens = quote! {
        #(#rep #rep : #nonrep #nonrep),*
    };

    let expected = r#""a" "a" : "c" "c" , "b" "b" : "c" "c""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_empty_quote() {
    let tokens = quote!();
    assert_eq!("", tokens.to_string());
}

#[test]
fn test_box_str() {
    let b = "str".to_owned().into_boxed_str();
    let tokens = quote! { #b };
    assert_eq!("\"str\"", tokens.to_string());
}

#[test]
fn test_cow() {
    let owned: Cow<Ident> = Cow::Owned(Ident::new("owned", Span::call_site()));

    let ident = Ident::new("borrowed", Span::call_site());
    let borrowed = Cow::Borrowed(&ident);

    let tokens = quote! { #owned #borrowed };
    assert_eq!("owned borrowed", tokens.to_string());
}

#[test]
fn test_closure() {
    fn field_i(i: usize) -> Ident {
        format_ident!("__field{}", i)
    }

    let fields = (0usize..3)
        .map(field_i as fn(_) -> _)
        .map(|var| quote! { #var });

    let tokens = quote! { #(#fields)* };
    assert_eq!("__field0 __field1 __field2", tokens.to_string());
}

#[test]
fn test_append_tokens() {
    let mut a = quote!(a);
    let b = quote!(b);
    a.append_all(b);
    assert_eq!("a b", a.to_string());
}

#[test]
fn test_format_ident() {
    let id0 = format_ident!("Aa");
    let id1 = format_ident!("Hello{x}", x = id0);
    let id2 = format_ident!("Hello{x}", x = 5usize);
    let id3 = format_ident!("Hello{}_{x}", id0, x = 10usize);
    let id4 = format_ident!("Aa", span = Span::call_site());

    assert_eq!(id0, "Aa");
    assert_eq!(id1, "HelloAa");
    assert_eq!(id2, "Hello5");
    assert_eq!(id3, "HelloAa_10");
    assert_eq!(id4, "Aa");
}

#[test]
fn test_format_ident_strip_raw() {
    let id = format_ident!("r#struct");
    let my_id = format_ident!("MyId{}", id);
    let raw_my_id = format_ident!("r#MyId{}", id);

    assert_eq!(id, "r#struct");
    assert_eq!(my_id, "MyIdstruct");
    assert_eq!(raw_my_id, "r#MyIdstruct");
}

#[test]
fn test_outer_line_comment() {
    let tokens = quote! {
        /// doc
    };
    let expected = "# [ doc = r\" doc\" ]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_inner_line_comment() {
    let tokens = quote! {
        //! doc
    };
    let expected = "# ! [ doc = r\" doc\" ]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_outer_block_comment() {
    let tokens = quote! {
        /** doc */
    };
    let expected = "# [ doc = r\" doc \" ]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_inner_block_comment() {
    let tokens = quote! {
        /*! doc */
    };
    let expected = "# ! [ doc = r\" doc \" ]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_outer_attr() {
    let tokens = quote! {
        #[inline]
    };
    let expected = "# [ inline ]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_inner_attr() {
    let tokens = quote! {
        #![no_std]
    };
    let expected = "# ! [ no_std ]";
    assert_eq!(expected, tokens.to_string());
}
<commit_msg>Add test that repeats BTreeSet<commit_after>#![cfg_attr(feature = "cargo-clippy", allow(blacklisted_name))]

use std::borrow::Cow;
use std::collections::BTreeSet;

use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote, TokenStreamExt};

struct X;

impl quote::ToTokens for X {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        tokens.append(Ident::new("X", Span::call_site()));
    }
}

#[test]
fn test_quote_impl() {
    let tokens = quote! {
        impl<'a, T: ToTokens> ToTokens for &'a T {
            fn to_tokens(&self, tokens: &mut TokenStream) {
                (**self).to_tokens(tokens)
            }
        }
    };

    let expected = concat!(
        "impl < 'a , T : ToTokens > ToTokens for & 'a T { ",
        "fn to_tokens ( & self , tokens : & mut TokenStream ) { ",
        "( * * self ) . to_tokens ( tokens ) ",
        "} ",
        "}"
    );

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_substitution() {
    let x = X;
    let tokens = quote!(#x <#x> (#x) [#x] {#x});

    let expected = "X < X > ( X ) [ X ] { X }";

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_iter() {
    let primes = &[X, X, X, X];

    assert_eq!("X X X X", quote!(#(#primes)*).to_string());

    assert_eq!("X , X , X , X ,", quote!(#(#primes,)*).to_string());

    assert_eq!("X , X , X , X", quote!(#(#primes),*).to_string());
}

#[test]
fn test_advanced() {
    let generics = quote!( <'a, T> );

    let where_clause = quote!( where T: Serialize );

    let field_ty = quote!(String);

    let item_ty = quote!(Cow<'a, str>);

    let path = quote!(SomeTrait::serialize_with);

    let value = quote!(self.x);

    let tokens = quote! {
        struct SerializeWith #generics #where_clause {
            value: &'a #field_ty,
            phantom: ::std::marker::PhantomData<#item_ty>,
        }

        impl #generics ::serde::Serialize for SerializeWith #generics #where_clause {
            fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
                where S: ::serde::Serializer
            {
                #path(self.value, s)
            }
        }

        SerializeWith {
            value: #value,
            phantom: ::std::marker::PhantomData::<#item_ty>,
        }
    };

    let expected = concat!(
        "struct SerializeWith < 'a , T > where T : Serialize { ",
        "value : & 'a String , ",
        "phantom : :: std :: marker :: PhantomData < Cow < 'a , str > > , ",
        "} ",
        "impl < 'a , T > :: serde :: Serialize for SerializeWith < 'a , T > where T : Serialize { ",
        "fn serialize < S > ( & self , s : & mut S ) -> Result < ( ) , S :: Error > ",
        "where S : :: serde :: Serializer ",
        "{ ",
        "SomeTrait :: serialize_with ( self . value , s ) ",
        "} ",
        "} ",
        "SerializeWith { ",
        "value : self . x , ",
        "phantom : :: std :: marker :: PhantomData :: < Cow < 'a , str > > , ",
        "}"
    );

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_integer() {
    let ii8 = -1i8;
    let ii16 = -1i16;
    let ii32 = -1i32;
    let ii64 = -1i64;
    let ii128 = -1i128;
    let iisize = -1isize;
    let uu8 = 1u8;
    let uu16 = 1u16;
    let uu32 = 1u32;
    let uu64 = 1u64;
    let uu128 = 1u128;
    let uusize = 1usize;

    let tokens = quote! {
        #ii8 #ii16 #ii32 #ii64 #ii128 #iisize
        #uu8 #uu16 #uu32 #uu64 #uu128 #uusize
    };
    let expected = "-1i8 -1i16 -1i32 -1i64 -1i128 -1isize 1u8 1u16 1u32 1u64 1u128 1usize";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_floating() {
    let e32 = 2.345f32;

    let e64 = 2.345f64;

    let tokens = quote! {
        #e32
        #e64
    };
    let expected = concat!("2.345f32 2.345f64");
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_char() {
    let zero = '\0';
    let pound = '#';
    let quote = '"';
    let apost = '\'';
    let newline = '\n';
    let heart = '\u{2764}';

    let tokens = quote! {
        #zero #pound #quote #apost #newline #heart
    };
    let expected = "'\\u{0}' '#' '\"' '\\'' '\\n' '\\u{2764}'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_str() {
    let s = "\0 a 'b \" c";
    let tokens = quote!(#s);
    let expected = "\"\\u{0} a 'b \\\" c\"";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_string() {
    let s = "\0 a 'b \" c".to_string();
    let tokens = quote!(#s);
    let expected = "\"\\u{0} a 'b \\\" c\"";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_ident() {
    let foo = Ident::new("Foo", Span::call_site());
    let bar = Ident::new(&format!("Bar{}", 7), Span::call_site());
    let tokens = quote!(struct #foo; enum #bar {});
    let expected = "struct Foo ; enum Bar7 { }";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_duplicate() {
    let ch = 'x';

    let tokens = quote!(#ch #ch);

    let expected = "'x' 'x'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_fancy_repetition() {
    let foo = vec!["a", "b"];
    let bar = vec![true, false];

    let tokens = quote! {
        #(#foo: #bar),*
    };

    let expected = r#""a" : true , "b" : false"#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_nested_fancy_repetition() {
    let nested = vec![vec!['a', 'b', 'c'], vec!['x', 'y', 'z']];

    let tokens = quote! {
        #(
            #(#nested)*
        ),*
    };

    let expected = "'a' 'b' 'c' , 'x' 'y' 'z'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_duplicate_name_repetition() {
    let foo = &["a", "b"];

    let tokens = quote! {
        #(#foo: #foo),*
        #(#foo: #foo),*
    };

    let expected = r#""a" : "a" , "b" : "b" "a" : "a" , "b" : "b""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_duplicate_name_repetition_no_copy() {
    let foo = vec!["a".to_owned(), "b".to_owned()];

    let tokens = quote! {
        #(#foo: #foo),*
    };

    let expected = r#""a" : "a" , "b" : "b""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_btreeset_repetition() {
    let mut set = BTreeSet::new();
    set.insert("a".to_owned());
    set.insert("b".to_owned());

    let tokens = quote! {
        #(#set: #set),*
    };

    let expected = r#""a" : "a" , "b" : "b""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_variable_name_conflict() {
    // The implementation of `#(...),*` uses the variable `_i` but it should be
    // fine, if a little confusing when debugging.
    let _i = vec!['a', 'b'];
    let tokens = quote! { #(#_i),* };
    let expected = "'a' , 'b'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_nonrep_in_repetition() {
    let rep = vec!["a", "b"];
    let nonrep = "c";

    let tokens = quote! {
        #(#rep #rep : #nonrep #nonrep),*
    };

    let expected = r#""a" "a" : "c" "c" , "b" "b" : "c" "c""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_empty_quote() {
    let tokens = quote!();
    assert_eq!("", tokens.to_string());
}

#[test]
fn test_box_str() {
    let b = "str".to_owned().into_boxed_str();
    let tokens = quote! { #b };
    assert_eq!("\"str\"", tokens.to_string());
}

#[test]
fn test_cow() {
    let owned: Cow<Ident> = Cow::Owned(Ident::new("owned", Span::call_site()));

    let ident = Ident::new("borrowed", Span::call_site());
    let borrowed = Cow::Borrowed(&ident);

    let tokens = quote! { #owned #borrowed };
    assert_eq!("owned borrowed", tokens.to_string());
}

#[test]
fn test_closure() {
    fn field_i(i: usize) -> Ident {
        format_ident!("__field{}", i)
    }

    let fields = (0usize..3)
        .map(field_i as fn(_) -> _)
        .map(|var| quote! { #var });

    let tokens = quote! { #(#fields)* };
    assert_eq!("__field0 __field1 __field2", tokens.to_string());
}

#[test]
fn test_append_tokens() {
    let mut a = quote!(a);
    let b = quote!(b);
    a.append_all(b);
    assert_eq!("a b", a.to_string());
}

#[test]
fn test_format_ident() {
    let id0 = format_ident!("Aa");
    let id1 = format_ident!("Hello{x}", x = id0);
    let id2 = format_ident!("Hello{x}", x = 5usize);
    let id3 = format_ident!("Hello{}_{x}", id0, x = 10usize);
    let id4 = format_ident!("Aa", span = Span::call_site());

    assert_eq!(id0, "Aa");
    assert_eq!(id1, "HelloAa");
    assert_eq!(id2, "Hello5");
    assert_eq!(id3, "HelloAa_10");
    assert_eq!(id4, "Aa");
}

#[test]
fn test_format_ident_strip_raw() {
    let id = format_ident!("r#struct");
    let my_id = format_ident!("MyId{}", id);
    let raw_my_id = format_ident!("r#MyId{}", id);

    assert_eq!(id, "r#struct");
    assert_eq!(my_id, "MyIdstruct");
    assert_eq!(raw_my_id, "r#MyIdstruct");
}

#[test]
fn test_outer_line_comment() {
    let tokens = quote! {
        /// doc
    };
    let expected = "# [ doc = r\" doc\" ]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_inner_line_comment() {
    let tokens = quote! {
        //! doc
    };
    let expected = "# ! [ doc = r\" doc\" ]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_outer_block_comment() {
    let tokens = quote! {
        /** doc */
    };
    let expected = "# [ doc = r\" doc \" ]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_inner_block_comment() {
    let tokens = quote! {
        /*! doc */
    };
    let expected = "# ! [ doc = r\" doc \" ]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_outer_attr() {
    let tokens = quote! {
        #[inline]
    };
    let expected = "# [ inline ]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_inner_attr() {
    let tokens = quote! {
        #![no_std]
    };
    let expected = "# ! [ no_std ]";
    assert_eq!(expected, tokens.to_string());
}
<|endoftext|>


<commit_before>use context::Context;
use error::RenderError;
use helpers::{HelperDef, HelperResult};
use output::Output;
use registry::Registry;
use render::{Helper, RenderContext, Renderable};
use value::JsonTruthy;

#[derive(Clone, Copy)]
pub struct IfHelper {
    positive: bool,
}

impl HelperDef for IfHelper {
    fn call<'reg: 'rc, 'rc>(
        &self,
        h: &Helper<'reg, 'rc>,
        r: &'reg Registry,
        ctx: &Context,
        rc: &mut RenderContext<'reg>,
        out: &mut Output,
    ) -> HelperResult {
        let param = h
            .param(0)
            .ok_or_else(|| RenderError::new("Param not found for helper \"if\""))?;

        let mut value = param.value().is_truthy(false);

        if !self.positive {
            value = !value;
        }

        let tmpl =
            if value { h.template() } else { h.inverse() };
        match tmpl {
            Some(ref t) => t.render(r, ctx, rc, out),
            None => Ok(()),
        }
    }
}

pub static IF_HELPER: IfHelper = IfHelper { positive: true };
pub static UNLESS_HELPER: IfHelper = IfHelper { positive: false };

#[cfg(test)]
mod test {
    use helpers::WITH_HELPER;
    use registry::Registry;
    use serde_json::value::Value as Json;
    use std::str::FromStr;

    #[test]
    fn test_if() {
        let mut handlebars = Registry::new();
        assert!(
            handlebars
                .register_template_string("t0", "{{#if this}}hello{{/if}}")
                .is_ok()
        );
        assert!(
            handlebars
                .register_template_string("t1", "{{#unless this}}hello{{else}}world{{/unless}}")
                .is_ok()
        );

        let r0 = handlebars.render("t0", &true);
        assert_eq!(r0.ok().unwrap(), "hello".to_string());

        let r1 = handlebars.render("t1", &true);
        assert_eq!(r1.ok().unwrap(), "world".to_string());

        let r2 = handlebars.render("t0", &false);
        assert_eq!(r2.ok().unwrap(), "".to_string());
    }

    #[test]
    fn test_if_context() {
        let json_str = r#"{"a":{"b":99,"c":{"d": true}}}"#;
        let data = Json::from_str(json_str).unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_helper("with", Box::new(WITH_HELPER));
        assert!(
            handlebars
                .register_template_string("t0", "{{#if a.c.d}}hello {{a.b}}{{/if}}")
                .is_ok()
        );
        assert!(
            handlebars
                .register_template_string(
                    "t1",
                    "{{#with a}}{{#if c.d}}hello {{../a.b}}{{/if}}{{/with}}"
                )
                .is_ok()
        );

        let r0 = handlebars.render("t0", &data);
        assert_eq!(r0.ok().unwrap(), "hello 99".to_string());

        let r1 = handlebars.render("t1", &data);
        assert_eq!(r1.ok().unwrap(), "hello 99".to_string());
    }
}
<commit_msg>(feat) added support for includeZero option [#240]<commit_after>use context::Context;
use error::RenderError;
use helpers::{HelperDef, HelperResult};
use output::Output;
use registry::Registry;
use render::{Helper, RenderContext, Renderable};
use value::JsonTruthy;

#[derive(Clone, Copy)]
pub struct IfHelper {
    positive: bool,
}

impl HelperDef for IfHelper {
    fn call<'reg: 'rc, 'rc>(
        &self,
        h: &Helper<'reg, 'rc>,
        r: &'reg Registry,
        ctx: &Context,
        rc: &mut RenderContext<'reg>,
        out: &mut Output,
    ) -> HelperResult {
        let param = h
            .param(0)
            .ok_or_else(|| RenderError::new("Param not found for helper \"if\""))?;
        let include_zero = h
            .hash_get("includeZero")
            .and_then(|v| v.value().as_bool())
            .unwrap_or(false);

        let mut value = param.value().is_truthy(include_zero);

        if !self.positive {
            value = !value;
        }

        let tmpl = if value { h.template() } else { h.inverse() };
        match tmpl {
            Some(ref t) => t.render(r, ctx, rc, out),
            None => Ok(()),
        }
    }
}

pub static IF_HELPER: IfHelper = IfHelper { positive: true };
pub static UNLESS_HELPER: IfHelper = IfHelper { positive: false };

#[cfg(test)]
mod test {
    use helpers::WITH_HELPER;
    use registry::Registry;
    use serde_json::value::Value as Json;
    use std::str::FromStr;

    #[test]
    fn test_if() {
        let mut handlebars = Registry::new();
        assert!(
            handlebars
                .register_template_string("t0", "{{#if this}}hello{{/if}}")
                .is_ok()
        );
        assert!(
            handlebars
                .register_template_string("t1", "{{#unless this}}hello{{else}}world{{/unless}}")
                .is_ok()
        );

        let r0 = handlebars.render("t0", &true);
        assert_eq!(r0.ok().unwrap(), "hello".to_string());

        let r1 = handlebars.render("t1", &true);
        assert_eq!(r1.ok().unwrap(), "world".to_string());

        let r2 = handlebars.render("t0", &false);
        assert_eq!(r2.ok().unwrap(), "".to_string());
    }

    #[test]
    fn test_if_context() {
        let json_str = r#"{"a":{"b":99,"c":{"d": true}}}"#;
        let data = Json::from_str(json_str).unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_helper("with", Box::new(WITH_HELPER));
        assert!(
            handlebars
                .register_template_string("t0", "{{#if a.c.d}}hello {{a.b}}{{/if}}")
                .is_ok()
        );
        assert!(
            handlebars
                .register_template_string(
                    "t1",
                    "{{#with a}}{{#if c.d}}hello {{../a.b}}{{/if}}{{/with}}"
                )
                .is_ok()
        );

        let r0 = handlebars.render("t0", &data);
        assert_eq!(r0.ok().unwrap(), "hello 99".to_string());

        let r1 = handlebars.render("t1", &data);
        assert_eq!(r1.ok().unwrap(), "hello 99".to_string());
    }

    #[test]
    fn test_if_include_zero() {
        use std::f64;
        let handlebars = Registry::new();

        assert_eq!(
            "0".to_owned(),
            handlebars
                .render_template("{{#if a}}1{{else}}0{{/if}}", &json!({"a": 0}))
                .unwrap()
        );
        assert_eq!(
            "1".to_owned(),
            handlebars
                .render_template(
                    "{{#if a includeZero=true}}1{{else}}0{{/if}}",
                    &json!({"a": 0})
                )
                .unwrap()
        );
        assert_eq!(
            "0".to_owned(),
            handlebars
                .render_template(
                    "{{#if a includeZero=true}}1{{else}}0{{/if}}",
                    &json!({ "a": f64::NAN })
                )
                .unwrap()
        );
    }
}
<|endoftext|>


<commit_before>//! Multi-dimensional arrays with per-dimension specifiable lower bounds

use std::cast;
use std::vec::VecIterator;

/// Information about a dimension of an array
#[deriving(Eq, Clone)]
pub struct DimensionInfo {
    /// The size of the dimension
    len: uint,
    /// The index of the first element of the dimension
    lower_bound: int,
}

/// Specifies methods that can be performed on multi-dimensional arrays
pub trait Array<T> {
    /// Returns information about the dimensions of this array
    fn dimension_info<'a>(&'a self) -> &'a [DimensionInfo];

    /// Slices into this array, returning an immutable view of a subarray.
    ///
    /// # Failure
    ///
    /// Fails if the array is one-dimensional or the index is out of bounds.
    fn slice<'a>(&'a self, idx: int) -> ArraySlice<'a, T>;

    /// Retrieves an immutable reference to a value in this array.
    ///
    ///
    /// # Failure
    ///
    /// Fails if the array is multi-dimensional or the index is out of bounds.
    fn get<'a>(&'a self, idx: int) -> &'a T;
}

/// Specifies methods that can be performed on mutable multi-dimensional arrays
pub trait MutableArray<T> : Array<T> {
    /// Slices into this array, returning a mutable view of a subarray.
    ///
    /// # Failure
    ///
    /// Fails if the array is one-dimensional or the index is out of bounds.
    fn slice_mut<'a>(&'a mut self, idx: int) -> MutArraySlice<'a, T> {
        MutArraySlice { slice: self.slice(idx) }
    }

    /// Retrieves a mutable reference to a value in this array.
    ///
    ///
    /// # Failure
    ///
    /// Fails if the array is multi-dimensional or the index is out of bounds.
    fn get_mut<'a>(&'a mut self, idx: int) -> &'a mut T {
        unsafe { cast::transmute_mut(self.get(idx)) }
    }
}

trait InternalArray<T> : Array<T> {
    fn shift_idx(&self, idx: int) -> uint {
        let shifted_idx = idx - self.dimension_info()[0].lower_bound;
        assert!(shifted_idx >= 0 &&
                    shifted_idx < self.dimension_info()[0].len as int,
                "Out of bounds array access");
        shifted_idx as uint
    }

    fn raw_get<'a>(&'a self, idx: uint, size: uint) -> &'a T;
}

/// A multi-dimensional array
#[deriving(Eq, Clone)]
pub struct ArrayBase<T> {
    priv info: ~[DimensionInfo],
    priv data: ~[T],
}

impl<T> ArrayBase<T> {
    /// Creates a new multi-dimensional array from its underlying components.
    ///
    /// The data array should be provided in the higher-dimensional equivalent
    /// of row-major order.
    ///
    /// # Failure
    ///
    /// Fails if there are 0 dimensions or the number of elements provided does
    /// not match the number of elements specified.
    pub fn from_raw(data: ~[T], info: ~[DimensionInfo])
            -> ArrayBase<T> {
        assert!(!info.is_empty(), "Cannot create a 0x0 array");
        assert!(data.len() == info.iter().fold(1, |acc, i| acc * i.len),
                "Size mismatch");
        ArrayBase {
            info: info,
            data: data,
        }
    }

    /// Creates a new one-dimensional array from a vector.
    pub fn from_vec(data: ~[T], lower_bound: int) -> ArrayBase<T> {
        ArrayBase {
            info: ~[DimensionInfo {
                len: data.len(),
                lower_bound: lower_bound
            }],
            data: data
        }
    }

    /// Wraps this array in a new dimension of size 1.
    ///
    /// For example the one-dimensional array `[1,2]` would turn into
    /// the two-dimensional array `[[1,2]]`.
    pub fn wrap(&mut self, lower_bound: int) {
        self.info.unshift(DimensionInfo {
            len: 1,
            lower_bound: lower_bound
        })
    }

    /// Takes ownership of another array, appending it to the top-level
    /// dimension of this array.
    ///
    /// The dimensions of the other array must have an identical shape to the
    /// dimensions of a slice of this array. This includes both the sizes of
    /// the dimensions as well as their lower bounds.
    ///
    /// For example, if `[3,4]` is pushed onto `[[1,2]]`, the result is
    /// `[[1,2],[3,4]]`.
    ///
    /// # Failure
    ///
    /// Fails if the other array does not have dimensions identical to the
    /// dimensions of a slice of this array.
    pub fn push_move(&mut self, other: ArrayBase<T>) {
        assert!(self.info.len() - 1 == other.info.len(),
                "Cannot append differently shaped arrays");
        for (info1, info2) in self.info.iter().skip(1).zip(other.info.iter()) {
            assert!(info1 == info2, "Cannot join differently shaped arrays");
        }
        self.info[0].len += 1;
        self.data.push_all_move(other.data);
    }

    /// Returns an iterator over the values in this array, in the
    /// higher-dimensional equivalent of row-major order.
    pub fn values<'a>(&'a self) -> VecIterator<'a, T> {
        self.data.iter()
    }
}

impl<T> Array<T> for ArrayBase<T> {
    fn dimension_info<'a>(&'a self) -> &'a [DimensionInfo] {
        self.info.as_slice()
    }

    fn slice<'a>(&'a self, idx: int) -> ArraySlice<'a, T> {
        assert!(self.info.len() != 1,
                "Attempted to slice a one-dimensional array");
        ArraySlice {
            parent: BaseParent(self),
            idx: self.shift_idx(idx)
        }
    }

    fn get<'a>(&'a self, idx: int) -> &'a T {
        assert!(self.info.len() == 1,
                "Attempted to get from a multi-dimensional array");
        self.raw_get(self.shift_idx(idx), 1)
    }
}

impl<T> MutableArray<T> for ArrayBase<T> {}

impl<T> InternalArray<T> for ArrayBase<T> {
    fn raw_get<'a>(&'a self, idx: uint, _size: uint) -> &'a T {
        &self.data[idx]
    }
}

enum ArrayParent<'parent, T> {
    SliceParent(&'parent ArraySlice<'parent, T>),
    BaseParent(&'parent ArrayBase<T>),
}

/// An immutable slice of a multi-dimensional array
pub struct ArraySlice<'parent, T> {
    priv parent: ArrayParent<'parent, T>,
    priv idx: uint,
}

impl<'parent, T> Array<T> for ArraySlice<'parent, T> {
    fn dimension_info<'a>(&'a self) -> &'a [DimensionInfo] {
        let info = match self.parent {
            SliceParent(p) => p.dimension_info(),
            BaseParent(p) => p.dimension_info()
        };
        info.slice_from(1)
    }

    fn slice<'a>(&'a self, idx: int) -> ArraySlice<'a, T> {
        assert!(self.dimension_info().len() != 1,
                "Attempted to slice a one-dimensional array");
        ArraySlice {
            parent: SliceParent(self),
            idx: self.shift_idx(idx)
        }
    }

    fn get<'a>(&'a self, idx: int) -> &'a T {
        assert!(self.dimension_info().len() == 1,
                "Attempted to get from a multi-dimensional array");
        self.raw_get(self.shift_idx(idx), 1)
    }
}

impl<'parent, T> InternalArray<T> for ArraySlice<'parent, T> {
    fn raw_get<'a>(&'a self, idx: uint, size: uint) -> &'a T {
        let size = size * self.dimension_info()[0].len;
        let idx = size * self.idx + idx;
        match self.parent {
            SliceParent(p) => p.raw_get(idx, size),
            BaseParent(p) => p.raw_get(idx, size)
        }
    }
}

/// A mutable slice of a multi-dimensional array
pub struct MutArraySlice<'parent, T> {
    priv slice: ArraySlice<'parent, T>
}

impl<'parent, T> Array<T> for MutArraySlice<'parent, T> {
    fn dimension_info<'a>(&'a self) -> &'a [DimensionInfo] {
        self.slice.dimension_info()
    }

    fn slice<'a>(&'a self, idx: int) -> ArraySlice<'a, T> {
        self.slice.slice(idx)
    }

    fn get<'a>(&'a self, idx: int) -> &'a T {
        self.slice.get(idx)
    }
}

impl<'parent, T> MutableArray<T> for MutArraySlice<'parent, T> {}

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

    #[test]
    fn test_from_vec() {
        let a = ArrayBase::from_vec(~[0, 1, 2], -1);
        assert_eq!([DimensionInfo { len: 3, lower_bound: -1 }],
                   a.dimension_info());
        assert_eq!(&0, a.get(-1));
        assert_eq!(&1, a.get(0));
        assert_eq!(&2, a.get(1));
    }

    #[test]
    #[should_fail]
    fn test_get_2d_fail() {
        let mut a = ArrayBase::from_vec(~[0, 1, 2], -1);
        a.wrap(1);
        a.get(1);
    }

    #[test]
    #[should_fail]
    fn test_2d_slice_range_fail_low() {
        let mut a = ArrayBase::from_vec(~[0, 1, 2], -1);
        a.wrap(1);
        a.slice(0);
    }

    #[test]
    #[should_fail]
    fn test_2d_slice_range_fail_high() {
        let mut a = ArrayBase::from_vec(~[0, 1, 2], -1);
        a.wrap(1);
        a.slice(2);
    }

    #[test]
    fn test_2d_slice_get() {
        let mut a = ArrayBase::from_vec(~[0, 1, 2], -1);
        a.wrap(1);
        let s = a.slice(1);
        assert_eq!(&0, s.get(-1));
        assert_eq!(&1, s.get(0));
        assert_eq!(&2, s.get(1));
    }

    #[test]
    #[should_fail]
    fn test_push_move_wrong_lower_bound() {
        let mut a = ArrayBase::from_vec(~[1], -1);
        a.push_move(ArrayBase::from_vec(~[2], 0));
    }

    #[test]
    #[should_fail]
    fn test_push_move_wrong_dims() {
        let mut a = ArrayBase::from_vec(~[1], -1);
        a.wrap(1);
        a.push_move(ArrayBase::from_vec(~[1, 2], -1));
    }

    #[test]
    #[should_fail]
    fn test_push_move_wrong_dim_count() {
        let mut a = ArrayBase::from_vec(~[1], -1);
        a.wrap(1);
        let mut b = ArrayBase::from_vec(~[2], -1);
        b.wrap(1);
        a.push_move(b);
    }

    #[test]
    fn test_push_move_ok() {
        let mut a = ArrayBase::from_vec(~[1, 2], 0);
        a.wrap(0);
        a.push_move(ArrayBase::from_vec(~[3, 4], 0));
        let s = a.slice(0);
        assert_eq!(&1, s.get(0));
        assert_eq!(&2, s.get(1));
        let s = a.slice(1);
        assert_eq!(&3, s.get(0));
        assert_eq!(&4, s.get(1));
    }

    #[test]
    fn test_3d() {
        let mut a = ArrayBase::from_vec(~[0, 1], 0);
        a.wrap(0);
        a.push_move(ArrayBase::from_vec(~[2, 3], 0));
        a.wrap(0);
        let mut b = ArrayBase::from_vec(~[4, 5], 0);
        b.wrap(0);
        b.push_move(ArrayBase::from_vec(~[6, 7], 0));
        a.push_move(b);
        let s1 = a.slice(0);
        let s2 = s1.slice(0);
        assert_eq!(&0, s2.get(0));
        assert_eq!(&1, s2.get(1));
        let s2 = s1.slice(1);
        assert_eq!(&2, s2.get(0));
        assert_eq!(&3, s2.get(1));
        let s1 = a.slice(1);
        let s2 = s1.slice(0);
        assert_eq!(&4, s2.get(0));
        assert_eq!(&5, s2.get(1));
        let s2 = s1.slice(1);
        assert_eq!(&6, s2.get(0));
        assert_eq!(&7, s2.get(1));
    }

    #[test]
    fn test_mut() {
        let mut a = ArrayBase::from_vec(~[1, 2], 0);
        a.wrap(0);
        {
            let mut s = a.slice_mut(0);
            *s.get_mut(0) = 3;
        }
        let s = a.slice(0);
        assert_eq!(&3, s.get(0));
    }

    #[test]
    #[should_fail]
    fn test_base_overslice() {
        let a = ArrayBase::from_vec(~[1], 0);
        a.slice(0);
    }

    #[test]
    #[should_fail]
    fn test_slice_overslice() {
        let mut a = ArrayBase::from_vec(~[1], 0);
        a.wrap(0);
        let s = a.slice(0);
        s.slice(0);
    }
}
<commit_msg>Switch to new vec iterator name<commit_after>//! Multi-dimensional arrays with per-dimension specifiable lower bounds

use std::cast;
use std::vec;

/// Information about a dimension of an array
#[deriving(Eq, Clone)]
pub struct DimensionInfo {
    /// The size of the dimension
    len: uint,
    /// The index of the first element of the dimension
    lower_bound: int,
}

/// Specifies methods that can be performed on multi-dimensional arrays
pub trait Array<T> {
    /// Returns information about the dimensions of this array
    fn dimension_info<'a>(&'a self) -> &'a [DimensionInfo];

    /// Slices into this array, returning an immutable view of a subarray.
    ///
    /// # Failure
    ///
    /// Fails if the array is one-dimensional or the index is out of bounds.
    fn slice<'a>(&'a self, idx: int) -> ArraySlice<'a, T>;

    /// Retrieves an immutable reference to a value in this array.
    ///
    ///
    /// # Failure
    ///
    /// Fails if the array is multi-dimensional or the index is out of bounds.
    fn get<'a>(&'a self, idx: int) -> &'a T;
}

/// Specifies methods that can be performed on mutable multi-dimensional arrays
pub trait MutableArray<T> : Array<T> {
    /// Slices into this array, returning a mutable view of a subarray.
    ///
    /// # Failure
    ///
    /// Fails if the array is one-dimensional or the index is out of bounds.
    fn slice_mut<'a>(&'a mut self, idx: int) -> MutArraySlice<'a, T> {
        MutArraySlice { slice: self.slice(idx) }
    }

    /// Retrieves a mutable reference to a value in this array.
    ///
    ///
    /// # Failure
    ///
    /// Fails if the array is multi-dimensional or the index is out of bounds.
    fn get_mut<'a>(&'a mut self, idx: int) -> &'a mut T {
        unsafe { cast::transmute_mut(self.get(idx)) }
    }
}

trait InternalArray<T> : Array<T> {
    fn shift_idx(&self, idx: int) -> uint {
        let shifted_idx = idx - self.dimension_info()[0].lower_bound;
        assert!(shifted_idx >= 0 &&
                    shifted_idx < self.dimension_info()[0].len as int,
                "Out of bounds array access");
        shifted_idx as uint
    }

    fn raw_get<'a>(&'a self, idx: uint, size: uint) -> &'a T;
}

/// A multi-dimensional array
#[deriving(Eq, Clone)]
pub struct ArrayBase<T> {
    priv info: ~[DimensionInfo],
    priv data: ~[T],
}

impl<T> ArrayBase<T> {
    /// Creates a new multi-dimensional array from its underlying components.
    ///
    /// The data array should be provided in the higher-dimensional equivalent
    /// of row-major order.
    ///
    /// # Failure
    ///
    /// Fails if there are 0 dimensions or the number of elements provided does
    /// not match the number of elements specified.
    pub fn from_raw(data: ~[T], info: ~[DimensionInfo])
            -> ArrayBase<T> {
        assert!(!info.is_empty(), "Cannot create a 0x0 array");
        assert!(data.len() == info.iter().fold(1, |acc, i| acc * i.len),
                "Size mismatch");
        ArrayBase {
            info: info,
            data: data,
        }
    }

    /// Creates a new one-dimensional array from a vector.
    pub fn from_vec(data: ~[T], lower_bound: int) -> ArrayBase<T> {
        ArrayBase {
            info: ~[DimensionInfo {
                len: data.len(),
                lower_bound: lower_bound
            }],
            data: data
        }
    }

    /// Wraps this array in a new dimension of size 1.
    ///
    /// For example the one-dimensional array `[1,2]` would turn into
    /// the two-dimensional array `[[1,2]]`.
    pub fn wrap(&mut self, lower_bound: int) {
        self.info.unshift(DimensionInfo {
            len: 1,
            lower_bound: lower_bound
        })
    }

    /// Takes ownership of another array, appending it to the top-level
    /// dimension of this array.
    ///
    /// The dimensions of the other array must have an identical shape to the
    /// dimensions of a slice of this array. This includes both the sizes of
    /// the dimensions as well as their lower bounds.
    ///
    /// For example, if `[3,4]` is pushed onto `[[1,2]]`, the result is
    /// `[[1,2],[3,4]]`.
    ///
    /// # Failure
    ///
    /// Fails if the other array does not have dimensions identical to the
    /// dimensions of a slice of this array.
    pub fn push_move(&mut self, other: ArrayBase<T>) {
        assert!(self.info.len() - 1 == other.info.len(),
                "Cannot append differently shaped arrays");
        for (info1, info2) in self.info.iter().skip(1).zip(other.info.iter()) {
            assert!(info1 == info2, "Cannot join differently shaped arrays");
        }
        self.info[0].len += 1;
        self.data.push_all_move(other.data);
    }

    /// Returns an iterator over the values in this array, in the
    /// higher-dimensional equivalent of row-major order.
    pub fn values<'a>(&'a self) -> vec::Items<'a, T> {
        self.data.iter()
    }
}

impl<T> Array<T> for ArrayBase<T> {
    fn dimension_info<'a>(&'a self) -> &'a [DimensionInfo] {
        self.info.as_slice()
    }

    fn slice<'a>(&'a self, idx: int) -> ArraySlice<'a, T> {
        assert!(self.info.len() != 1,
                "Attempted to slice a one-dimensional array");
        ArraySlice {
            parent: BaseParent(self),
            idx: self.shift_idx(idx)
        }
    }

    fn get<'a>(&'a self, idx: int) -> &'a T {
        assert!(self.info.len() == 1,
                "Attempted to get from a multi-dimensional array");
        self.raw_get(self.shift_idx(idx), 1)
    }
}

impl<T> MutableArray<T> for ArrayBase<T> {}

impl<T> InternalArray<T> for ArrayBase<T> {
    fn raw_get<'a>(&'a self, idx: uint, _size: uint) -> &'a T {
        &self.data[idx]
    }
}

enum ArrayParent<'parent, T> {
    SliceParent(&'parent ArraySlice<'parent, T>),
    BaseParent(&'parent ArrayBase<T>),
}

/// An immutable slice of a multi-dimensional array
pub struct ArraySlice<'parent, T> {
    priv parent: ArrayParent<'parent, T>,
    priv idx: uint,
}

impl<'parent, T> Array<T> for ArraySlice<'parent, T> {
    fn dimension_info<'a>(&'a self) -> &'a [DimensionInfo] {
        let info = match self.parent {
            SliceParent(p) => p.dimension_info(),
            BaseParent(p) => p.dimension_info()
        };
        info.slice_from(1)
    }

    fn slice<'a>(&'a self, idx: int) -> ArraySlice<'a, T> {
        assert!(self.dimension_info().len() != 1,
                "Attempted to slice a one-dimensional array");
        ArraySlice {
            parent: SliceParent(self),
            idx: self.shift_idx(idx)
        }
    }

    fn get<'a>(&'a self, idx: int) -> &'a T {
        assert!(self.dimension_info().len() == 1,
                "Attempted to get from a multi-dimensional array");
        self.raw_get(self.shift_idx(idx), 1)
    }
}

impl<'parent, T> InternalArray<T> for ArraySlice<'parent, T> {
    fn raw_get<'a>(&'a self, idx: uint, size: uint) -> &'a T {
        let size = size * self.dimension_info()[0].len;
        let idx = size * self.idx + idx;
        match self.parent {
            SliceParent(p) => p.raw_get(idx, size),
            BaseParent(p) => p.raw_get(idx, size)
        }
    }
}

/// A mutable slice of a multi-dimensional array
pub struct MutArraySlice<'parent, T> {
    priv slice: ArraySlice<'parent, T>
}

impl<'parent, T> Array<T> for MutArraySlice<'parent, T> {
    fn dimension_info<'a>(&'a self) -> &'a [DimensionInfo] {
        self.slice.dimension_info()
    }

    fn slice<'a>(&'a self, idx: int) -> ArraySlice<'a, T> {
        self.slice.slice(idx)
    }

    fn get<'a>(&'a self, idx: int) -> &'a T {
        self.slice.get(idx)
    }
}

impl<'parent, T> MutableArray<T> for MutArraySlice<'parent, T> {}

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

    #[test]
    fn test_from_vec() {
        let a = ArrayBase::from_vec(~[0, 1, 2], -1);
        assert_eq!([DimensionInfo { len: 3, lower_bound: -1 }],
                   a.dimension_info());
        assert_eq!(&0, a.get(-1));
        assert_eq!(&1, a.get(0));
        assert_eq!(&2, a.get(1));
    }

    #[test]
    #[should_fail]
    fn test_get_2d_fail() {
        let mut a = ArrayBase::from_vec(~[0, 1, 2], -1);
        a.wrap(1);
        a.get(1);
    }

    #[test]
    #[should_fail]
    fn test_2d_slice_range_fail_low() {
        let mut a = ArrayBase::from_vec(~[0, 1, 2], -1);
        a.wrap(1);
        a.slice(0);
    }

    #[test]
    #[should_fail]
    fn test_2d_slice_range_fail_high() {
        let mut a = ArrayBase::from_vec(~[0, 1, 2], -1);
        a.wrap(1);
        a.slice(2);
    }

    #[test]
    fn test_2d_slice_get() {
        let mut a = ArrayBase::from_vec(~[0, 1, 2], -1);
        a.wrap(1);
        let s = a.slice(1);
        assert_eq!(&0, s.get(-1));
        assert_eq!(&1, s.get(0));
        assert_eq!(&2, s.get(1));
    }

    #[test]
    #[should_fail]
    fn test_push_move_wrong_lower_bound() {
        let mut a = ArrayBase::from_vec(~[1], -1);
        a.push_move(ArrayBase::from_vec(~[2], 0));
    }

    #[test]
    #[should_fail]
    fn test_push_move_wrong_dims() {
        let mut a = ArrayBase::from_vec(~[1], -1);
        a.wrap(1);
        a.push_move(ArrayBase::from_vec(~[1, 2], -1));
    }

    #[test]
    #[should_fail]
    fn test_push_move_wrong_dim_count() {
        let mut a = ArrayBase::from_vec(~[1], -1);
        a.wrap(1);
        let mut b = ArrayBase::from_vec(~[2], -1);
        b.wrap(1);
        a.push_move(b);
    }

    #[test]
    fn test_push_move_ok() {
        let mut a = ArrayBase::from_vec(~[1, 2], 0);
        a.wrap(0);
        a.push_move(ArrayBase::from_vec(~[3, 4], 0));
        let s = a.slice(0);
        assert_eq!(&1, s.get(0));
        assert_eq!(&2, s.get(1));
        let s = a.slice(1);
        assert_eq!(&3, s.get(0));
        assert_eq!(&4, s.get(1));
    }

    #[test]
    fn test_3d() {
        let mut a = ArrayBase::from_vec(~[0, 1], 0);
        a.wrap(0);
        a.push_move(ArrayBase::from_vec(~[2, 3], 0));
        a.wrap(0);
        let mut b = ArrayBase::from_vec(~[4, 5], 0);
        b.wrap(0);
        b.push_move(ArrayBase::from_vec(~[6, 7], 0));
        a.push_move(b);
        let s1 = a.slice(0);
        let s2 = s1.slice(0);
        assert_eq!(&0, s2.get(0));
        assert_eq!(&1, s2.get(1));
        let s2 = s1.slice(1);
        assert_eq!(&2, s2.get(0));
        assert_eq!(&3, s2.get(1));
        let s1 = a.slice(1);
        let s2 = s1.slice(0);
        assert_eq!(&4, s2.get(0));
        assert_eq!(&5, s2.get(1));
        let s2 = s1.slice(1);
        assert_eq!(&6, s2.get(0));
        assert_eq!(&7, s2.get(1));
    }

    #[test]
    fn test_mut() {
        let mut a = ArrayBase::from_vec(~[1, 2], 0);
        a.wrap(0);
        {
            let mut s = a.slice_mut(0);
            *s.get_mut(0) = 3;
        }
        let s = a.slice(0);
        assert_eq!(&3, s.get(0));
    }

    #[test]
    #[should_fail]
    fn test_base_overslice() {
        let a = ArrayBase::from_vec(~[1], 0);
        a.slice(0);
    }

    #[test]
    #[should_fail]
    fn test_slice_overslice() {
        let mut a = ArrayBase::from_vec(~[1], 0);
        a.wrap(0);
        let s = a.slice(0);
        s.slice(0);
    }
}
<|endoftext|>


<commit_before>use hubcaps::{
    self,
    gists::{self, Content, GistOptions},
    Credentials, Github,
};
use hyper;
use hyper_tls;
use std::collections::HashMap;
use tokio::{prelude::Future, runtime::current_thread::Runtime};

const FILENAME: &str = "playground.rs";
const DESCRIPTION: &str = "Code shared from the Rust Playground";

pub struct Gist {
    pub id: String,
    pub url: String,
    pub code: String,
}

impl From<gists::Gist> for Gist {
    fn from(other: gists::Gist) -> Self {
        let mut files: Vec<_> = other.files
            .into_iter()
            .map(|(name, file)| (name, file.content.unwrap_or_default()))
            .collect();

        files.sort_by(|(name1, _), (name2, _)| name1.cmp(name2));

        let code = match files.len() {
            0 | 1 => files.into_iter().map(|(_, content)| content).collect(),
            _ => {
                files
                    .into_iter()
                    .map(|(name, content)| format!("// {}\n\n{}\n\n", name, content))
                    .collect()
            }
        };

        Gist {
            id: other.id,
            url: other.html_url,
            code: code,
        }
    }
}

pub fn create(token: String, code: String) -> Gist {
    Runtime::new()
        .expect("unable to create runtime")
        .block_on(create_future(token, code))
        .expect("Unable to create gist")
    // TODO: Better reporting of failures
}

pub fn create_future(token: String, code: String) -> impl Future<Item = Gist, Error = hubcaps::Error> {
    let github = github(token);

    let file = Content {
        filename: None,
        content: code,
    };

    let mut files = HashMap::new();
    files.insert(FILENAME.into(), file);

    let options = GistOptions {
        description: Some(DESCRIPTION.into()),
        public: Some(true),
        files,
    };

    github
        .gists()
        .create(&options)
        .map(Into::into)
}

pub fn load(token: String, id: &str) -> Gist {
    Runtime::new()
        .expect("unable to create runtime")
        .block_on(load_future(token, id))
        .expect("Unable to load gist")
    // TODO: Better reporting of a 404
}

pub fn load_future(token: String, id: &str) -> impl Future<Item = Gist, Error = ::hubcaps::Error> {
    let github = github(token);

    github
        .gists()
        .get(id)
        .map(Into::into)
}

type HubcapConnector = hyper_tls::HttpsConnector<hyper::client::HttpConnector>;

fn github(token: String) -> Github<HubcapConnector> {
    Github::new(
        String::from("The Rust Playground"),
        Some(Credentials::Token(token)),
    )
}
<commit_msg>Remove trailing whitespace for multiple gist embed<commit_after>use hubcaps::{
    self,
    gists::{self, Content, GistOptions},
    Credentials, Github,
};
use hyper;
use hyper_tls;
use std::collections::HashMap;
use tokio::{prelude::Future, runtime::current_thread::Runtime};

const FILENAME: &str = "playground.rs";
const DESCRIPTION: &str = "Code shared from the Rust Playground";

pub struct Gist {
    pub id: String,
    pub url: String,
    pub code: String,
}

impl From<gists::Gist> for Gist {
    fn from(other: gists::Gist) -> Self {
        let mut files: Vec<_> = other.files
            .into_iter()
            .map(|(name, file)| (name, file.content.unwrap_or_default()))
            .collect();

        files.sort_by(|(name1, _), (name2, _)| name1.cmp(name2));

        let code = match files.len() {
            0 | 1 => files.into_iter().map(|(_, content)| content).collect(),
            _ => {
                files
                    .into_iter()
                    .map(|(name, content)| format!("// {}\n{}\n\n", name, content))
                    .collect()
            }
        };

        Gist {
            id: other.id,
            url: other.html_url,
            code: code,
        }
    }
}

pub fn create(token: String, code: String) -> Gist {
    Runtime::new()
        .expect("unable to create runtime")
        .block_on(create_future(token, code))
        .expect("Unable to create gist")
    // TODO: Better reporting of failures
}

pub fn create_future(token: String, code: String) -> impl Future<Item = Gist, Error = hubcaps::Error> {
    let github = github(token);

    let file = Content {
        filename: None,
        content: code,
    };

    let mut files = HashMap::new();
    files.insert(FILENAME.into(), file);

    let options = GistOptions {
        description: Some(DESCRIPTION.into()),
        public: Some(true),
        files,
    };

    github
        .gists()
        .create(&options)
        .map(Into::into)
}

pub fn load(token: String, id: &str) -> Gist {
    Runtime::new()
        .expect("unable to create runtime")
        .block_on(load_future(token, id))
        .expect("Unable to load gist")
    // TODO: Better reporting of a 404
}

pub fn load_future(token: String, id: &str) -> impl Future<Item = Gist, Error = ::hubcaps::Error> {
    let github = github(token);

    github
        .gists()
        .get(id)
        .map(Into::into)
}

type HubcapConnector = hyper_tls::HttpsConnector<hyper::client::HttpConnector>;

fn github(token: String) -> Github<HubcapConnector> {
    Github::new(
        String::from("The Rust Playground"),
        Some(Credentials::Token(token)),
    )
}
<|endoftext|>