<commit_before>use std::fs::File;
use std::ffi::OsStr;
use std::path::Path;
use std::io::prelude::*;
use std::io::Cursor;
use std::collections::HashSet;
use quick_xml::writer::Writer;
use quick_xml::events::{Event, BytesEnd, BytesStart, BytesDecl};
use quick_xml::errors::Result;
use tracer::TracerData;
use config::Config;

fn write_header<T:Write>(writer: &mut Writer<T>, config: &Config) -> Result<usize> {

    writer.write_event(Event::Start(BytesStart::borrowed(b"sources", b"sources".len())))?;
    writer.write_event(Event::Start(BytesStart::borrowed(b"source", b"source".len())))?;

    let parent_folder = match config.manifest.parent() {
        Some(s) => s.to_str().unwrap_or_default(),
        None => "",
    };

    writer.write(parent_folder.as_bytes()).unwrap();
    writer.write_event(Event::End(BytesEnd::borrowed(b"source")))?;
    writer.write_event(Event::End(BytesEnd::borrowed(b"sources")))
}

/// Input only from single source file
fn write_class<T:Write>(writer: &mut Writer<T>, manifest_path: &Path, coverage: &[&TracerData]) ->Result<usize> {
    if !coverage.is_empty() {
        let covered = coverage.iter().filter(|&x| (x.hits > 0)).count();
        let covered = (covered as f32)/(coverage.len() as f32);
        //let filename = coverage[0].path.file_name().unwrap_or_default().to_str().unwrap_or_default();
        let filename = match coverage[0].path.strip_prefix(manifest_path) {
            Ok(p) => p,
            _ => coverage[0].path.as_path()
        };
        let name = coverage[0].path.file_stem().unwrap_or_default().to_str().unwrap_or_default();

        let mut class = BytesStart::owned(b"class".to_vec(), b"class".len());
        class.push_attribute(("name", name));
        class.push_attribute(("filename", filename.to_str().unwrap_or_default()));
        class.push_attribute(("line-rate", covered.to_string().as_ref()));
        class.push_attribute(("branch-rate", "1.0"));
        class.push_attribute(("complexity", "0.0"));
        writer.write_event(Event::Start(class))?;
        writer.write_event(Event::Empty(BytesStart::owned(b"methods".to_vec(), b"methods".len())))?;
        writer.write_event(Event::Start(BytesStart::borrowed(b"lines", b"lines".len())))?;
        for trace in coverage {
            let mut line = BytesStart::owned(b"line".to_vec(), b"line".len());
            line.push_attribute(("number", trace.line.to_string().as_ref()));
            line.push_attribute(("hits", trace.hits.to_string().as_ref()));
            writer.write_event(Event::Empty(line))?;
        }
        writer.write_event(Event::End(BytesEnd::borrowed(b"lines")))?;
        writer.write_event(Event::End(BytesEnd::borrowed(b"class")))
    } else {
        Ok(0)
    }
}

/// Input only tracer data from a single source folder
fn write_package<T:Write>(mut writer: &mut Writer<T>,
                          package: &Path,
                          package_name: &str,
                          coverage: &[&TracerData]) -> Result<usize> {
    let covered = coverage.iter().filter(|&x| (x.hits > 0)).count();
    let covered = (covered as f32)/(coverage.len() as f32);

    let mut pack = BytesStart::owned(b"package".to_vec(), b"package".len());
    pack.push_attribute(("name", package_name));
    pack.push_attribute(("line-rate", covered.to_string().as_ref()));
    pack.push_attribute(("branch-rate", "1.0"));
    pack.push_attribute(("complexity", "0.0"));
    writer.write_event(Event::Start(pack))?;
    writer.write_event(Event::Start(BytesStart::borrowed(b"classes", b"classes".len())))?;
    let mut file_set: HashSet<&OsStr> = HashSet::new();

    for t in coverage.iter() {
        let filename = t.path.file_name();
        if !file_set.contains(filename.unwrap_or_default()) {
            file_set.insert(filename.unwrap_or_default());
            let class = coverage.iter()
                                .filter(|x| x.path.file_name() == filename)
                                .map(|x| *x)
                                .collect::<Vec<_>>();

            write_class(&mut writer, package, &class)?;
        }
    }

    writer.write_event(Event::End(BytesEnd::borrowed(b"classes")))?;
    writer.write_event(Event::End(BytesEnd::borrowed(b"package")))
}

pub fn export(coverage_data: &[TracerData], config: &Config) {
    let mut file = File::create("cobertura.xml").unwrap();
    let mut writer = Writer::new(Cursor::new(Vec::new()));
    writer.write_event(Event::Decl(BytesDecl::new(b"1.0", None, None))).unwrap();
    // Construct cobertura xml
    let covered = coverage_data.iter().filter(|&x| (x.hits > 0 )).count();
    let total = coverage_data.len();
    let line_rate = (covered as f32)/(total as f32);
    let mut cov = BytesStart::owned(b"coverage".to_vec(), b"coverage".len());
    cov.push_attribute(("line-rate", line_rate.to_string().as_ref()));
    cov.push_attribute(("branch-rate", "1.0"));
    cov.push_attribute(("version", "1.9"));
    cov.push_attribute(("timestamp", "1502816840"));

    writer.write_event(Event::Start(cov)).unwrap();
    let _ = write_header(&mut writer, &config);
    // other data
    writer.write_event(Event::Start(BytesStart::borrowed(b"packages", b"packages".len()))).unwrap();

    let mut folder_set: HashSet<&Path> = HashSet::new();
    for t in coverage_data.iter() {
        let parent = match t.path.parent() {
            Some(x) => x,
            None => continue,
        };
        if !folder_set.contains(&parent) {
            folder_set.insert(parent);
            let manifest_path = config.manifest.parent().unwrap_or(&config.manifest);
            let package_name = match parent.strip_prefix(manifest_path) {
                Ok(p) => p,
                _ => manifest_path,
            };
            let package_name = package_name.to_str().unwrap_or_default();
            let package = coverage_data.iter().filter(|x| x.path.parent() == Some(&parent)).collect::<Vec<_>>();
            let _ = write_package(&mut writer, &manifest_path, package_name, &package);
        }
    }

    writer.write_event(Event::End(BytesEnd::borrowed(b"packages"))).unwrap();
    writer.write_event(Event::End(BytesEnd::borrowed(b"coverage"))).unwrap();
    let result = writer.into_inner().into_inner();
    file.write_all(&result).unwrap();
}
<commit_msg>Added unix timestamp to cobertura report<commit_after>use std::time::{SystemTime, UNIX_EPOCH};
use std::fs::File;
use std::ffi::OsStr;
use std::path::Path;
use std::io::prelude::*;
use std::io::Cursor;
use std::collections::HashSet;
use quick_xml::writer::Writer;
use quick_xml::events::{Event, BytesEnd, BytesStart, BytesDecl};
use quick_xml::errors::Result;
use tracer::TracerData;
use config::Config;

fn write_header<T:Write>(writer: &mut Writer<T>, config: &Config) -> Result<usize> {

    writer.write_event(Event::Start(BytesStart::borrowed(b"sources", b"sources".len())))?;
    writer.write_event(Event::Start(BytesStart::borrowed(b"source", b"source".len())))?;

    let parent_folder = match config.manifest.parent() {
        Some(s) => s.to_str().unwrap_or_default(),
        None => "",
    };
    writer.write(parent_folder.as_bytes()).unwrap();
    writer.write_event(Event::End(BytesEnd::borrowed(b"source")))?;
    writer.write_event(Event::End(BytesEnd::borrowed(b"sources")))
}

/// Input only from single source file
fn write_class<T:Write>(writer: &mut Writer<T>, manifest_path: &Path, coverage: &[&TracerData]) ->Result<usize> {
    if !coverage.is_empty() {
        let covered = coverage.iter().filter(|&x| (x.hits > 0)).count();
        let covered = (covered as f32)/(coverage.len() as f32);
        //let filename = coverage[0].path.file_name().unwrap_or_default().to_str().unwrap_or_default();
        let filename = match coverage[0].path.strip_prefix(manifest_path) {
            Ok(p) => p,
            _ => coverage[0].path.as_path()
        };
        let name = coverage[0].path.file_stem().unwrap_or_default().to_str().unwrap_or_default();

        let mut class = BytesStart::owned(b"class".to_vec(), b"class".len());
        class.push_attribute(("name", name));
        class.push_attribute(("filename", filename.to_str().unwrap_or_default()));
        class.push_attribute(("line-rate", covered.to_string().as_ref()));
        class.push_attribute(("branch-rate", "1.0"));
        class.push_attribute(("complexity", "0.0"));
        writer.write_event(Event::Start(class))?;
        writer.write_event(Event::Empty(BytesStart::owned(b"methods".to_vec(), b"methods".len())))?;
        writer.write_event(Event::Start(BytesStart::borrowed(b"lines", b"lines".len())))?;
        for trace in coverage {
            let mut line = BytesStart::owned(b"line".to_vec(), b"line".len());
            line.push_attribute(("number", trace.line.to_string().as_ref()));
            line.push_attribute(("hits", trace.hits.to_string().as_ref()));
            writer.write_event(Event::Empty(line))?;
        }
        writer.write_event(Event::End(BytesEnd::borrowed(b"lines")))?;
        writer.write_event(Event::End(BytesEnd::borrowed(b"class")))
    } else {
        Ok(0)
    }
}

/// Input only tracer data from a single source folder
fn write_package<T:Write>(mut writer: &mut Writer<T>,
                          package: &Path,
                          package_name: &str,
                          coverage: &[&TracerData]) -> Result<usize> {
    let covered = coverage.iter().filter(|&x| (x.hits > 0)).count();
    let covered = (covered as f32)/(coverage.len() as f32);

    let mut pack = BytesStart::owned(b"package".to_vec(), b"package".len());
    pack.push_attribute(("name", package_name));
    pack.push_attribute(("line-rate", covered.to_string().as_ref()));
    pack.push_attribute(("branch-rate", "1.0"));
    pack.push_attribute(("complexity", "0.0"));
    writer.write_event(Event::Start(pack))?;
    writer.write_event(Event::Start(BytesStart::borrowed(b"classes", b"classes".len())))?;
    let mut file_set: HashSet<&OsStr> = HashSet::new();

    for t in coverage.iter() {
        let filename = t.path.file_name();
        if !file_set.contains(filename.unwrap_or_default()) {
            file_set.insert(filename.unwrap_or_default());
            let class = coverage.iter()
                                .filter(|x| x.path.file_name() == filename)
                                .map(|x| *x)
                                .collect::<Vec<_>>();

            write_class(&mut writer, package, &class)?;
        }
    }

    writer.write_event(Event::End(BytesEnd::borrowed(b"classes")))?;
    writer.write_event(Event::End(BytesEnd::borrowed(b"package")))
}

pub fn export(coverage_data: &[TracerData], config: &Config) {
    let mut file = File::create("cobertura.xml").unwrap();
    let mut writer = Writer::new(Cursor::new(Vec::new()));
    writer.write_event(Event::Decl(BytesDecl::new(b"1.0", None, None))).unwrap();
    // Construct cobertura xml
    let covered = coverage_data.iter().filter(|&x| (x.hits > 0 )).count();
    let total = coverage_data.len();
    let line_rate = (covered as f32)/(total as f32);
    let mut cov = BytesStart::owned(b"coverage".to_vec(), b"coverage".len());
    cov.push_attribute(("line-rate", line_rate.to_string().as_ref()));
    cov.push_attribute(("branch-rate", "1.0"));
    cov.push_attribute(("version", "1.9"));

    if let Ok(s) = SystemTime::now().duration_since(UNIX_EPOCH) {
        cov.push_attribute(("timestamp", s.as_secs().to_string().as_ref()));
    } else {
        cov.push_attribute(("timestamp", "0"));
    }

    writer.write_event(Event::Start(cov)).unwrap();
    let _ = write_header(&mut writer, &config);
    // other data
    writer.write_event(Event::Start(BytesStart::borrowed(b"packages", b"packages".len()))).unwrap();

    let mut folder_set: HashSet<&Path> = HashSet::new();
    for t in coverage_data.iter() {
        let parent = match t.path.parent() {
            Some(x) => x,
            None => continue,
        };
        if !folder_set.contains(&parent) {
            folder_set.insert(parent);
            let manifest_path = config.manifest.parent().unwrap_or(&config.manifest);
            let package_name = match parent.strip_prefix(manifest_path) {
                Ok(p) => p,
                _ => manifest_path,
            };
            let package_name = package_name.to_str().unwrap_or_default();
            let package = coverage_data.iter().filter(|x| x.path.parent() == Some(&parent)).collect::<Vec<_>>();
            let _ = write_package(&mut writer, &manifest_path, package_name, &package);
        }
    }

    writer.write_event(Event::End(BytesEnd::borrowed(b"packages"))).unwrap();
    writer.write_event(Event::End(BytesEnd::borrowed(b"coverage"))).unwrap();
    let result = writer.into_inner().into_inner();
    file.write_all(&result).unwrap();
}
<|endoftext|>


<commit_before>// Copyright 2013-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use types::Type;
use types::StaticType;
use ffi as glib_ffi;
use translate::*;
use std::borrow::{Borrow, Cow, ToOwned};
use std::cmp::{PartialEq, Eq};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::slice;
use value::{Value, ToValue, SetValue, FromValueOptional};
use gobject_ffi;

/// Describes `Variant` types.
///
/// The `Variant` type system (based on the D-Bus one) describes types with
/// "type strings". `VariantType` is an owned immutable type string (you can
/// think of it as a `Box<str>` statically guaranteed to be a valid type
/// string), `&VariantTy` is a borrowed one (like `&str`).
pub struct VariantType {
    // GVariantType* essentially is a char*, that always is valid UTF-8 but
    // isn't NUL-terminated.
    ptr: *mut glib_ffi::GVariantType,
    // We query the length on creation assuming it's cheap (because type strings
    // are short) and likely to happen anyway.
    len: usize,
}

impl VariantType {
    /// Tries to create a `VariantType` from a string slice.
    ///
    /// Returns `Ok` if the string is a valid type string, `Err` otherwise.
    pub fn new(type_string: &str) -> Result<VariantType, ()> {
        VariantTy::new(type_string).map(ToOwned::to_owned)
    }
}

unsafe impl Send for VariantType { }
unsafe impl Sync for VariantType { }

impl Drop for VariantType {
    fn drop(&mut self) {
        unsafe { glib_ffi::g_variant_type_free(self.ptr) }
    }
}

impl Borrow<VariantTy> for VariantType {
    fn borrow(&self) -> &VariantTy {
        self
    }
}

impl Clone for VariantType {
    fn clone(&self) -> VariantType {
        unsafe {
            VariantType {
                ptr: glib_ffi::g_variant_type_copy(self.ptr),
                len: self.len,
            }
        }
    }
}

impl Deref for VariantType {
    type Target = VariantTy;
    fn deref(&self) -> &VariantTy {
        unsafe {
            &*(slice::from_raw_parts(self.ptr as *const u8, self.len) as *const [u8] as *const VariantTy)
        }
    }
}

impl fmt::Debug for VariantType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        <VariantTy as fmt::Debug>::fmt(self, f)
    }
}

impl fmt::Display for VariantType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.to_str())
    }
}

impl Hash for VariantType {
    fn hash<H: Hasher>(&self, state: &mut H) {
        <VariantTy as Hash>::hash(self, state)
    }
}

impl<'a> Into<Cow<'a, VariantTy>> for VariantType {
    fn into(self) -> Cow<'a, VariantTy> {
        Cow::Owned(self)
    }
}

#[doc(hidden)]
impl<'a> ToGlibPtrMut<'a, *mut glib_ffi::GVariantType> for VariantType {
    type Storage = &'a mut Self;

    fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut glib_ffi::GVariantType, Self> {
        StashMut(self.ptr, self)
    }
}

#[doc(hidden)]
impl FromGlibPtrNone<*const glib_ffi::GVariantType> for VariantType {
    unsafe fn from_glib_none(ptr: *const glib_ffi::GVariantType) -> VariantType {
        VariantTy::from_ptr(ptr).to_owned()
    }
}

#[doc(hidden)]
impl FromGlibPtrFull<*const glib_ffi::GVariantType> for VariantType {
    unsafe fn from_glib_full(ptr: *const glib_ffi::GVariantType) -> VariantType {
        // Don't assume ownership of a const pointer.
        // A transfer: full annotation on a `const GVariantType*` is likely a bug.
        VariantTy::from_ptr(ptr).to_owned()
    }
}

/// Describes `Variant` types.
///
/// This is a borrowed counterpart of [`VariantType`](struct.VariantType.html).
/// Essentially it's a `str` statically guaranteed to be a valid type string.
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct VariantTy {
    inner: str,
}

impl VariantTy {
    /// Tries to create a `&VariantTy` from a string slice.
    ///
    /// Returns `Ok` if the string is a valid type string, `Err` otherwise.
    pub fn new(type_string: &str) -> Result<&VariantTy, ()> {
        let ptr = type_string.as_ptr();
        let limit = ptr as usize + type_string.len();
        let mut end = 0_usize;
        unsafe {
            let ok = from_glib(glib_ffi::g_variant_type_string_scan(ptr as *const _,
                limit as *const _, &mut end as *mut usize as *mut _));
            if ok && end == limit {
                Ok(&*(type_string.as_bytes() as *const [u8] as *const VariantTy))
            } else {
                Err(())
            }
        }
    }

    /// Converts a type string into `&VariantTy` without any checks.
    pub unsafe fn from_str_unchecked(type_string: &str) -> &VariantTy {
        &*(type_string as *const str as *const VariantTy)
    }

    /// Creates `&VariantTy` with a wildcard lifetime from a `GVariantType`
    /// pointer.
    #[doc(hidden)]
    pub unsafe fn from_ptr<'a>(ptr: *const glib_ffi::GVariantType) -> &'a VariantTy {
        let len = glib_ffi::g_variant_type_get_string_length(ptr) as usize;
        &*(slice::from_raw_parts(ptr as *const u8, len) as *const [u8] as *const VariantTy)
    }

    /// Returns a `GVariantType` pointer.
    #[doc(hidden)]
    pub fn as_ptr(&self) -> *const glib_ffi::GVariantType {
        self.inner.as_ptr() as *const _
    }

    /// Converts to a string slice.
    pub fn to_str(&self) -> &str {
        &self.inner
    }
}

unsafe impl Sync for VariantTy { }

#[doc(hidden)]
impl<'a> ToGlibPtr<'a, *const glib_ffi::GVariantType> for VariantTy {
    type Storage = &'a Self;

    fn to_glib_none(&'a self) -> Stash<'a, *const glib_ffi::GVariantType, Self> {
        Stash(self.as_ptr(), self)
    }
}

impl fmt::Display for VariantTy {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.to_str())
    }
}

impl<'a> Into<Cow<'a, VariantTy>> for &'a VariantTy {
    fn into(self) -> Cow<'a, VariantTy> {
        Cow::Borrowed(self)
    }
}

impl ToOwned for VariantTy {
    type Owned = VariantType;

    fn to_owned(&self) -> VariantType {
        unsafe {
            VariantType {
                ptr: glib_ffi::g_variant_type_copy(self.as_ptr()),
                len: self.inner.len(),
            }
        }
    }
}

impl StaticType for VariantTy {
    fn static_type() -> Type {
        unsafe { from_glib(glib_ffi::g_variant_type_get_gtype()) }
    }
}

impl SetValue for VariantTy {
    unsafe fn set_value(value: &mut Value, this: &Self) {
        gobject_ffi::g_value_set_boxed(value.to_glib_none_mut().0, this.to_glib_none().0 as glib_ffi::gpointer)
    }
}

impl<'a> FromValueOptional<'a> for &'a VariantTy {
    unsafe fn from_value_optional(value: &'a Value) -> Option<Self> {
        let cvty = gobject_ffi::g_value_dup_boxed(value.to_glib_none().0) as *mut glib_ffi::GVariantType;
        if cvty.is_null() {
            None
        } else {
            Some(VariantTy::from_ptr(cvty))
        }
    }
}

impl StaticType for VariantType {
    fn static_type() -> Type {
        unsafe { from_glib(glib_ffi::g_variant_type_get_gtype()) }
    }
}

impl SetValue for VariantType {
    unsafe fn set_value(value: &mut Value, this: &Self) {
        gobject_ffi::g_value_set_boxed(value.to_glib_none_mut().0, this.to_glib_none().0 as glib_ffi::gpointer)
    }
}

impl<'a> FromValueOptional<'a> for VariantType {
    unsafe fn from_value_optional(value: &'a Value) -> Option<Self> {
        Option::<VariantType>::from_glib_full(gobject_ffi::g_value_dup_boxed(value.to_glib_none().0) as *mut glib_ffi::GVariantType)
    }
}

impl PartialEq for VariantType {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        <VariantTy as PartialEq>::eq(self, other)
    }
}

macro_rules! impl_eq {
    ($lhs:ty, $rhs: ty) => {
        impl<'a, 'b> PartialEq<$rhs> for $lhs {
            #[inline]
            fn eq(&self, other: &$rhs) -> bool {
                <VariantTy as PartialEq>::eq(self, other)
            }
        }

        impl<'a, 'b> PartialEq<$lhs> for $rhs {
            #[inline]
            fn eq(&self, other: &$lhs) -> bool {
                <VariantTy as PartialEq>::eq(self, other)
            }
        }
    }
}

impl_eq!(VariantType, VariantTy);
impl_eq!(VariantType, &'a VariantTy);
impl_eq!(VariantType, Cow<'a, VariantTy>);
impl_eq!(&'a VariantTy, Cow<'b, VariantTy>);

macro_rules! impl_str_eq {
    ($lhs:ty, $rhs: ty) => {
        impl<'a, 'b> PartialEq<$rhs> for $lhs {
            #[inline]
            fn eq(&self, other: &$rhs) -> bool {
                self.to_str().eq(&other[..])
            }
        }

        impl<'a, 'b> PartialEq<$lhs> for $rhs {
            #[inline]
            fn eq(&self, other: &$lhs) -> bool {
                self[..].eq(other.to_str())
            }
        }
    }
}

impl_str_eq!(VariantTy, str);
impl_str_eq!(VariantTy, &'a str);
impl_str_eq!(&'a VariantTy, str);
impl_str_eq!(VariantTy, String);
impl_str_eq!(&'a VariantTy, String);
impl_str_eq!(VariantType, str);
impl_str_eq!(VariantType, &'a str);
impl_str_eq!(VariantType, String);

impl Eq for VariantType { }

#[cfg(test)]
mod tests {
    use ffi as glib_ffi;
    use translate::*;
    use super::*;

    unsafe fn equal<T, U>(ptr1: *const T, ptr2: *const U) -> bool {
        from_glib(glib_ffi::g_variant_type_equal(ptr1 as *const _, ptr2 as *const _))
    }

    #[test]
    fn new() {
        let ty = VariantTy::new("((iii)s)").unwrap();
        unsafe {
            assert!(equal(ty.as_ptr(), b"((iii)s)\0" as *const u8));
        }
    }

    #[test]
    fn new_empty() {
        assert!(VariantTy::new("").is_err());
    }

    #[test]
    fn new_with_nul() {
        assert!(VariantTy::new("((iii\0)s)").is_err());
    }

    #[test]
    fn new_too_short() {
        assert!(VariantTy::new("((iii").is_err());
    }

    #[test]
    fn new_too_long() {
        assert!(VariantTy::new("(iii)s").is_err());
    }

    #[test]
    fn eq() {
        let ty1 = VariantTy::new("((iii)s)").unwrap();
        let ty2 = VariantTy::new("((iii)s)").unwrap();
        assert_eq!(ty1, ty2);
        assert_eq!(ty1, "((iii)s)");
        unsafe {
            assert!(equal(ty1.as_ptr(), ty2.as_ptr()));
        }
    }

    #[test]
    fn ne() {
        let ty1 = VariantTy::new("((iii)s)").unwrap();
        let ty2 = VariantTy::new("((iii)o)").unwrap();
        assert!(ty1 != ty2);
        assert!(ty1 != "((iii)o)");
        unsafe {
            assert!(!equal(ty1.as_ptr(), ty2.as_ptr()));
        }
    }

    #[test]
    fn from_bytes() {
        unsafe {
            let ty = VariantTy::from_ptr(b"((iii)s)" as *const u8 as *const _);
            assert_eq!(ty, "((iii)s)");
            assert!(equal(ty.as_ptr(), "((iii)s)".as_ptr()));
        }
    }

    #[test]
    fn to_owned() {
        let ty1 = VariantTy::new("((iii)s)").unwrap();
        let ty2 = ty1.to_owned();
        assert_eq!(ty1, ty2);
        assert_eq!(ty2, "((iii)s)");
        unsafe {
            assert!(equal(ty1.as_ptr(), ty2.as_ptr()));
        }
    }

    #[test]
    fn value() {
        let ty1 = VariantType::new("*").unwrap();
        let tyv = ty1.to_value();
        let ty2 = tyv.get::<VariantType>().unwrap();
        assert_eq!(ty1, ty2);
    }

}
<commit_msg>fix memory issues<commit_after>// Copyright 2013-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use types::Type;
use types::StaticType;
use ffi as glib_ffi;
use translate::*;
use std::borrow::{Borrow, Cow, ToOwned};
use std::cmp::{PartialEq, Eq};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::slice;
use value::{Value, ToValue, SetValue, FromValueOptional, SetValueOptional};
use gobject_ffi;

/// Describes `Variant` types.
///
/// The `Variant` type system (based on the D-Bus one) describes types with
/// "type strings". `VariantType` is an owned immutable type string (you can
/// think of it as a `Box<str>` statically guaranteed to be a valid type
/// string), `&VariantTy` is a borrowed one (like `&str`).
pub struct VariantType {
    // GVariantType* essentially is a char*, that always is valid UTF-8 but
    // isn't NUL-terminated.
    ptr: *mut glib_ffi::GVariantType,
    // We query the length on creation assuming it's cheap (because type strings
    // are short) and likely to happen anyway.
    len: usize,
}

impl VariantType {
    /// Tries to create a `VariantType` from a string slice.
    ///
    /// Returns `Ok` if the string is a valid type string, `Err` otherwise.
    pub fn new(type_string: &str) -> Result<VariantType, ()> {
        VariantTy::new(type_string).map(ToOwned::to_owned)
    }
}

unsafe impl Send for VariantType { }
unsafe impl Sync for VariantType { }

impl Drop for VariantType {
    fn drop(&mut self) {
        unsafe { glib_ffi::g_variant_type_free(self.ptr) }
    }
}

impl Borrow<VariantTy> for VariantType {
    fn borrow(&self) -> &VariantTy {
        self
    }
}

impl Clone for VariantType {
    fn clone(&self) -> VariantType {
        unsafe {
            VariantType {
                ptr: glib_ffi::g_variant_type_copy(self.ptr),
                len: self.len,
            }
        }
    }
}

impl Deref for VariantType {
    type Target = VariantTy;
    fn deref(&self) -> &VariantTy {
        unsafe {
            &*(slice::from_raw_parts(self.ptr as *const u8, self.len) as *const [u8] as *const VariantTy)
        }
    }
}

impl fmt::Debug for VariantType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        <VariantTy as fmt::Debug>::fmt(self, f)
    }
}

impl fmt::Display for VariantType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.to_str())
    }
}

impl Hash for VariantType {
    fn hash<H: Hasher>(&self, state: &mut H) {
        <VariantTy as Hash>::hash(self, state)
    }
}

impl<'a> Into<Cow<'a, VariantTy>> for VariantType {
    fn into(self) -> Cow<'a, VariantTy> {
        Cow::Owned(self)
    }
}

#[doc(hidden)]
impl<'a> ToGlibPtrMut<'a, *mut glib_ffi::GVariantType> for VariantType {
    type Storage = &'a mut Self;

    fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut glib_ffi::GVariantType, Self> {
        StashMut(self.ptr, self)
    }
}

#[doc(hidden)]
impl FromGlibPtrNone<*const glib_ffi::GVariantType> for VariantType {
    unsafe fn from_glib_none(ptr: *const glib_ffi::GVariantType) -> VariantType {
        VariantTy::from_ptr(ptr).to_owned()
    }
}

#[doc(hidden)]
impl FromGlibPtrFull<*const glib_ffi::GVariantType> for VariantType {
    unsafe fn from_glib_full(ptr: *const glib_ffi::GVariantType) -> VariantType {
        // Don't assume ownership of a const pointer.
        // A transfer: full annotation on a `const GVariantType*` is likely a bug.
        VariantTy::from_ptr(ptr).to_owned()
    }
}

/// Describes `Variant` types.
///
/// This is a borrowed counterpart of [`VariantType`](struct.VariantType.html).
/// Essentially it's a `str` statically guaranteed to be a valid type string.
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct VariantTy {
    inner: str,
}

impl VariantTy {
    /// Tries to create a `&VariantTy` from a string slice.
    ///
    /// Returns `Ok` if the string is a valid type string, `Err` otherwise.
    pub fn new(type_string: &str) -> Result<&VariantTy, ()> {
        let ptr = type_string.as_ptr();
        let limit = ptr as usize + type_string.len();
        let mut end = 0_usize;
        unsafe {
            let ok = from_glib(glib_ffi::g_variant_type_string_scan(ptr as *const _,
                limit as *const _, &mut end as *mut usize as *mut _));
            if ok && end == limit {
                Ok(&*(type_string.as_bytes() as *const [u8] as *const VariantTy))
            } else {
                Err(())
            }
        }
    }

    /// Converts a type string into `&VariantTy` without any checks.
    pub unsafe fn from_str_unchecked(type_string: &str) -> &VariantTy {
        &*(type_string as *const str as *const VariantTy)
    }

    /// Creates `&VariantTy` with a wildcard lifetime from a `GVariantType`
    /// pointer.
    #[doc(hidden)]
    pub unsafe fn from_ptr<'a>(ptr: *const glib_ffi::GVariantType) -> &'a VariantTy {
        let len = glib_ffi::g_variant_type_get_string_length(ptr) as usize;
        &*(slice::from_raw_parts(ptr as *const u8, len) as *const [u8] as *const VariantTy)
    }

    /// Returns a `GVariantType` pointer.
    #[doc(hidden)]
    pub fn as_ptr(&self) -> *const glib_ffi::GVariantType {
        self.inner.as_ptr() as *const _
    }

    /// Converts to a string slice.
    pub fn to_str(&self) -> &str {
        &self.inner
    }
}

unsafe impl Sync for VariantTy { }

#[doc(hidden)]
impl<'a> ToGlibPtr<'a, *const glib_ffi::GVariantType> for VariantTy {
    type Storage = &'a Self;

    fn to_glib_none(&'a self) -> Stash<'a, *const glib_ffi::GVariantType, Self> {
        Stash(self.as_ptr(), self)
    }
}

impl fmt::Display for VariantTy {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.to_str())
    }
}

impl<'a> Into<Cow<'a, VariantTy>> for &'a VariantTy {
    fn into(self) -> Cow<'a, VariantTy> {
        Cow::Borrowed(self)
    }
}

impl ToOwned for VariantTy {
    type Owned = VariantType;

    fn to_owned(&self) -> VariantType {
        unsafe {
            VariantType {
                ptr: glib_ffi::g_variant_type_copy(self.as_ptr()),
                len: self.inner.len(),
            }
        }
    }
}

impl StaticType for VariantTy {
    fn static_type() -> Type {
        unsafe { from_glib(glib_ffi::g_variant_type_get_gtype()) }
    }
}

impl SetValue for VariantTy {
    unsafe fn set_value(value: &mut Value, this: &Self) {
        gobject_ffi::g_value_set_boxed(value.to_glib_none_mut().0, this.to_glib_none().0 as glib_ffi::gpointer)
    }
}

impl SetValueOptional for VariantTy {
    unsafe fn set_value_optional(value: &mut Value, this: Option<&Self>) {
        gobject_ffi::g_value_set_boxed(value.to_glib_none_mut().0, this.to_glib_none().0 as glib_ffi::gpointer)
    }
}

impl<'a> FromValueOptional<'a> for &'a VariantTy {
    unsafe fn from_value_optional(value: &'a Value) -> Option<Self> {
        let cvty = gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *mut glib_ffi::GVariantType;
        assert!(cvty.is_null());
        Some(VariantTy::from_ptr(cvty))
    }
}

impl StaticType for VariantType {
    fn static_type() -> Type {
        unsafe { from_glib(glib_ffi::g_variant_type_get_gtype()) }
    }
}

impl SetValue for VariantType {
    unsafe fn set_value(value: &mut Value, this: &Self) {
        gobject_ffi::g_value_set_boxed(value.to_glib_none_mut().0, this.to_glib_none().0 as glib_ffi::gpointer)
    }
}

// impl SetValueOptional for VariantType {
//     unsafe fn set_value_optional(value: &mut Value, this: Option<&Self>) {
//         gobject_ffi::g_value_set_boxed(value.to_glib_none_mut().0, this.to_glib_none().0 as glib_ffi::gpointer)
//     }
// }

impl<'a> FromValueOptional<'a> for VariantType {
    unsafe fn from_value_optional(value: &'a Value) -> Option<Self> {
        Option::<VariantType>::from_glib_none(gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *mut glib_ffi::GVariantType)
    }
}

impl PartialEq for VariantType {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        <VariantTy as PartialEq>::eq(self, other)
    }
}

macro_rules! impl_eq {
    ($lhs:ty, $rhs: ty) => {
        impl<'a, 'b> PartialEq<$rhs> for $lhs {
            #[inline]
            fn eq(&self, other: &$rhs) -> bool {
                <VariantTy as PartialEq>::eq(self, other)
            }
        }

        impl<'a, 'b> PartialEq<$lhs> for $rhs {
            #[inline]
            fn eq(&self, other: &$lhs) -> bool {
                <VariantTy as PartialEq>::eq(self, other)
            }
        }
    }
}

impl_eq!(VariantType, VariantTy);
impl_eq!(VariantType, &'a VariantTy);
impl_eq!(VariantType, Cow<'a, VariantTy>);
impl_eq!(&'a VariantTy, Cow<'b, VariantTy>);

macro_rules! impl_str_eq {
    ($lhs:ty, $rhs: ty) => {
        impl<'a, 'b> PartialEq<$rhs> for $lhs {
            #[inline]
            fn eq(&self, other: &$rhs) -> bool {
                self.to_str().eq(&other[..])
            }
        }

        impl<'a, 'b> PartialEq<$lhs> for $rhs {
            #[inline]
            fn eq(&self, other: &$lhs) -> bool {
                self[..].eq(other.to_str())
            }
        }
    }
}

impl_str_eq!(VariantTy, str);
impl_str_eq!(VariantTy, &'a str);
impl_str_eq!(&'a VariantTy, str);
impl_str_eq!(VariantTy, String);
impl_str_eq!(&'a VariantTy, String);
impl_str_eq!(VariantType, str);
impl_str_eq!(VariantType, &'a str);
impl_str_eq!(VariantType, String);

impl Eq for VariantType { }

#[cfg(test)]
mod tests {
    use ffi as glib_ffi;
    use translate::*;
    use super::*;

    unsafe fn equal<T, U>(ptr1: *const T, ptr2: *const U) -> bool {
        from_glib(glib_ffi::g_variant_type_equal(ptr1 as *const _, ptr2 as *const _))
    }

    #[test]
    fn new() {
        let ty = VariantTy::new("((iii)s)").unwrap();
        unsafe {
            assert!(equal(ty.as_ptr(), b"((iii)s)\0" as *const u8));
        }
    }

    #[test]
    fn new_empty() {
        assert!(VariantTy::new("").is_err());
    }

    #[test]
    fn new_with_nul() {
        assert!(VariantTy::new("((iii\0)s)").is_err());
    }

    #[test]
    fn new_too_short() {
        assert!(VariantTy::new("((iii").is_err());
    }

    #[test]
    fn new_too_long() {
        assert!(VariantTy::new("(iii)s").is_err());
    }

    #[test]
    fn eq() {
        let ty1 = VariantTy::new("((iii)s)").unwrap();
        let ty2 = VariantTy::new("((iii)s)").unwrap();
        assert_eq!(ty1, ty2);
        assert_eq!(ty1, "((iii)s)");
        unsafe {
            assert!(equal(ty1.as_ptr(), ty2.as_ptr()));
        }
    }

    #[test]
    fn ne() {
        let ty1 = VariantTy::new("((iii)s)").unwrap();
        let ty2 = VariantTy::new("((iii)o)").unwrap();
        assert!(ty1 != ty2);
        assert!(ty1 != "((iii)o)");
        unsafe {
            assert!(!equal(ty1.as_ptr(), ty2.as_ptr()));
        }
    }

    #[test]
    fn from_bytes() {
        unsafe {
            let ty = VariantTy::from_ptr(b"((iii)s)" as *const u8 as *const _);
            assert_eq!(ty, "((iii)s)");
            assert!(equal(ty.as_ptr(), "((iii)s)".as_ptr()));
        }
    }

    #[test]
    fn to_owned() {
        let ty1 = VariantTy::new("((iii)s)").unwrap();
        let ty2 = ty1.to_owned();
        assert_eq!(ty1, ty2);
        assert_eq!(ty2, "((iii)s)");
        unsafe {
            assert!(equal(ty1.as_ptr(), ty2.as_ptr()));
        }
    }

    #[test]
    fn value() {
        let ty1 = VariantType::new("*").unwrap();
        let tyv = ty1.to_value();
        let ty2 = tyv.get::<VariantType>().unwrap();
        assert_eq!(ty1, ty2);
    }

}
<|endoftext|>


<commit_before>//! Holds common search paths implementations
use super::config::data::PATHS_MAX;
use std::path::Path;

/// This struct holds the search paths, which can change before startup but not later
pub struct SearchPath {
    paths: Vec<String>,
}

impl SearchPath {
    pub fn new(paths: &Vec<String>) -> Self {
        // 2) clean-up and straighten
        let cleaned_paths = clean_paths(&paths);
        // 3) and then cut-off unwanted parts (more paths input than PATHS_MAX)
        let paths = cleaned_paths
            .into_iter()
            .enumerate()
            .filter(|(i, _el)| i < &PATHS_MAX)
            .map(|(_i, el)| el)
            .collect();
        Self { paths }
    }
    pub fn update(&mut self, new_vec: Vec<String>) {
        let cleaned_new = clean_paths(&new_vec);
        self.paths = cleaned_new;
    }
    pub fn read(&self) -> Vec<String> {
        self.paths.clone()
    }
    pub fn len(&self) -> usize {
        self.paths.len()
    }
}

/// Clean paths checks that paths exists and that intersection
/// paths are exluded, also down-up-climbing of existing paths
/// hierarchy are working!
///
/// E.g. given:
///     "/home/user/Music/audiobooks/E-F"
///     "/home/user/Music/audiobooks/E-F/George Orwell"
///     "/home/user/Music/audiobooks/A-D/../../audiobooks/A-D"
///     "/home/user/Music/audiobooks/A-D"
///     "/home/user/Music/audiobooks/E-F/George Orwell/Animal Farm"
///
/// will lead to:
///     "/home/user/Music/audiobooks/E-F"
///     "/home/user/Music/audiobooks/A-D"
///
///
/// todo: implement vfs then write document_test
fn clean_paths(unchecked_paths: &Vec<String>) -> Vec<String> {
    let mut checked_paths: Vec<String> = vec![];
    for unchecked in unchecked_paths {
        if let Ok(path_ok) = Path::new(unchecked).canonicalize() {
            if path_ok.is_dir() {
                if let Some(path_str) = path_ok.to_str() {
                    let mut is_add_worthy = true;
                    let path_string = path_str.to_string();
                    for checked in checked_paths.iter_mut() {
                        // check all already checked path, if there is
                        // no reason to not add it, add it.
                        let path_len = path_str.len();
                        let checked_len = checked.len();
                        if path_len < checked_len {
                            // if substring matches checked must be exchanged
                            if checked[..path_len] == path_string {
                                *checked = path_string;
                                is_add_worthy = false;
                                break;
                            }
                        } else {
                            // only add if not substring with any
                            if path_string[..checked_len] == *checked {
                                //
                                is_add_worthy = false;
                                break;
                            }
                        }
                    }
                    // add it if there is no reason not to
                    if is_add_worthy {
                        checked_paths.push(path_str.to_string());
                    }
                } else {
                    warn!(
                        "Path {:?} has some encoding problem, and will not be included in search!",
                        unchecked
                    )
                }
            } else {
                warn!("Path {:?} does not exist as directory/folder, and will not be included in search!", unchecked)
            }
        } else {
            error!(
                "Path {:?} is not a valid directory/folder, and will not be included in search!",
                unchecked
            );
        }
    }
    checked_paths
}

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

    fn init() {
        let _ = env_logger::builder().is_test(true).try_init();
    }

    // todo: use crate vfs for better unit tests
    static HAS_FS_UP: bool = false;
    // run: mkdir -p "/tmp/adbf/Music/audiobooks/A-D"
    //      mkdir -p "/tmp/adbf/Music/audiobooks/E-F/George Orwell/Animal Farm"
    //      mkdir -p "/tmp/adbf/audible/George Orwell/Animal Farm"
    //      mkdir -p "/tmp/adbf/audible/Philip K. Dick/Electric Dreams"
    //      mkdir -p "/tmp/adbf"
    static _TEST_DATA: [&str; 6] = [
        "/tmp/adbf/Music/audiobooks/A-D",
        "/tmp/adbf/Music/audiobooks/E-F/George Orwell/Animal Farm",
        "/tmp/adbf/audible/George Orwell/Animal Farm",
        "/tmp/adbf/audible/..",
        "/tmp/adbf/audible/Philip K. Dick/Electric Dreams",
        "/tmp/adbf",
    ];

    #[test]
    fn test_clean_paths_overlap() {
        init();

        // 1 out
        let testing_path = vec![
            "/tmp/adbf/Music/audiobooks/A-D".to_string(),
            "/tmp/adbf/Music/audiobooks/E-F/George Orwell/Animal Farm".to_string(),
            "/tmp/adbf/audible/George Orwell/Animal Farm".to_string(),
            "/tmp/adbf/Music/audiobooks/E-F".to_string(),
        ];
        let return_value = clean_paths(&testing_path);
        if HAS_FS_UP {
            assert_eq!(
                return_value,
                vec![
                    "/tmp/adbf/Music/audiobooks/A-D".to_string(),
                    "/tmp/adbf/Music/audiobooks/E-F".to_string(),
                    "/tmp/adbf/audible/George Orwell/Animal Farm".to_string(),
                ]
            );
        }

        // 2 out
        let testing_path = vec![
            "/tmp/adbf/Music/audiobooks/A-D".to_string(),
            "/tmp/adbf/Music/audiobooks/E-F/George Orwell/Animal Farm".to_string(),
            "/tmp/adbf/audible/George Orwell/Animal Farm".to_string(),
            "/tmp/adbf/Music/audiobooks/E-F".to_string(),
            "/tmp/adbf/audible/George Orwell".to_string(),
        ];
        let return_value = clean_paths(&testing_path);

        if HAS_FS_UP {
            assert_eq!(
                return_value,
                vec![
                    "/tmp/adbf/Music/audiobooks/A-D".to_string(),
                    "/tmp/adbf/Music/audiobooks/E-F".to_string(),
                    "/tmp/adbf/audible/George Orwell".to_string(),
                ]
            );
        }
    }

    #[test]
    fn test_clean_paths_parent() {
        init();

        // ..
        let testing_path = vec![
            "/tmp/adbf/Music/audiobooks/A-D".to_string(),
            "/tmp/adbf/Music/audiobooks/../audiobooks/E-F/George Orwell/Animal Farm".to_string(),
            "/tmp/adbf/audible/George Orwell/Animal Farm".to_string(),
        ];
        let return_value = clean_paths(&testing_path);
        if HAS_FS_UP {
            assert_eq!(
                return_value,
                vec![
                    "/tmp/adbf/Music/audiobooks/A-D".to_string(),
                    "/tmp/adbf/Music/audiobooks/E-F/George Orwell/Animal Farm".to_string(),
                    "/tmp/adbf/audible/George Orwell/Animal Farm".to_string(),
                ]
            );
        }

        // ..
        let testing_path = vec![
            "/tmp/adbf/Music/../../adbf/Music/audiobooks/A-D".to_string(),
            "/tmp/adbf/Music/audiobooks".to_string(),
            "/tmp/adbf/Music/audiobooks/E-F/George Orwell/Animal Farm".to_string(),
        ];
        let return_value = clean_paths(&testing_path);
        if HAS_FS_UP {
            assert_eq!(
                return_value,
                vec!["/tmp/adbf/Music/audiobooks".to_string(),]
            );
        }

        // ..
        let testing_path = vec![
            "/tmp/adbf/Music/../../adbf/Music/audiobooks/A-D".to_string(),
            "/tmp/adbf/Music/audiobooks/E-F/George Orwell/Animal Farm".to_string(),
        ];
        let return_value = clean_paths(&testing_path);
        if HAS_FS_UP {
            assert_eq!(
                return_value,
                vec![
                    "/tmp/adbf/Music/audiobooks/A-D".to_string(),
                    "/tmp/adbf/Music/audiobooks/E-F/George Orwell/Animal Farm".to_string(),
                ]
            );
        }
    }
}
<commit_msg>[documentation] added a bit more inline documentation<commit_after>//! Holds common search paths implementations
use super::config::data::PATHS_MAX;
use std::path::Path;

/// This struct holds the search paths, which can change before startup but not later
pub struct SearchPath {
    paths: Vec<String>,
}

impl SearchPath {
    /// Create a controlled SearchPath from a simple vector
    pub fn new(paths: &Vec<String>) -> Self {
        // 2) clean-up and straighten
        let cleaned_paths = clean_paths(&paths);
        // 3) and then cut-off unwanted parts (more paths input than PATHS_MAX)
        let paths = cleaned_paths
            .into_iter()
            .enumerate()
            .filter(|(i, _el)| i < &PATHS_MAX)
            .map(|(_i, el)| el)
            .collect();
        Self { paths }
    }
    pub fn update(&mut self, new_vec: Vec<String>) {
        let cleaned_new = clean_paths(&new_vec);
        self.paths = cleaned_new;
    }
    pub fn read(&self) -> Vec<String> {
        self.paths.clone()
    }
    pub fn len(&self) -> usize {
        self.paths.len()
    }
}

/// Clean paths checks that paths exists and that intersection
/// paths are exluded, also down-up-climbing of existing paths
/// hierarchy are working!
///
/// E.g. given:
///     "/home/user/Music/audiobooks/E-F"
///     "/home/user/Music/audiobooks/E-F/George Orwell"
///     "/home/user/Music/audiobooks/A-D/../../audiobooks/A-D"
///     "/home/user/Music/audiobooks/A-D"
///     "/home/user/Music/audiobooks/E-F/George Orwell/Animal Farm"
///
/// will lead to:
///     "/home/user/Music/audiobooks/E-F"
///     "/home/user/Music/audiobooks/A-D"
///
///
/// todo: implement vfs then write document_test
fn clean_paths(unchecked_paths: &Vec<String>) -> Vec<String> {
    let mut checked_paths: Vec<String> = vec![];
    for unchecked in unchecked_paths {
        if let Ok(path_ok) = Path::new(unchecked).canonicalize() {
            if path_ok.is_dir() {
                if let Some(path_str) = path_ok.to_str() {
                    let mut is_add_worthy = true;
                    let path_string = path_str.to_string();
                    for checked in checked_paths.iter_mut() {
                        // check all already checked path, if there is
                        // no reason to not add it, add it.
                        let path_len = path_str.len();
                        let checked_len = checked.len();
                        if path_len < checked_len {
                            // if substring matches checked must be exchanged
                            if checked[..path_len] == path_string {
                                *checked = path_string;
                                is_add_worthy = false;
                                break;
                            }
                        } else {
                            // only add if not substring with any
                            if path_string[..checked_len] == *checked {
                                //
                                is_add_worthy = false;
                                break;
                            }
                        }
                    }
                    // add it if there is no reason not to
                    if is_add_worthy {
                        checked_paths.push(path_str.to_string());
                    }
                } else {
                    warn!(
                        "Path {:?} has some encoding problem, and will not be included in search!",
                        unchecked
                    )
                }
            } else {
                warn!("Path {:?} does not exist as directory/folder, and will not be included in search!", unchecked)
            }
        } else {
            error!(
                "Path {:?} is not a valid directory/folder, and will not be included in search!",
                unchecked
            );
        }
    }
    checked_paths
}

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

    fn init() {
        let _ = env_logger::builder().is_test(true).try_init();
    }

    // todo: use crate vfs for better unit tests
    static HAS_FS_UP: bool = false;
    // run: mkdir -p "/tmp/adbf/Music/audiobooks/A-D"
    //      mkdir -p "/tmp/adbf/Music/audiobooks/E-F/George Orwell/Animal Farm"
    //      mkdir -p "/tmp/adbf/audible/George Orwell/Animal Farm"
    //      mkdir -p "/tmp/adbf/audible/Philip K. Dick/Electric Dreams"
    //      mkdir -p "/tmp/adbf"
    static _TEST_DATA: [&str; 6] = [
        "/tmp/adbf/Music/audiobooks/A-D",
        "/tmp/adbf/Music/audiobooks/E-F/George Orwell/Animal Farm",
        "/tmp/adbf/audible/George Orwell/Animal Farm",
        "/tmp/adbf/audible/..",
        "/tmp/adbf/audible/Philip K. Dick/Electric Dreams",
        "/tmp/adbf",
    ];

    #[test]
    fn test_clean_paths_overlap() {
        init();

        // 1 out
        let testing_path = vec![
            "/tmp/adbf/Music/audiobooks/A-D".to_string(),
            "/tmp/adbf/Music/audiobooks/E-F/George Orwell/Animal Farm".to_string(),
            "/tmp/adbf/audible/George Orwell/Animal Farm".to_string(),
            "/tmp/adbf/Music/audiobooks/E-F".to_string(),
        ];
        let return_value = clean_paths(&testing_path);
        if HAS_FS_UP {
            assert_eq!(
                return_value,
                vec![
                    "/tmp/adbf/Music/audiobooks/A-D".to_string(),
                    "/tmp/adbf/Music/audiobooks/E-F".to_string(),
                    "/tmp/adbf/audible/George Orwell/Animal Farm".to_string(),
                ]
            );
        }

        // 2 out
        let testing_path = vec![
            "/tmp/adbf/Music/audiobooks/A-D".to_string(),
            "/tmp/adbf/Music/audiobooks/E-F/George Orwell/Animal Farm".to_string(),
            "/tmp/adbf/audible/George Orwell/Animal Farm".to_string(),
            "/tmp/adbf/Music/audiobooks/E-F".to_string(),
            "/tmp/adbf/audible/George Orwell".to_string(),
        ];
        let return_value = clean_paths(&testing_path);

        if HAS_FS_UP {
            assert_eq!(
                return_value,
                vec![
                    "/tmp/adbf/Music/audiobooks/A-D".to_string(),
                    "/tmp/adbf/Music/audiobooks/E-F".to_string(),
                    "/tmp/adbf/audible/George Orwell".to_string(),
                ]
            );
        }
    }

    #[test]
    fn test_clean_paths_parent() {
        init();

        // ..
        let testing_path = vec![
            "/tmp/adbf/Music/audiobooks/A-D".to_string(),
            "/tmp/adbf/Music/audiobooks/../audiobooks/E-F/George Orwell/Animal Farm".to_string(),
            "/tmp/adbf/audible/George Orwell/Animal Farm".to_string(),
        ];
        let return_value = clean_paths(&testing_path);
        if HAS_FS_UP {
            assert_eq!(
                return_value,
                vec![
                    "/tmp/adbf/Music/audiobooks/A-D".to_string(),
                    "/tmp/adbf/Music/audiobooks/E-F/George Orwell/Animal Farm".to_string(),
                    "/tmp/adbf/audible/George Orwell/Animal Farm".to_string(),
                ]
            );
        }

        // ..
        let testing_path = vec![
            "/tmp/adbf/Music/../../adbf/Music/audiobooks/A-D".to_string(),
            "/tmp/adbf/Music/audiobooks".to_string(),
            "/tmp/adbf/Music/audiobooks/E-F/George Orwell/Animal Farm".to_string(),
        ];
        let return_value = clean_paths(&testing_path);
        if HAS_FS_UP {
            assert_eq!(
                return_value,
                vec!["/tmp/adbf/Music/audiobooks".to_string(),]
            );
        }

        // ..
        let testing_path = vec![
            "/tmp/adbf/Music/../../adbf/Music/audiobooks/A-D".to_string(),
            "/tmp/adbf/Music/audiobooks/E-F/George Orwell/Animal Farm".to_string(),
        ];
        let return_value = clean_paths(&testing_path);
        if HAS_FS_UP {
            assert_eq!(
                return_value,
                vec![
                    "/tmp/adbf/Music/audiobooks/A-D".to_string(),
                    "/tmp/adbf/Music/audiobooks/E-F/George Orwell/Animal Farm".to_string(),
                ]
            );
        }
    }
}
<|endoftext|>


<commit_before>use platform::process::get_pid;
use platform::time::time_now;
use storage::Cache;
use storage::Key;
use storage::Value;
use tcp_transport::stats::TransportStats;

use super::cmd::Cmd;
use super::cmd::Delete;
use super::cmd::Get;
use super::cmd::Resp;
use super::cmd::Set;
use super::cmd::Stat;
use super::cmd::Value as CmdValue;

struct DriverStats {
    cmd_get: u64,
    cmd_set: u64,
    cmd_flush: u64,
    cmd_touch: u64,
}

impl DriverStats {
    pub fn new() -> DriverStats {
        DriverStats {
            cmd_get: 0,
            cmd_set: 0,
            cmd_flush: 0,
            cmd_touch: 0,
        }
    }
}

pub struct Driver {
    cache: Cache,
    time_start: f64,

    stats: DriverStats,
    transport_stats: TransportStats, // this is a global snapshot
}

impl Driver {
    pub fn new(cache: Cache) -> Driver {
        Driver {
            cache: cache,
            stats: DriverStats::new(),
            time_start: time_now(),
            transport_stats: TransportStats::new(),
        }
    }

    fn set_exptime(&self, value: &mut Value, exptime: u32) {
        // If exptime is greater than zero we need to set it on the value
        if exptime > 0 {
            let tm;

            // Is it an interval greater than 30 days? Then it's a timestamp
            if exptime > 60 * 60 * 24 * 30 {
                tm = exptime as f64;

            } else {
                // Otherwise it's relative from now
                tm = time_now() + exptime as f64;
            }

            value.set_exptime(tm);
        }
    }

    fn do_delete(&mut self, delete: Delete) -> Resp {
        let key = Key::new(delete.key.clone().into_bytes());

        let rv = self.cache.remove(&key);

        match delete.noreply {
            true => Resp::Empty,
            false => {
                match rv {
                    Ok(_) => Resp::Deleted,
                    Err(_) => Resp::NotFound,
                }
            }
        }
    }

    fn do_get(&mut self, get: Get) -> Resp {
        // Update stats
        self.stats.cmd_get += 1;

        let mut values = vec![];

        for key in get.keys {
            let key_str = key.clone();

            let key_st = Key::new(key.into_bytes());
            let rv = self.cache.get(&key_st);

            match rv {
                Ok(value) => {
                    let val_st = CmdValue {
                        key: key_str,
                        flags: value.flags,
                        data: value.item.clone(),
                    };
                    values.push(val_st);
                }
                // Keys that were not found are skipped, no error given
                Err(_) => (),
            }
        }

        Resp::Values(values)
    }

    fn do_set(&mut self, set: Set) -> Resp {
        // Update stats
        self.stats.cmd_set += 1;

        let key = Key::new(set.key.into_bytes());
        let mut value = Value::new(set.data);
        value.with_flags(set.flags);
        self.set_exptime(&mut value, set.exptime);

        let rv = self.cache.set(key, value);

        match rv {
            Ok(_) => {
                match set.noreply {
                    true => Resp::Empty,
                    false => Resp::Stored,
                }
            }
            Err(_) => Resp::Error,
        }
    }

    fn do_stats(&self) -> Resp {
        let storage = self.cache.get_stats();

        let pid = get_pid().to_string();
        let uptime = ((time_now() - self.time_start) as u64).to_string();
        let time = (time_now() as u64).to_string();
        let cmd_get = self.stats.cmd_get.to_string();
        let cmd_set = self.stats.cmd_set.to_string();
        let cmd_flush = self.stats.cmd_flush.to_string();
        let cmd_touch = self.stats.cmd_touch.to_string();
        let get_hits = storage.get_hits.to_string();
        let get_misses = storage.get_misses.to_string();
        let delete_hits = storage.delete_hits.to_string();
        let delete_misses = storage.delete_misses.to_string();
        let bytes_read = self.transport_stats.bytes_read.to_string();
        let bytes_written = self.transport_stats.bytes_written.to_string();
        let limit_maxbytes = self.cache.capacity.to_string();
        let bytes = storage.bytes.to_string();
        let curr_items = self.cache.len().to_string();
        let total_items = storage.total_items.to_string();
        let evictions = storage.evictions.to_string();
        let reclaimed = storage.reclaimed.to_string();

        let st_pid = Stat::new("pid", pid);
        let st_uptime = Stat::new("uptime", uptime);
        let st_time = Stat::new("time", time);
        let st_cmd_get = Stat::new("cmd_get", cmd_get);
        let st_cmd_set = Stat::new("cmd_set", cmd_set);
        let st_cmd_flush = Stat::new("cmd_flush", cmd_flush);
        let st_cmd_touch = Stat::new("cmd_touch", cmd_touch);
        let st_get_hits = Stat::new("get_hits", get_hits);
        let st_get_misses = Stat::new("get_misses", get_misses);
        let st_delete_hits = Stat::new("delete_hits", delete_hits);
        let st_delete_misses = Stat::new("delete_misses", delete_misses);
        let st_bytes_read = Stat::new("bytes_read", bytes_read);
        let st_bytes_written = Stat::new("bytes_written", bytes_written);
        let st_limit_maxbytes = Stat::new("limit_maxbytes", limit_maxbytes);
        let st_bytes = Stat::new("bytes", bytes);
        let st_curr_items = Stat::new("curr_items", curr_items);
        let st_total_items = Stat::new("total_items", total_items);
        let st_evictions = Stat::new("evictions", evictions);
        let st_reclaimed = Stat::new("reclaimed", reclaimed);

        Resp::Stats(vec![st_pid,
                         st_uptime,
                         st_time,
                         st_cmd_get,
                         st_cmd_set,
                         st_cmd_flush,
                         st_cmd_touch,
                         st_get_hits,
                         st_get_misses,
                         st_delete_hits,
                         st_delete_misses,
                         st_bytes_read,
                         st_bytes_written,
                         st_limit_maxbytes,
                         st_bytes,
                         st_curr_items,
                         st_total_items,
                         st_evictions,
                         st_reclaimed])
    }

    pub fn run(&mut self, cmd: Cmd) -> Resp {
        match cmd {
            Cmd::Delete(del) => self.do_delete(del),
            Cmd::Get(get) => self.do_get(get),
            Cmd::Set(set) => self.do_set(set),
            Cmd::Stats => self.do_stats(),
        }
    }

    pub fn update_transport_stats(&mut self, stats: TransportStats) {
        self.transport_stats = stats;
    }
}
<commit_msg>fix set cmd to make sure noreply takes precedence over return value<commit_after>use platform::process::get_pid;
use platform::time::time_now;
use storage::Cache;
use storage::Key;
use storage::Value;
use tcp_transport::stats::TransportStats;

use super::cmd::Cmd;
use super::cmd::Delete;
use super::cmd::Get;
use super::cmd::Resp;
use super::cmd::Set;
use super::cmd::Stat;
use super::cmd::Value as CmdValue;

struct DriverStats {
    cmd_get: u64,
    cmd_set: u64,
    cmd_flush: u64,
    cmd_touch: u64,
}

impl DriverStats {
    pub fn new() -> DriverStats {
        DriverStats {
            cmd_get: 0,
            cmd_set: 0,
            cmd_flush: 0,
            cmd_touch: 0,
        }
    }
}

pub struct Driver {
    cache: Cache,
    time_start: f64,

    stats: DriverStats,
    transport_stats: TransportStats, // this is a global snapshot
}

impl Driver {
    pub fn new(cache: Cache) -> Driver {
        Driver {
            cache: cache,
            stats: DriverStats::new(),
            time_start: time_now(),
            transport_stats: TransportStats::new(),
        }
    }

    fn set_exptime(&self, value: &mut Value, exptime: u32) {
        // If exptime is greater than zero we need to set it on the value
        if exptime > 0 {
            let tm;

            // Is it an interval greater than 30 days? Then it's a timestamp
            if exptime > 60 * 60 * 24 * 30 {
                tm = exptime as f64;

            } else {
                // Otherwise it's relative from now
                tm = time_now() + exptime as f64;
            }

            value.set_exptime(tm);
        }
    }

    fn do_delete(&mut self, delete: Delete) -> Resp {
        let key = Key::new(delete.key.clone().into_bytes());

        let rv = self.cache.remove(&key);

        match delete.noreply {
            true => Resp::Empty,
            false => match rv {
                Ok(_) => Resp::Deleted,
                Err(_) => Resp::NotFound,
            }
        }
    }

    fn do_get(&mut self, get: Get) -> Resp {
        // Update stats
        self.stats.cmd_get += 1;

        let mut values = vec![];

        for key in get.keys {
            let key_str = key.clone();

            let key_st = Key::new(key.into_bytes());
            let rv = self.cache.get(&key_st);

            match rv {
                Ok(value) => {
                    let val_st = CmdValue {
                        key: key_str,
                        flags: value.flags,
                        data: value.item.clone(),
                    };
                    values.push(val_st);
                }
                // Keys that were not found are skipped, no error given
                Err(_) => (),
            }
        }

        Resp::Values(values)
    }

    fn do_set(&mut self, set: Set) -> Resp {
        // Update stats
        self.stats.cmd_set += 1;

        let key = Key::new(set.key.into_bytes());
        let mut value = Value::new(set.data);
        value.with_flags(set.flags);
        self.set_exptime(&mut value, set.exptime);

        let rv = self.cache.set(key, value);

        match set.noreply {
            true => Resp::Empty,
            false => match rv {
                Ok(_) => Resp::Stored,
                Err(_) => Resp::Error,
            }
        }
    }

    fn do_stats(&self) -> Resp {
        let storage = self.cache.get_stats();

        let pid = get_pid().to_string();
        let uptime = ((time_now() - self.time_start) as u64).to_string();
        let time = (time_now() as u64).to_string();
        let cmd_get = self.stats.cmd_get.to_string();
        let cmd_set = self.stats.cmd_set.to_string();
        let cmd_flush = self.stats.cmd_flush.to_string();
        let cmd_touch = self.stats.cmd_touch.to_string();
        let get_hits = storage.get_hits.to_string();
        let get_misses = storage.get_misses.to_string();
        let delete_hits = storage.delete_hits.to_string();
        let delete_misses = storage.delete_misses.to_string();
        let bytes_read = self.transport_stats.bytes_read.to_string();
        let bytes_written = self.transport_stats.bytes_written.to_string();
        let limit_maxbytes = self.cache.capacity.to_string();
        let bytes = storage.bytes.to_string();
        let curr_items = self.cache.len().to_string();
        let total_items = storage.total_items.to_string();
        let evictions = storage.evictions.to_string();
        let reclaimed = storage.reclaimed.to_string();

        let st_pid = Stat::new("pid", pid);
        let st_uptime = Stat::new("uptime", uptime);
        let st_time = Stat::new("time", time);
        let st_cmd_get = Stat::new("cmd_get", cmd_get);
        let st_cmd_set = Stat::new("cmd_set", cmd_set);
        let st_cmd_flush = Stat::new("cmd_flush", cmd_flush);
        let st_cmd_touch = Stat::new("cmd_touch", cmd_touch);
        let st_get_hits = Stat::new("get_hits", get_hits);
        let st_get_misses = Stat::new("get_misses", get_misses);
        let st_delete_hits = Stat::new("delete_hits", delete_hits);
        let st_delete_misses = Stat::new("delete_misses", delete_misses);
        let st_bytes_read = Stat::new("bytes_read", bytes_read);
        let st_bytes_written = Stat::new("bytes_written", bytes_written);
        let st_limit_maxbytes = Stat::new("limit_maxbytes", limit_maxbytes);
        let st_bytes = Stat::new("bytes", bytes);
        let st_curr_items = Stat::new("curr_items", curr_items);
        let st_total_items = Stat::new("total_items", total_items);
        let st_evictions = Stat::new("evictions", evictions);
        let st_reclaimed = Stat::new("reclaimed", reclaimed);

        Resp::Stats(vec![st_pid,
                         st_uptime,
                         st_time,
                         st_cmd_get,
                         st_cmd_set,
                         st_cmd_flush,
                         st_cmd_touch,
                         st_get_hits,
                         st_get_misses,
                         st_delete_hits,
                         st_delete_misses,
                         st_bytes_read,
                         st_bytes_written,
                         st_limit_maxbytes,
                         st_bytes,
                         st_curr_items,
                         st_total_items,
                         st_evictions,
                         st_reclaimed])
    }

    pub fn run(&mut self, cmd: Cmd) -> Resp {
        match cmd {
            Cmd::Delete(del) => self.do_delete(del),
            Cmd::Get(get) => self.do_get(get),
            Cmd::Set(set) => self.do_set(set),
            Cmd::Stats => self.do_stats(),
        }
    }

    pub fn update_transport_stats(&mut self, stats: TransportStats) {
        self.transport_stats = stats;
    }
}
<|endoftext|>


<commit_before>use num::Integer;
use std::iter::FromIterator;
use std::marker::PhantomData;
use std::mem;

use generate::decompose::IntoVertices;

pub trait Topological: Sized {
    type Vertex: Clone;
}

pub trait Polygonal: Topological {}

pub trait Arity {
    const ARITY: usize;
}

pub trait MapVerticesInto<T, U>: Topological<Vertex = T>
where
    T: Clone,
    U: Clone,
{
    type Output: Topological<Vertex = U>;

    fn map_vertices_into<F>(self, f: F) -> Self::Output
    where
        F: FnMut(T) -> U;
}

pub trait MapVertices<T, U>: Sized
where
    T: Clone,
    U: Clone,
{
    fn map_vertices<F>(self, f: F) -> Map<Self, T, U, F>
    where
        F: FnMut(T) -> U;
}

/// Zips the vertices of topologies into a single topology.
///
/// For example, given three `Triangle`s with vertex types of `A`, `B`, and
/// `C`, the result of the zip would be a single `Triangle` with a vertex type
/// of `(A, B, C)`. This is useful for unifying topology streams of different
/// attributes like position, index, and normal into a single stream.
///
/// This trait is implemented for tuples of topological types (`Triangle`,
/// `Polygon`, etc.).
///
/// # Examples
///
/// Zip the vertices of three `Triangle`s:
///
/// ```rust
/// # extern crate nalgebra;
/// # extern crate num;
/// # extern crate plexus;
/// use nalgebra::{Point3, Vector3};
/// use num::Zero;
/// use plexus::generate::Triangle;
/// use plexus::prelude::*;
///
/// # fn main() {
/// let t1 = Triangle::converged(Point3::<f32>::origin());
/// let t2 = Triangle::converged(Vector3::<f32>::zero());
/// let t3 = Triangle::converged(0u32);
/// let tz = (t1, t2, t3).zip_vertices_into();
/// let (point, vector, scalar) = tz.a;
/// # }
/// ```
///
/// Create a topological stream of position and texture coordinate data for a
/// cube using `izip` from the [itertools](https://crates.io/crates/itertools)
/// crate:
///
/// ```rust
/// # extern crate decorum;
/// # #[macro_use]
/// # extern crate itertools;
/// # extern crate num;
/// # extern crate plexus;
/// use plexus::generate::cube::Cube;
/// use plexus::prelude::*;
///
/// # use decorum::R32;
/// # use num::One;
/// # fn map_to_color(texture: &Duplet<R32>) -> Triplet<R32> {
/// #     Triplet(One::one(), One::one(), One::one())
/// # }
///
/// # fn main() {
/// let cube = Cube::new();
/// let polygons = izip!(
///     cube.polygons_with_position(),
///     cube.polygons_with_texture()
/// ).map(|polygons| polygons.zip_vertices_into())
///     .map_vertices(|(position, texture)| (position, texture, map_to_color(&texture)))
///     .triangulate()
///     .collect::<Vec<_>>();
/// # }
/// ```
pub trait ZipVerticesInto {
    type Output: FromIterator<<Self::Output as Topological>::Vertex> + Topological;

    fn zip_vertices_into(self) -> Self::Output;
}

// TODO: Using `FromIterator` to implement this is fragile. This is especially
//       true for `Polygon`, because the arity of the polygons that are zipped
//       may not be the same. This could cause panics or unexpected behavior.
//       It may be a better idea to hide `ZipVerticesInto` behind a more
//       restricted interface.
macro_rules! zip_vertices_into {
    (topology => $t:ident, geometries => ($($g:ident),*)) => (
        #[allow(non_snake_case)]
        impl<$($g: Clone),*> ZipVerticesInto for ($($t<$g>),*) {
            type Output = $t<($($g),*)>;

            fn zip_vertices_into(self) -> Self::Output {
                let ($($g,)*) = self;
                izip!($($g.into_vertices()),*).collect()
            }
        }
    );
}

pub trait Rotate {
    fn rotate(&mut self, n: isize);
}

impl<I, T, U, P, Q> MapVertices<T, U> for I
where
    I: Iterator<Item = P>,
    P: MapVerticesInto<T, U, Output = Q>,
    Q: Topological<Vertex = U>,
    T: Clone,
    U: Clone,
{
    fn map_vertices<F>(self, f: F) -> Map<Self, T, U, F>
    where
        F: FnMut(T) -> U,
    {
        Map::new(self, f)
    }
}

pub struct Map<I, T, U, F> {
    input: I,
    f: F,
    phantom: PhantomData<(T, U)>,
}

impl<I, T, U, F> Map<I, T, U, F> {
    fn new(input: I, f: F) -> Self {
        Map {
            input: input,
            f: f,
            phantom: PhantomData,
        }
    }
}

impl<I, T, U, F, P, Q> Iterator for Map<I, T, U, F>
where
    I: Iterator<Item = P>,
    F: FnMut(T) -> U,
    P: MapVerticesInto<T, U, Output = Q>,
    Q: Topological<Vertex = U>,
    T: Clone,
    U: Clone,
{
    type Item = Q;

    fn next(&mut self) -> Option<Self::Item> {
        self.input
            .next()
            .map(|topology| topology.map_vertices_into(&mut self.f))
    }
}

pub struct Line<T> {
    pub a: T,
    pub b: T,
}

impl<T> Line<T> {
    pub fn new(a: T, b: T) -> Self {
        Line { a, b }
    }

    pub fn converged(value: T) -> Self
    where
        T: Clone,
    {
        Line::new(value.clone(), value)
    }
}

impl<T> Arity for Line<T> {
    const ARITY: usize = 2;
}

impl<T> FromIterator<T> for Line<T> {
    fn from_iter<I>(input: I) -> Self
    where
        I: IntoIterator<Item = T>,
    {
        let mut input = input.into_iter();
        Line::new(input.next().unwrap(), input.next().unwrap())
    }
}
zip_vertices_into!(topology => Line, geometries => (A, B));
zip_vertices_into!(topology => Line, geometries => (A, B, C));
zip_vertices_into!(topology => Line, geometries => (A, B, C, D));

impl<T, U> MapVerticesInto<T, U> for Line<T>
where
    T: Clone,
    U: Clone,
{
    type Output = Line<U>;

    fn map_vertices_into<F>(self, mut f: F) -> Self::Output
    where
        F: FnMut(T) -> U,
    {
        let Line { a, b } = self;
        Line::new(f(a), f(b))
    }
}

impl<T> Topological for Line<T>
where
    T: Clone,
{
    type Vertex = T;
}

impl<T> Rotate for Line<T>
where
    T: Clone,
{
    fn rotate(&mut self, n: isize) {
        if n % 2 != 0 {
            mem::swap(&mut self.a, &mut self.b);
        }
    }
}

pub struct Triangle<T> {
    pub a: T,
    pub b: T,
    pub c: T,
}

impl<T> Triangle<T> {
    pub fn new(a: T, b: T, c: T) -> Self {
        Triangle { a, b, c }
    }

    pub fn converged(value: T) -> Self
    where
        T: Clone,
    {
        Triangle::new(value.clone(), value.clone(), value)
    }
}

impl<T> Arity for Triangle<T> {
    const ARITY: usize = 3;
}

impl<T> FromIterator<T> for Triangle<T> {
    fn from_iter<I>(input: I) -> Self
    where
        I: IntoIterator<Item = T>,
    {
        let mut input = input.into_iter();
        Triangle::new(
            input.next().unwrap(),
            input.next().unwrap(),
            input.next().unwrap(),
        )
    }
}
zip_vertices_into!(topology => Triangle, geometries => (A, B));
zip_vertices_into!(topology => Triangle, geometries => (A, B, C));
zip_vertices_into!(topology => Triangle, geometries => (A, B, C, D));

impl<T, U> MapVerticesInto<T, U> for Triangle<T>
where
    T: Clone,
    U: Clone,
{
    type Output = Triangle<U>;

    fn map_vertices_into<F>(self, mut f: F) -> Self::Output
    where
        F: FnMut(T) -> U,
    {
        let Triangle { a, b, c } = self;
        Triangle::new(f(a), f(b), f(c))
    }
}

impl<T> Topological for Triangle<T>
where
    T: Clone,
{
    type Vertex = T;
}

impl<T> Polygonal for Triangle<T>
where
    T: Clone,
{
}

impl<T> Rotate for Triangle<T>
where
    T: Clone,
{
    fn rotate(&mut self, n: isize) {
        let n = umod(n, 3);
        if n == 1 {
            mem::swap(&mut self.a, &mut self.b);
            mem::swap(&mut self.b, &mut self.c);
        }
        else if n == 2 {
            mem::swap(&mut self.c, &mut self.b);
            mem::swap(&mut self.b, &mut self.a);
        }
    }
}

pub struct Quad<T> {
    pub a: T,
    pub b: T,
    pub c: T,
    pub d: T,
}

impl<T> Quad<T> {
    pub fn new(a: T, b: T, c: T, d: T) -> Self {
        Quad { a, b, c, d }
    }

    pub fn converged(value: T) -> Self
    where
        T: Clone,
    {
        Quad::new(value.clone(), value.clone(), value.clone(), value)
    }
}

impl<T> Arity for Quad<T> {
    const ARITY: usize = 4;
}

impl<T> FromIterator<T> for Quad<T> {
    fn from_iter<I>(input: I) -> Self
    where
        I: IntoIterator<Item = T>,
    {
        let mut input = input.into_iter();
        Quad::new(
            input.next().unwrap(),
            input.next().unwrap(),
            input.next().unwrap(),
            input.next().unwrap(),
        )
    }
}
zip_vertices_into!(topology => Quad, geometries => (A, B));
zip_vertices_into!(topology => Quad, geometries => (A, B, C));
zip_vertices_into!(topology => Quad, geometries => (A, B, C, D));

impl<T, U> MapVerticesInto<T, U> for Quad<T>
where
    T: Clone,
    U: Clone,
{
    type Output = Quad<U>;

    fn map_vertices_into<F>(self, mut f: F) -> Self::Output
    where
        F: FnMut(T) -> U,
    {
        let Quad { a, b, c, d } = self;
        Quad::new(f(a), f(b), f(c), f(d))
    }
}

impl<T> Topological for Quad<T>
where
    T: Clone,
{
    type Vertex = T;
}

impl<T> Polygonal for Quad<T>
where
    T: Clone,
{
}

impl<T> Rotate for Quad<T>
where
    T: Clone,
{
    fn rotate(&mut self, n: isize) {
        let n = umod(n, 4);
        if n == 1 {
            mem::swap(&mut self.a, &mut self.b);
            mem::swap(&mut self.b, &mut self.c);
            mem::swap(&mut self.c, &mut self.d);
        }
        else if n == 2 {
            mem::swap(&mut self.a, &mut self.c);
            mem::swap(&mut self.b, &mut self.d);
        }
        else if n == 3 {
            mem::swap(&mut self.d, &mut self.c);
            mem::swap(&mut self.c, &mut self.b);
            mem::swap(&mut self.b, &mut self.a);
        }
    }
}

pub enum Polygon<T> {
    Triangle(Triangle<T>),
    Quad(Quad<T>),
}

impl<T> From<Triangle<T>> for Polygon<T> {
    fn from(triangle: Triangle<T>) -> Self {
        Polygon::Triangle(triangle)
    }
}

impl<T> From<Quad<T>> for Polygon<T> {
    fn from(quad: Quad<T>) -> Self {
        Polygon::Quad(quad)
    }
}

impl<T> FromIterator<T> for Polygon<T> {
    fn from_iter<I>(input: I) -> Self
    where
        I: IntoIterator<Item = T>,
    {
        let input = input.into_iter().collect::<Vec<_>>();
        match input.len() {
            n if n >= 4 => Polygon::Quad(Quad::from_iter(input)),
            _ => Polygon::Triangle(Triangle::from_iter(input)),
        }
    }
}
zip_vertices_into!(topology => Polygon, geometries => (A, B));
zip_vertices_into!(topology => Polygon, geometries => (A, B, C));
zip_vertices_into!(topology => Polygon, geometries => (A, B, C, D));

impl<T, U> MapVerticesInto<T, U> for Polygon<T>
where
    T: Clone,
    U: Clone,
{
    type Output = Polygon<U>;

    fn map_vertices_into<F>(self, f: F) -> Self::Output
    where
        F: FnMut(T) -> U,
    {
        match self {
            Polygon::Triangle(triangle) => Polygon::Triangle(triangle.map_vertices_into(f)),
            Polygon::Quad(quad) => Polygon::Quad(quad.map_vertices_into(f)),
        }
    }
}

impl<T> Topological for Polygon<T>
where
    T: Clone,
{
    type Vertex = T;
}

impl<T> Polygonal for Polygon<T>
where
    T: Clone,
{
}

impl<T> Rotate for Polygon<T>
where
    T: Clone,
{
    fn rotate(&mut self, n: isize) {
        match *self {
            Polygon::Triangle(ref mut triangle) => triangle.rotate(n),
            Polygon::Quad(ref mut quad) => quad.rotate(n),
        }
    }
}

fn umod<T>(n: T, m: T) -> T
where
    T: Copy + Integer,
{
    ((n % m) + m) % m
}
<commit_msg>Use exact arity in `FromIterator` for `Polygon`.<commit_after>use arrayvec::ArrayVec;
use num::Integer;
use std::iter::FromIterator;
use std::marker::PhantomData;
use std::mem;

use generate::decompose::IntoVertices;

pub trait Topological: Sized {
    type Vertex: Clone;
}

pub trait Polygonal: Topological {}

pub trait Arity {
    const ARITY: usize;
}

pub trait MapVerticesInto<T, U>: Topological<Vertex = T>
where
    T: Clone,
    U: Clone,
{
    type Output: Topological<Vertex = U>;

    fn map_vertices_into<F>(self, f: F) -> Self::Output
    where
        F: FnMut(T) -> U;
}

pub trait MapVertices<T, U>: Sized
where
    T: Clone,
    U: Clone,
{
    fn map_vertices<F>(self, f: F) -> Map<Self, T, U, F>
    where
        F: FnMut(T) -> U;
}

/// Zips the vertices of topologies into a single topology.
///
/// For example, given three `Triangle`s with vertex types of `A`, `B`, and
/// `C`, the result of the zip would be a single `Triangle` with a vertex type
/// of `(A, B, C)`. This is useful for unifying topology streams of different
/// attributes like position, index, and normal into a single stream.
///
/// This trait is implemented for tuples of topological types (`Triangle`,
/// `Polygon`, etc.).
///
/// # Examples
///
/// Zip the vertices of three `Triangle`s:
///
/// ```rust
/// # extern crate nalgebra;
/// # extern crate num;
/// # extern crate plexus;
/// use nalgebra::{Point3, Vector3};
/// use num::Zero;
/// use plexus::generate::Triangle;
/// use plexus::prelude::*;
///
/// # fn main() {
/// let t1 = Triangle::converged(Point3::<f32>::origin());
/// let t2 = Triangle::converged(Vector3::<f32>::zero());
/// let t3 = Triangle::converged(0u32);
/// let tz = (t1, t2, t3).zip_vertices_into();
/// let (point, vector, scalar) = tz.a;
/// # }
/// ```
///
/// Create a topological stream of position and texture coordinate data for a
/// cube using `izip` from the [itertools](https://crates.io/crates/itertools)
/// crate:
///
/// ```rust
/// # extern crate decorum;
/// # #[macro_use]
/// # extern crate itertools;
/// # extern crate num;
/// # extern crate plexus;
/// use plexus::generate::cube::Cube;
/// use plexus::prelude::*;
///
/// # use decorum::R32;
/// # use num::One;
/// # fn map_to_color(texture: &Duplet<R32>) -> Triplet<R32> {
/// #     Triplet(One::one(), One::one(), One::one())
/// # }
///
/// # fn main() {
/// let cube = Cube::new();
/// let polygons = izip!(
///     cube.polygons_with_position(),
///     cube.polygons_with_texture()
/// ).map(|polygons| polygons.zip_vertices_into())
///     .map_vertices(|(position, texture)| (position, texture, map_to_color(&texture)))
///     .triangulate()
///     .collect::<Vec<_>>();
/// # }
/// ```
pub trait ZipVerticesInto {
    type Output: FromIterator<<Self::Output as Topological>::Vertex> + Topological;

    fn zip_vertices_into(self) -> Self::Output;
}

// TODO: Using `FromIterator` to implement this is fragile. This is especially
//       true for `Polygon`, because the arity of the polygons that are zipped
//       may not be the same. This could cause panics or unexpected behavior.
//       It may be a better idea to hide `ZipVerticesInto` behind a more
//       restricted interface.
macro_rules! zip_vertices_into {
    (topology => $t:ident, geometries => ($($g:ident),*)) => (
        #[allow(non_snake_case)]
        impl<$($g: Clone),*> ZipVerticesInto for ($($t<$g>),*) {
            type Output = $t<($($g),*)>;

            fn zip_vertices_into(self) -> Self::Output {
                let ($($g,)*) = self;
                izip!($($g.into_vertices()),*).collect()
            }
        }
    );
}

pub trait Rotate {
    fn rotate(&mut self, n: isize);
}

impl<I, T, U, P, Q> MapVertices<T, U> for I
where
    I: Iterator<Item = P>,
    P: MapVerticesInto<T, U, Output = Q>,
    Q: Topological<Vertex = U>,
    T: Clone,
    U: Clone,
{
    fn map_vertices<F>(self, f: F) -> Map<Self, T, U, F>
    where
        F: FnMut(T) -> U,
    {
        Map::new(self, f)
    }
}

pub struct Map<I, T, U, F> {
    input: I,
    f: F,
    phantom: PhantomData<(T, U)>,
}

impl<I, T, U, F> Map<I, T, U, F> {
    fn new(input: I, f: F) -> Self {
        Map {
            input: input,
            f: f,
            phantom: PhantomData,
        }
    }
}

impl<I, T, U, F, P, Q> Iterator for Map<I, T, U, F>
where
    I: Iterator<Item = P>,
    F: FnMut(T) -> U,
    P: MapVerticesInto<T, U, Output = Q>,
    Q: Topological<Vertex = U>,
    T: Clone,
    U: Clone,
{
    type Item = Q;

    fn next(&mut self) -> Option<Self::Item> {
        self.input
            .next()
            .map(|topology| topology.map_vertices_into(&mut self.f))
    }
}

pub struct Line<T> {
    pub a: T,
    pub b: T,
}

impl<T> Line<T> {
    pub fn new(a: T, b: T) -> Self {
        Line { a, b }
    }

    pub fn converged(value: T) -> Self
    where
        T: Clone,
    {
        Line::new(value.clone(), value)
    }
}

impl<T> Arity for Line<T> {
    const ARITY: usize = 2;
}

impl<T> FromIterator<T> for Line<T> {
    fn from_iter<I>(input: I) -> Self
    where
        I: IntoIterator<Item = T>,
    {
        let mut input = input.into_iter();
        Line::new(input.next().unwrap(), input.next().unwrap())
    }
}
zip_vertices_into!(topology => Line, geometries => (A, B));
zip_vertices_into!(topology => Line, geometries => (A, B, C));
zip_vertices_into!(topology => Line, geometries => (A, B, C, D));

impl<T, U> MapVerticesInto<T, U> for Line<T>
where
    T: Clone,
    U: Clone,
{
    type Output = Line<U>;

    fn map_vertices_into<F>(self, mut f: F) -> Self::Output
    where
        F: FnMut(T) -> U,
    {
        let Line { a, b } = self;
        Line::new(f(a), f(b))
    }
}

impl<T> Topological for Line<T>
where
    T: Clone,
{
    type Vertex = T;
}

impl<T> Rotate for Line<T>
where
    T: Clone,
{
    fn rotate(&mut self, n: isize) {
        if n % 2 != 0 {
            mem::swap(&mut self.a, &mut self.b);
        }
    }
}

pub struct Triangle<T> {
    pub a: T,
    pub b: T,
    pub c: T,
}

impl<T> Triangle<T> {
    pub fn new(a: T, b: T, c: T) -> Self {
        Triangle { a, b, c }
    }

    pub fn converged(value: T) -> Self
    where
        T: Clone,
    {
        Triangle::new(value.clone(), value.clone(), value)
    }
}

impl<T> Arity for Triangle<T> {
    const ARITY: usize = 3;
}

impl<T> FromIterator<T> for Triangle<T> {
    fn from_iter<I>(input: I) -> Self
    where
        I: IntoIterator<Item = T>,
    {
        let mut input = input.into_iter();
        Triangle::new(
            input.next().unwrap(),
            input.next().unwrap(),
            input.next().unwrap(),
        )
    }
}
zip_vertices_into!(topology => Triangle, geometries => (A, B));
zip_vertices_into!(topology => Triangle, geometries => (A, B, C));
zip_vertices_into!(topology => Triangle, geometries => (A, B, C, D));

impl<T, U> MapVerticesInto<T, U> for Triangle<T>
where
    T: Clone,
    U: Clone,
{
    type Output = Triangle<U>;

    fn map_vertices_into<F>(self, mut f: F) -> Self::Output
    where
        F: FnMut(T) -> U,
    {
        let Triangle { a, b, c } = self;
        Triangle::new(f(a), f(b), f(c))
    }
}

impl<T> Topological for Triangle<T>
where
    T: Clone,
{
    type Vertex = T;
}

impl<T> Polygonal for Triangle<T>
where
    T: Clone,
{
}

impl<T> Rotate for Triangle<T>
where
    T: Clone,
{
    fn rotate(&mut self, n: isize) {
        let n = umod(n, Self::ARITY as isize);
        if n == 1 {
            mem::swap(&mut self.a, &mut self.b);
            mem::swap(&mut self.b, &mut self.c);
        }
        else if n == 2 {
            mem::swap(&mut self.c, &mut self.b);
            mem::swap(&mut self.b, &mut self.a);
        }
    }
}

pub struct Quad<T> {
    pub a: T,
    pub b: T,
    pub c: T,
    pub d: T,
}

impl<T> Quad<T> {
    pub fn new(a: T, b: T, c: T, d: T) -> Self {
        Quad { a, b, c, d }
    }

    pub fn converged(value: T) -> Self
    where
        T: Clone,
    {
        Quad::new(value.clone(), value.clone(), value.clone(), value)
    }
}

impl<T> Arity for Quad<T> {
    const ARITY: usize = 4;
}

impl<T> FromIterator<T> for Quad<T> {
    fn from_iter<I>(input: I) -> Self
    where
        I: IntoIterator<Item = T>,
    {
        let mut input = input.into_iter();
        Quad::new(
            input.next().unwrap(),
            input.next().unwrap(),
            input.next().unwrap(),
            input.next().unwrap(),
        )
    }
}
zip_vertices_into!(topology => Quad, geometries => (A, B));
zip_vertices_into!(topology => Quad, geometries => (A, B, C));
zip_vertices_into!(topology => Quad, geometries => (A, B, C, D));

impl<T, U> MapVerticesInto<T, U> for Quad<T>
where
    T: Clone,
    U: Clone,
{
    type Output = Quad<U>;

    fn map_vertices_into<F>(self, mut f: F) -> Self::Output
    where
        F: FnMut(T) -> U,
    {
        let Quad { a, b, c, d } = self;
        Quad::new(f(a), f(b), f(c), f(d))
    }
}

impl<T> Topological for Quad<T>
where
    T: Clone,
{
    type Vertex = T;
}

impl<T> Polygonal for Quad<T>
where
    T: Clone,
{
}

impl<T> Rotate for Quad<T>
where
    T: Clone,
{
    fn rotate(&mut self, n: isize) {
        let n = umod(n, Self::ARITY as isize);
        if n == 1 {
            mem::swap(&mut self.a, &mut self.b);
            mem::swap(&mut self.b, &mut self.c);
            mem::swap(&mut self.c, &mut self.d);
        }
        else if n == 2 {
            mem::swap(&mut self.a, &mut self.c);
            mem::swap(&mut self.b, &mut self.d);
        }
        else if n == 3 {
            mem::swap(&mut self.d, &mut self.c);
            mem::swap(&mut self.c, &mut self.b);
            mem::swap(&mut self.b, &mut self.a);
        }
    }
}

pub enum Polygon<T> {
    Triangle(Triangle<T>),
    Quad(Quad<T>),
}

impl<T> From<Triangle<T>> for Polygon<T> {
    fn from(triangle: Triangle<T>) -> Self {
        Polygon::Triangle(triangle)
    }
}

impl<T> From<Quad<T>> for Polygon<T> {
    fn from(quad: Quad<T>) -> Self {
        Polygon::Quad(quad)
    }
}

impl<T> FromIterator<T> for Polygon<T> {
    fn from_iter<I>(input: I) -> Self
    where
        I: IntoIterator<Item = T>,
    {
        // Associated constants cannot be used in constant expressions, so the
        // size of the `ArrayVec` uses a literal instead of `Quad::<T>::ARITY`.
        let input = input
            .into_iter()
            .take(Quad::<T>::ARITY)
            .collect::<ArrayVec<[T; 4]>>();
        match input.len() {
            Quad::<T>::ARITY => Polygon::Quad(Quad::from_iter(input)),
            Triangle::<T>::ARITY => Polygon::Triangle(Triangle::from_iter(input)),
            _ => panic!(),
        }
    }
}
zip_vertices_into!(topology => Polygon, geometries => (A, B));
zip_vertices_into!(topology => Polygon, geometries => (A, B, C));
zip_vertices_into!(topology => Polygon, geometries => (A, B, C, D));

impl<T, U> MapVerticesInto<T, U> for Polygon<T>
where
    T: Clone,
    U: Clone,
{
    type Output = Polygon<U>;

    fn map_vertices_into<F>(self, f: F) -> Self::Output
    where
        F: FnMut(T) -> U,
    {
        match self {
            Polygon::Triangle(triangle) => Polygon::Triangle(triangle.map_vertices_into(f)),
            Polygon::Quad(quad) => Polygon::Quad(quad.map_vertices_into(f)),
        }
    }
}

impl<T> Topological for Polygon<T>
where
    T: Clone,
{
    type Vertex = T;
}

impl<T> Polygonal for Polygon<T>
where
    T: Clone,
{
}

impl<T> Rotate for Polygon<T>
where
    T: Clone,
{
    fn rotate(&mut self, n: isize) {
        match *self {
            Polygon::Triangle(ref mut triangle) => triangle.rotate(n),
            Polygon::Quad(ref mut quad) => quad.rotate(n),
        }
    }
}

fn umod<T>(n: T, m: T) -> T
where
    T: Copy + Integer,
{
    ((n % m) + m) % m
}
<|endoftext|>


<commit_before>use super::ident::Identifier;

use std::collections::{HashMap};
use vec_map::VecMap;

/// Represents a single scope, translates to source code locations.
pub type ScopeId = usize;

/// Holds an exact location within a ScopedTable.
#[derive(Clone, Debug)]
pub struct ScopePos {
    pub scope: Vec<ScopeId>,
    pub scope_lengths: Vec<usize>,
}

#[derive(Clone, Debug)]
pub struct Symbol<T> where T: Clone {
    pub scope: ScopePos,
    pub val: T,
}

pub struct ScopedTable<T> where T: Clone {
    symbols: HashMap<ScopeId, VecMap<Symbol<T>>>,
    scope: Vec<ScopeId>,
    scope_lengths: Vec<usize>,
}

impl<T> ScopedTable<T> where T: Clone {
    pub fn new() -> ScopedTable<T> {
        let mut symbols = HashMap::new();
        symbols.insert(0, VecMap::new());
        ScopedTable {
            symbols: symbols,
            scope: vec![0],
            scope_lengths: vec![0],
        }
    }

    /// Push a list of scopes which will all be popped off at once when pop() is called.
    pub fn push_scope(&mut self, scopes: &[ScopeId]) {
        self.scope.push_all(scopes);
        self.scope_lengths.push(scopes.len());
    }

    /// Push a single scope, which will be popped off when pop() is called.
    pub fn push(&mut self, scope: ScopeId) {
        self.scope.push(scope);
        self.scope_lengths.push(1);
        self.symbols.entry(scope).or_insert(VecMap::new());
    }

    /// Push a single scope onto the end of the current group.
    /// It will be popped off along with the rest of the group when pop() is called.
    pub fn push_to_scope(&mut self, scope: ScopeId) {
        self.scope.push(scope);
        *self.scope_lengths.last_mut().unwrap() += 1;
        self.symbols.entry(scope).or_insert(VecMap::new());
    }

    /// Pops the topmost group of scopes from the stack.
    pub fn pop(&mut self) {
        assert!(self.scope_lengths.len() > 1, "tried to leave the outermost scope!");
        for _ in 0..self.scope_lengths.pop().unwrap() {
            self.scope.pop().unwrap();
        }
    }

    /// Returns the current stack of scopes.
    pub fn get_scope_pos(&self) -> ScopePos {
        ScopePos {
            scope: self.scope.clone(),
            scope_lengths: self.scope_lengths.clone(),
        }
    }

    /// Sets a source identifer with the given scope identifier to a value.
    pub fn set_val(&mut self, id: Identifier, scope: ScopeId, val: T) {
        self.push_to_scope(scope);
        let scope_pos = self.get_scope_pos();
        let id_map = self.symbols.get_mut(&scope).unwrap();
        id_map.insert(id, Symbol {
                scope: scope_pos,
                val: val,
            });
    }

    /// Searches backwards through the scope stack until a symbol with the given identifier is
    /// found, and returns the symbol.
    pub fn get_symbol(&self, id: Identifier) -> Option<&Symbol<T>> {
        for block_pos in self.scope.iter().rev() {
            let id_map = self.symbols.get(&block_pos).unwrap();
            if let Some(symbol) = id_map.get(&id) {
                return Some(symbol)
            }
        }
        None
    }

    /// Searches backwards through the scope stack until a symbol with the given identifier is
    /// found, and returns the depth down the stack it was found at.
    pub fn get_symbol_depth(&self, id: Identifier) -> Option<usize> {
        return self.get_symbol_height(id).map(|x| self.scope_lengths.len() - x - 1);
    }

    /// Searches backwards through the scope stack until a symbol with the given identifier is
    /// found, and returns the height up the stack it was found at.
    pub fn get_symbol_height(&self, id: Identifier) -> Option<usize> {
        let mut len_iter = self.scope_lengths.iter().rev();
        let mut len = match len_iter.next() {
            Some(x) => *x,
            None => return None
        };
        let mut count = len_iter.len();
        for block_pos in self.scope.iter().rev() {
            len -= 1;
            if len == 0 {
                len = match len_iter.next() {
                    Some(x) => *x,
                    None => return None
                };
                count -= 1;
            }
            let id_map = self.symbols.get(&block_pos).unwrap();
            if let Some(_) = id_map.get(&id) {
                return Some(count)
            }
        }
        None
    }
}
<commit_msg>unnecessary return statement<commit_after>use super::ident::Identifier;

use std::collections::{HashMap};
use vec_map::VecMap;

/// Represents a single scope, translates to source code locations.
pub type ScopeId = usize;

/// Holds an exact location within a ScopedTable.
#[derive(Clone, Debug)]
pub struct ScopePos {
    pub scope: Vec<ScopeId>,
    pub scope_lengths: Vec<usize>,
}

#[derive(Clone, Debug)]
pub struct Symbol<T> where T: Clone {
    pub scope: ScopePos,
    pub val: T,
}

pub struct ScopedTable<T> where T: Clone {
    symbols: HashMap<ScopeId, VecMap<Symbol<T>>>,
    scope: Vec<ScopeId>,
    scope_lengths: Vec<usize>,
}

impl<T> ScopedTable<T> where T: Clone {
    pub fn new() -> ScopedTable<T> {
        let mut symbols = HashMap::new();
        symbols.insert(0, VecMap::new());
        ScopedTable {
            symbols: symbols,
            scope: vec![0],
            scope_lengths: vec![0],
        }
    }

    /// Push a list of scopes which will all be popped off at once when pop() is called.
    pub fn push_scope(&mut self, scopes: &[ScopeId]) {
        self.scope.push_all(scopes);
        self.scope_lengths.push(scopes.len());
    }

    /// Push a single scope, which will be popped off when pop() is called.
    pub fn push(&mut self, scope: ScopeId) {
        self.scope.push(scope);
        self.scope_lengths.push(1);
        self.symbols.entry(scope).or_insert(VecMap::new());
    }

    /// Push a single scope onto the end of the current group.
    /// It will be popped off along with the rest of the group when pop() is called.
    pub fn push_to_scope(&mut self, scope: ScopeId) {
        self.scope.push(scope);
        *self.scope_lengths.last_mut().unwrap() += 1;
        self.symbols.entry(scope).or_insert(VecMap::new());
    }

    /// Pops the topmost group of scopes from the stack.
    pub fn pop(&mut self) {
        assert!(self.scope_lengths.len() > 1, "tried to leave the outermost scope!");
        for _ in 0..self.scope_lengths.pop().unwrap() {
            self.scope.pop().unwrap();
        }
    }

    /// Returns the current stack of scopes.
    pub fn get_scope_pos(&self) -> ScopePos {
        ScopePos {
            scope: self.scope.clone(),
            scope_lengths: self.scope_lengths.clone(),
        }
    }

    /// Sets a source identifer with the given scope identifier to a value.
    pub fn set_val(&mut self, id: Identifier, scope: ScopeId, val: T) {
        self.push_to_scope(scope);
        let scope_pos = self.get_scope_pos();
        let id_map = self.symbols.get_mut(&scope).unwrap();
        id_map.insert(id, Symbol {
                scope: scope_pos,
                val: val,
            });
    }

    /// Searches backwards through the scope stack until a symbol with the given identifier is
    /// found, and returns the symbol.
    pub fn get_symbol(&self, id: Identifier) -> Option<&Symbol<T>> {
        for block_pos in self.scope.iter().rev() {
            let id_map = self.symbols.get(&block_pos).unwrap();
            if let Some(symbol) = id_map.get(&id) {
                return Some(symbol)
            }
        }
        None
    }

    /// Searches backwards through the scope stack until a symbol with the given identifier is
    /// found, and returns the depth down the stack it was found at.
    pub fn get_symbol_depth(&self, id: Identifier) -> Option<usize> {
        self.get_symbol_height(id).map(|x| self.scope_lengths.len() - x - 1)
    }

    /// Searches backwards through the scope stack until a symbol with the given identifier is
    /// found, and returns the height up the stack it was found at.
    pub fn get_symbol_height(&self, id: Identifier) -> Option<usize> {
        let mut len_iter = self.scope_lengths.iter().rev();
        let mut len = match len_iter.next() {
            Some(x) => *x,
            None => return None
        };
        let mut count = len_iter.len();
        for block_pos in self.scope.iter().rev() {
            len -= 1;
            if len == 0 {
                len = match len_iter.next() {
                    Some(x) => *x,
                    None => return None
                };
                count -= 1;
            }
            let id_map = self.symbols.get(&block_pos).unwrap();
            if let Some(_) = id_map.get(&id) {
                return Some(count)
            }
        }
        None
    }
}
<|endoftext|>