<commit_before>// This file is part of Grust, GObject introspection bindings for Rust
//
// Copyright (C) 2014, 2015  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use boxed;
use boxed::BoxedType;
use gstr::{GStr, OwnedGStr};
use gtype::GType;
use object;
use object::ObjectType;
use types::{gboolean, gchar, gdouble, gfloat, gint, glong, gpointer};
use types::{guchar, guint, gulong};
use util::is_true;

use gobject as ffi;

use std::fmt;
use std::mem;
use std::ops::Deref;

#[unsafe_no_drop_flag]
pub struct Value(ffi::GValue);

impl Drop for Value {
    fn drop(&mut self) {
        if self.as_raw().g_type == 0 {
            return;
        }
        unsafe {
            ffi::g_value_unset(self.as_mut_raw());
        }
    }
}

impl Clone for Value {
    fn clone(&self) -> Value {
        Value(unsafe {
            let mut raw: ffi::GValue = mem::zeroed();
            ffi::g_value_init(&mut raw, self.as_raw().g_type);
            ffi::g_value_copy(self.as_raw(), &mut raw);
            raw
        })
    }
}

impl Value {
    pub fn new(g_type: GType) -> Value {
        Value(unsafe {
            let mut raw: ffi::GValue = mem::zeroed();
            ffi::g_value_init(&mut raw, g_type.to_raw());
            raw
        })
    }

    #[inline]
    fn as_raw(&self) -> &ffi::GValue {
        &self.0
    }

    #[inline]
    fn as_mut_raw(&mut self) -> &mut ffi::GValue {
        &mut self.0
    }

    #[inline]
    pub fn value_type(&self) -> GType {
        unsafe { GType::from_raw(self.as_raw().g_type) }
    }

    pub fn reset(&mut self) {
        unsafe {
            ffi::g_value_reset(self.as_mut_raw());
        }
    }

    pub fn get_boolean(&self) -> bool {
        is_true(unsafe { ffi::g_value_get_boolean(self.as_raw()) })
    }

    pub fn set_boolean(&mut self, val: bool) {
        unsafe { ffi::g_value_set_boolean(self.as_mut_raw(), val as gboolean) };
    }

    pub fn get_char(&self) -> gchar {
        unsafe { ffi::g_value_get_char(self.as_raw()) }
    }

    pub fn set_char(&mut self, val: gchar) {
        unsafe { ffi::g_value_set_char(self.as_mut_raw(), val) };
    }

    pub fn get_schar(&self) -> i8 {
        unsafe { ffi::g_value_get_schar(self.as_raw()) }
    }

    pub fn set_schar(&mut self, val: i8) {
        unsafe { ffi::g_value_set_schar(self.as_mut_raw(), val) };
    }

    pub fn get_uchar(&self) -> guchar {
        unsafe { ffi::g_value_get_uchar(self.as_raw()) }
    }

    pub fn set_uchar(&mut self, val: guchar) {
        unsafe { ffi::g_value_set_uchar(self.as_mut_raw(), val) };
    }

    pub fn get_int(&self) -> gint {
        unsafe { ffi::g_value_get_int(self.as_raw()) }
    }

    pub fn set_int(&mut self, val: gint) {
        unsafe { ffi::g_value_set_int(self.as_mut_raw(), val) };
    }

    pub fn get_uint(&self) -> guint {
        unsafe { ffi::g_value_get_uint(self.as_raw()) }
    }

    pub fn set_uint(&mut self, val: guint) {
        unsafe { ffi::g_value_set_uint(self.as_mut_raw(), val) };
    }

    pub fn get_long(&self) -> glong {
        unsafe { ffi::g_value_get_long(self.as_raw()) }
    }

    pub fn set_long(&mut self, val: glong) {
        unsafe { ffi::g_value_set_long(self.as_mut_raw(), val) };
    }

    pub fn get_ulong(&self) -> gulong {
        unsafe { ffi::g_value_get_ulong(self.as_raw()) }
    }

    pub fn set_ulong(&mut self, val: gulong) {
        unsafe { ffi::g_value_set_ulong(self.as_mut_raw(), val) };
    }

    pub fn get_int64(&self) -> i64 {
        unsafe { ffi::g_value_get_int64(self.as_raw()) }
    }

    pub fn set_int64(&mut self, val: i64) {
        unsafe { ffi::g_value_set_int64(self.as_mut_raw(), val) };
    }

    pub fn get_uint64(&self) -> u64 {
        unsafe { ffi::g_value_get_uint64(self.as_raw()) }
    }

    pub fn set_uint64(&mut self, val: u64) {
        unsafe { ffi::g_value_set_uint64(self.as_mut_raw(), val) };
    }

    pub fn get_float(&self) -> gfloat {
        unsafe { ffi::g_value_get_float(self.as_raw()) }
    }

    pub fn set_float(&mut self, val: gfloat) {
        unsafe { ffi::g_value_set_float(self.as_mut_raw(), val) };
    }

    pub fn get_double(&self) -> gdouble {
        unsafe { ffi::g_value_get_double(self.as_raw()) }
    }

    pub fn set_double(&mut self, val: gdouble) {
        unsafe { ffi::g_value_set_double(self.as_mut_raw(), val) };
    }

    pub fn get_string(&self) -> Option<&GStr> {
        unsafe {
            let ptr = ffi::g_value_get_string(self.as_raw());
            if ptr.is_null() {
                return None;
            }
            Some(GStr::from_ptr(ptr))
        }
    }

    pub fn set_string(&mut self, val: &GStr) {
        unsafe { ffi::g_value_set_string(self.as_mut_raw(), val.as_ptr()) }
    }

    pub fn set_static_string(&mut self, val: &'static GStr) {
        unsafe {
            ffi::g_value_set_static_string(self.as_mut_raw(), val.as_ptr())
        }
    }

    pub fn take_string(&mut self, consumed: OwnedGStr) {
        unsafe {
            let ptr = consumed.as_ptr() as *mut gchar;
            ffi::g_value_take_string(self.as_mut_raw(), ptr);
            mem::forget(consumed);
        }
    }

    pub fn get_object<T>(&self) -> Option<&T>
        where T: ObjectType
    {
        assert!(self.value_type() == object::type_of::<T>(),
                "GValue does not have the object type {}",
                object::type_of::<T>().name());
        let p = unsafe { ffi::g_value_get_object(self.as_raw()) as *const T };
        if p.is_null() {
            return None;
        }
        Some(unsafe { mem::copy_lifetime(self, &*p) })
    }

    pub fn set_object<T>(&mut self, val: &T)
        where T: ObjectType
    {
        let p = val as *const T;
        unsafe { ffi::g_value_set_object(self.as_mut_raw(), p as gpointer) }
    }

    fn assert_boxed_type<T>(&self)
        where T: BoxedType
    {
        assert!(self.value_type() == boxed::type_of::<T>(),
                "GValue does not have the boxed type {}",
                boxed::type_of::<T>().name());
    }

    pub fn dup_boxed<T>(&self) -> Option<T>
        where T: BoxedType
    {
        self.assert_boxed_type::<T>();
        let p = unsafe { ffi::g_value_dup_boxed(self.as_raw()) };
        if p.is_null() {
            return None;
        }
        Some(unsafe {
            let boxed: T = BoxedType::from_ptr(p);
            boxed
        })
    }

    pub fn deref_boxed<T>(&self) -> Option<&<T as Deref>::Target>
        where T: BoxedType + Deref
    {
        self.assert_boxed_type::<T>();
        let p = unsafe { ffi::g_value_get_boxed(self.as_raw()) };
        if p.is_null() {
            return None;
        }
        unsafe {
            let boxed: T = BoxedType::from_ptr(p);
            let ret = Some(mem::copy_lifetime(self, &*boxed));

            // Lose the box by "subliming" it back into the raw pointer
            let q = boxed.into_ptr();
            debug_assert!(q == p,
                "BoxedType invariant violation: into_ptr does not reverse from_ptr");

            ret
        }
    }

    pub fn take_boxed<T>(&mut self, val: T) where T: BoxedType {
        unsafe {
            let p = val.into_ptr();
            ffi::g_value_take_boxed(self.as_mut_raw(), p);
        }
    }
}

impl fmt::Debug for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let contents = unsafe {
            let ptr = ffi::g_strdup_value_contents(self.as_raw());
            OwnedGStr::from_ptr(ptr)
        };
        write!(f, "GValue({})", String::from_utf8_lossy(contents.to_bytes()))
    }
}
<commit_msg>value: Make Value method as_mut_raw unsafe<commit_after>// This file is part of Grust, GObject introspection bindings for Rust
//
// Copyright (C) 2014, 2015  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use boxed;
use boxed::BoxedType;
use gstr::{GStr, OwnedGStr};
use gtype::GType;
use object;
use object::ObjectType;
use types::{gboolean, gchar, gdouble, gfloat, gint, glong, gpointer};
use types::{guchar, guint, gulong};
use util::is_true;

use gobject as ffi;

use std::fmt;
use std::mem;
use std::ops::Deref;

#[unsafe_no_drop_flag]
pub struct Value(ffi::GValue);

impl Drop for Value {
    fn drop(&mut self) {
        if self.as_raw().g_type == 0 {
            return;
        }
        unsafe {
            ffi::g_value_unset(self.as_mut_raw());
        }
    }
}

impl Clone for Value {
    fn clone(&self) -> Value {
        Value(unsafe {
            let mut raw: ffi::GValue = mem::zeroed();
            ffi::g_value_init(&mut raw, self.as_raw().g_type);
            ffi::g_value_copy(self.as_raw(), &mut raw);
            raw
        })
    }
}

impl Value {
    pub fn new(g_type: GType) -> Value {
        Value(unsafe {
            let mut raw: ffi::GValue = mem::zeroed();
            ffi::g_value_init(&mut raw, g_type.to_raw());
            raw
        })
    }

    #[inline]
    fn as_raw(&self) -> &ffi::GValue {
        &self.0
    }

    #[inline]
    unsafe fn as_mut_raw(&mut self) -> &mut ffi::GValue {
        &mut self.0
    }

    #[inline]
    pub fn value_type(&self) -> GType {
        unsafe { GType::from_raw(self.as_raw().g_type) }
    }

    pub fn reset(&mut self) {
        unsafe {
            ffi::g_value_reset(self.as_mut_raw());
        }
    }

    pub fn get_boolean(&self) -> bool {
        is_true(unsafe { ffi::g_value_get_boolean(self.as_raw()) })
    }

    pub fn set_boolean(&mut self, val: bool) {
        unsafe { ffi::g_value_set_boolean(self.as_mut_raw(), val as gboolean) };
    }

    pub fn get_char(&self) -> gchar {
        unsafe { ffi::g_value_get_char(self.as_raw()) }
    }

    pub fn set_char(&mut self, val: gchar) {
        unsafe { ffi::g_value_set_char(self.as_mut_raw(), val) };
    }

    pub fn get_schar(&self) -> i8 {
        unsafe { ffi::g_value_get_schar(self.as_raw()) }
    }

    pub fn set_schar(&mut self, val: i8) {
        unsafe { ffi::g_value_set_schar(self.as_mut_raw(), val) };
    }

    pub fn get_uchar(&self) -> guchar {
        unsafe { ffi::g_value_get_uchar(self.as_raw()) }
    }

    pub fn set_uchar(&mut self, val: guchar) {
        unsafe { ffi::g_value_set_uchar(self.as_mut_raw(), val) };
    }

    pub fn get_int(&self) -> gint {
        unsafe { ffi::g_value_get_int(self.as_raw()) }
    }

    pub fn set_int(&mut self, val: gint) {
        unsafe { ffi::g_value_set_int(self.as_mut_raw(), val) };
    }

    pub fn get_uint(&self) -> guint {
        unsafe { ffi::g_value_get_uint(self.as_raw()) }
    }

    pub fn set_uint(&mut self, val: guint) {
        unsafe { ffi::g_value_set_uint(self.as_mut_raw(), val) };
    }

    pub fn get_long(&self) -> glong {
        unsafe { ffi::g_value_get_long(self.as_raw()) }
    }

    pub fn set_long(&mut self, val: glong) {
        unsafe { ffi::g_value_set_long(self.as_mut_raw(), val) };
    }

    pub fn get_ulong(&self) -> gulong {
        unsafe { ffi::g_value_get_ulong(self.as_raw()) }
    }

    pub fn set_ulong(&mut self, val: gulong) {
        unsafe { ffi::g_value_set_ulong(self.as_mut_raw(), val) };
    }

    pub fn get_int64(&self) -> i64 {
        unsafe { ffi::g_value_get_int64(self.as_raw()) }
    }

    pub fn set_int64(&mut self, val: i64) {
        unsafe { ffi::g_value_set_int64(self.as_mut_raw(), val) };
    }

    pub fn get_uint64(&self) -> u64 {
        unsafe { ffi::g_value_get_uint64(self.as_raw()) }
    }

    pub fn set_uint64(&mut self, val: u64) {
        unsafe { ffi::g_value_set_uint64(self.as_mut_raw(), val) };
    }

    pub fn get_float(&self) -> gfloat {
        unsafe { ffi::g_value_get_float(self.as_raw()) }
    }

    pub fn set_float(&mut self, val: gfloat) {
        unsafe { ffi::g_value_set_float(self.as_mut_raw(), val) };
    }

    pub fn get_double(&self) -> gdouble {
        unsafe { ffi::g_value_get_double(self.as_raw()) }
    }

    pub fn set_double(&mut self, val: gdouble) {
        unsafe { ffi::g_value_set_double(self.as_mut_raw(), val) };
    }

    pub fn get_string(&self) -> Option<&GStr> {
        unsafe {
            let ptr = ffi::g_value_get_string(self.as_raw());
            if ptr.is_null() {
                return None;
            }
            Some(GStr::from_ptr(ptr))
        }
    }

    pub fn set_string(&mut self, val: &GStr) {
        unsafe { ffi::g_value_set_string(self.as_mut_raw(), val.as_ptr()) }
    }

    pub fn set_static_string(&mut self, val: &'static GStr) {
        unsafe {
            ffi::g_value_set_static_string(self.as_mut_raw(), val.as_ptr())
        }
    }

    pub fn take_string(&mut self, consumed: OwnedGStr) {
        unsafe {
            let ptr = consumed.as_ptr() as *mut gchar;
            ffi::g_value_take_string(self.as_mut_raw(), ptr);
            mem::forget(consumed);
        }
    }

    pub fn get_object<T>(&self) -> Option<&T>
        where T: ObjectType
    {
        assert!(self.value_type() == object::type_of::<T>(),
                "GValue does not have the object type {}",
                object::type_of::<T>().name());
        let p = unsafe { ffi::g_value_get_object(self.as_raw()) as *const T };
        if p.is_null() {
            return None;
        }
        Some(unsafe { mem::copy_lifetime(self, &*p) })
    }

    pub fn set_object<T>(&mut self, val: &T)
        where T: ObjectType
    {
        let p = val as *const T;
        unsafe { ffi::g_value_set_object(self.as_mut_raw(), p as gpointer) }
    }

    fn assert_boxed_type<T>(&self)
        where T: BoxedType
    {
        assert!(self.value_type() == boxed::type_of::<T>(),
                "GValue does not have the boxed type {}",
                boxed::type_of::<T>().name());
    }

    pub fn dup_boxed<T>(&self) -> Option<T>
        where T: BoxedType
    {
        self.assert_boxed_type::<T>();
        let p = unsafe { ffi::g_value_dup_boxed(self.as_raw()) };
        if p.is_null() {
            return None;
        }
        Some(unsafe {
            let boxed: T = BoxedType::from_ptr(p);
            boxed
        })
    }

    pub fn deref_boxed<T>(&self) -> Option<&<T as Deref>::Target>
        where T: BoxedType + Deref
    {
        self.assert_boxed_type::<T>();
        let p = unsafe { ffi::g_value_get_boxed(self.as_raw()) };
        if p.is_null() {
            return None;
        }
        unsafe {
            let boxed: T = BoxedType::from_ptr(p);
            let ret = Some(mem::copy_lifetime(self, &*boxed));

            // Lose the box by "subliming" it back into the raw pointer
            let q = boxed.into_ptr();
            debug_assert!(q == p,
                "BoxedType invariant violation: into_ptr does not reverse from_ptr");

            ret
        }
    }

    pub fn take_boxed<T>(&mut self, val: T) where T: BoxedType {
        unsafe {
            let p = val.into_ptr();
            ffi::g_value_take_boxed(self.as_mut_raw(), p);
        }
    }
}

impl fmt::Debug for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let contents = unsafe {
            let ptr = ffi::g_strdup_value_contents(self.as_raw());
            OwnedGStr::from_ptr(ptr)
        };
        write!(f, "GValue({})", String::from_utf8_lossy(contents.to_bytes()))
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2016-2017 Sebastian Dröge <sebastian@centricular.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::fmt;
use std::mem;
use std::ffi::{CStr, CString};
use value::*;
use miniobject::*;

use glib;
use gobject;
use gst;

pub trait Tag<'a> {
    type TagType: ValueType<'a>;
    fn tag_name() -> &'static str;
}

macro_rules! impl_tag(
    ($name:ident, $t:ty, $tag:expr) => {
        pub struct $name;
        impl<'a> Tag<'a> for $name {
            type TagType = $t;
            fn tag_name() -> &'static str {
                $tag
            }
        }
    };
);

impl_tag!(Title, &'a str, "title");
impl_tag!(Album, &'a str, "album");
impl_tag!(Artist, &'a str, "artist");
impl_tag!(Encoder, &'a str, "encoder");
impl_tag!(AudioCodec, &'a str, "audio-codec");
impl_tag!(VideoCodec, &'a str, "video-codec");
impl_tag!(SubtitleCodec, &'a str, "subtitle-codec");
impl_tag!(ContainerFormat, &'a str, "container-format");
// TODO: Should ideally enforce this to be ISO-639
impl_tag!(LanguageCode, &'a str, "language-code");
impl_tag!(Duration, u64, "duration");
impl_tag!(NominalBitrate, u32, "nominal-bitrate");

pub enum MergeMode {
    ReplaceAll,
    Replace,
    Append,
    Prepend,
    Keep,
    KeepAll,
}

impl MergeMode {
    fn to_ffi(&self) -> gst::GstTagMergeMode {
        match *self {
            MergeMode::ReplaceAll => gst::GST_TAG_MERGE_REPLACE_ALL,
            MergeMode::Replace => gst::GST_TAG_MERGE_REPLACE,
            MergeMode::Append => gst::GST_TAG_MERGE_APPEND,
            MergeMode::Prepend => gst::GST_TAG_MERGE_PREPEND,
            MergeMode::Keep => gst::GST_TAG_MERGE_KEEP,
            MergeMode::KeepAll => gst::GST_TAG_MERGE_KEEP_ALL,
        }
    }
}

#[derive(Eq)]
pub struct TagList(*mut gst::GstTagList);

unsafe impl MiniObject for TagList {
    type PtrType = gst::GstTagList;

    unsafe fn as_ptr(&self) -> *mut gst::GstTagList {
        self.0
    }

    unsafe fn replace_ptr(&mut self, ptr: *mut gst::GstTagList) {
        self.0 = ptr
    }

    unsafe fn from_ptr(ptr: *mut gst::GstTagList) -> Self {
        TagList(ptr)
    }
}

impl TagList {
    pub fn new() -> GstRc<Self> {
        unsafe { GstRc::from_owned_ptr(gst::gst_tag_list_new_empty()) }
    }

    pub fn add<'a, T: Tag<'a>>(&mut self, value: T::TagType, mode: MergeMode)
        where T::TagType: Into<Value>
    {
        unsafe {
            let v = value.into();
            let mut gvalue = v.into_raw();
            let tag_name = CString::new(T::tag_name()).unwrap();

            gst::gst_tag_list_add_value(self.0, mode.to_ffi(), tag_name.as_ptr(), &gvalue);

            gobject::g_value_unset(&mut gvalue);
        }
    }

    pub fn get<'a, T: Tag<'a>>(&self) -> Option<TypedValue<T::TagType>>
    {
        unsafe {
            let mut gvalue = mem::zeroed();
            let tag_name = CString::new(T::tag_name()).unwrap();

            let found = gst::gst_tag_list_copy_value(&mut gvalue, self.0, tag_name.as_ptr());

            if found == glib::GFALSE {
                return None;
            }

            let res = match Value::from_raw(gvalue) {
                Some(value) => TypedValue::from_value(value),
                None => None,
            };

            res
        }
    }

    pub fn get_index<'a, T: Tag<'a>>(&'a self, idx: u32) -> Option<TypedValueRef<'a, T::TagType>>
    {
        unsafe {
            let tag_name = CString::new(T::tag_name()).unwrap();

            let value = gst::gst_tag_list_get_value_index(self.0, tag_name.as_ptr(), idx);

            if value.is_null() {
                return None;
            }

            let res = match ValueRef::from_ptr(value) {
                Some(value) => TypedValueRef::from_value_ref(value),
                None => None,
            };

            res
        }
    }

    pub fn to_string(&self) -> String {
        unsafe {
            let ptr = gst::gst_tag_list_to_string(self.0);
            let s = CStr::from_ptr(ptr).to_str().unwrap().into();
            glib::g_free(ptr as glib::gpointer);

            s
        }
    }
}

impl fmt::Debug for TagList {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(&self.to_string())
    }
}

impl PartialEq for TagList {
    fn eq(&self, other: &TagList) -> bool {
        (unsafe { gst::gst_tag_list_is_equal(self.0, other.0) } == glib::GTRUE)
    }
}

unsafe impl Sync for TagList {}
unsafe impl Send for TagList {}

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

    fn init() {
        unsafe {
            gst::gst_init(ptr::null_mut(), ptr::null_mut());
        }
    }

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

        let mut tags = TagList::new();
        assert_eq!(tags.to_string(), "taglist;");
        {
            let tags = tags.get_mut().unwrap();
            tags.add::<Title>("some title".into(), MergeMode::Append);
            tags.add::<Duration>((1000u64 * 1000 * 1000 * 120).into(), MergeMode::Append);
        }
        assert_eq!(tags.to_string(),
                   "taglist, title=(string)\"some\\ title\", duration=(guint64)120000000000;");
    }

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

        let mut tags = TagList::new();
        assert_eq!(tags.to_string(), "taglist;");
        {
            let tags = tags.get_mut().unwrap();
            tags.add::<Title>("some title".into(), MergeMode::Append);
            tags.add::<Duration>((1000u64 * 1000 * 1000 * 120).into(), MergeMode::Append);
        }

        assert_eq!(tags.get::<Title>().unwrap().get(), "some title");
        assert_eq!(tags.get::<Duration>().unwrap().get(),
                   (1000u64 * 1000 * 1000 * 120));
        assert_eq!(tags.get_index::<Title>(0).unwrap().get(), "some title");
        assert_eq!(tags.get_index::<Duration>(0).unwrap().get(),
                   (1000u64 * 1000 * 1000 * 120));
    }
}
<commit_msg>Add Tag iterator for iterating one specific tag<commit_after>// Copyright (C) 2016-2017 Sebastian Dröge <sebastian@centricular.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::fmt;
use std::mem;
use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use value::*;
use miniobject::*;

use glib;
use gobject;
use gst;

pub trait Tag<'a> {
    type TagType: ValueType<'a>;
    fn tag_name() -> &'static str;
}

macro_rules! impl_tag(
    ($name:ident, $t:ty, $tag:expr) => {
        pub struct $name;
        impl<'a> Tag<'a> for $name {
            type TagType = $t;
            fn tag_name() -> &'static str {
                $tag
            }
        }
    };
);

impl_tag!(Title, &'a str, "title");
impl_tag!(Album, &'a str, "album");
impl_tag!(Artist, &'a str, "artist");
impl_tag!(Encoder, &'a str, "encoder");
impl_tag!(AudioCodec, &'a str, "audio-codec");
impl_tag!(VideoCodec, &'a str, "video-codec");
impl_tag!(SubtitleCodec, &'a str, "subtitle-codec");
impl_tag!(ContainerFormat, &'a str, "container-format");
// TODO: Should ideally enforce this to be ISO-639
impl_tag!(LanguageCode, &'a str, "language-code");
impl_tag!(Duration, u64, "duration");
impl_tag!(NominalBitrate, u32, "nominal-bitrate");

pub enum MergeMode {
    ReplaceAll,
    Replace,
    Append,
    Prepend,
    Keep,
    KeepAll,
}

impl MergeMode {
    fn to_ffi(&self) -> gst::GstTagMergeMode {
        match *self {
            MergeMode::ReplaceAll => gst::GST_TAG_MERGE_REPLACE_ALL,
            MergeMode::Replace => gst::GST_TAG_MERGE_REPLACE,
            MergeMode::Append => gst::GST_TAG_MERGE_APPEND,
            MergeMode::Prepend => gst::GST_TAG_MERGE_PREPEND,
            MergeMode::Keep => gst::GST_TAG_MERGE_KEEP,
            MergeMode::KeepAll => gst::GST_TAG_MERGE_KEEP_ALL,
        }
    }
}

#[derive(Eq)]
pub struct TagList(*mut gst::GstTagList);

unsafe impl MiniObject for TagList {
    type PtrType = gst::GstTagList;

    unsafe fn as_ptr(&self) -> *mut gst::GstTagList {
        self.0
    }

    unsafe fn replace_ptr(&mut self, ptr: *mut gst::GstTagList) {
        self.0 = ptr
    }

    unsafe fn from_ptr(ptr: *mut gst::GstTagList) -> Self {
        TagList(ptr)
    }
}

impl TagList {
    pub fn new() -> GstRc<Self> {
        unsafe { GstRc::from_owned_ptr(gst::gst_tag_list_new_empty()) }
    }

    pub fn add<'a, T: Tag<'a>>(&mut self, value: T::TagType, mode: MergeMode)
        where T::TagType: Into<Value>
    {
        unsafe {
            let v = value.into();
            let mut gvalue = v.into_raw();
            let tag_name = CString::new(T::tag_name()).unwrap();

            gst::gst_tag_list_add_value(self.0, mode.to_ffi(), tag_name.as_ptr(), &gvalue);

            gobject::g_value_unset(&mut gvalue);
        }
    }

    pub fn get<'a, T: Tag<'a>>(&self) -> Option<TypedValue<T::TagType>> {
        unsafe {
            let mut gvalue = mem::zeroed();
            let tag_name = CString::new(T::tag_name()).unwrap();

            let found = gst::gst_tag_list_copy_value(&mut gvalue, self.0, tag_name.as_ptr());

            if found == glib::GFALSE {
                return None;
            }

            let res = match Value::from_raw(gvalue) {
                Some(value) => TypedValue::from_value(value),
                None => None,
            };

            res
        }
    }

    pub fn get_index<'a, T: Tag<'a>>(&'a self, idx: u32) -> Option<TypedValueRef<'a, T::TagType>> {
        unsafe {
            let tag_name = CString::new(T::tag_name()).unwrap();

            let value = gst::gst_tag_list_get_value_index(self.0, tag_name.as_ptr(), idx);

            if value.is_null() {
                return None;
            }

            let res = match ValueRef::from_ptr(value) {
                Some(value) => TypedValueRef::from_value_ref(value),
                None => None,
            };

            res
        }
    }

    pub fn get_size<'a, T: Tag<'a>>(&'a self) -> u32 {
        unsafe {
            let tag_name = CString::new(T::tag_name()).unwrap();

            gst::gst_tag_list_get_tag_size(self.0, tag_name.as_ptr())
        }
    }

    pub fn iter_tag<'a, T: Tag<'a>>(&'a self) -> TagIterator<'a, T> {
        TagIterator::new(self)
    }

    pub fn to_string(&self) -> String {
        unsafe {
            let ptr = gst::gst_tag_list_to_string(self.0);
            let s = CStr::from_ptr(ptr).to_str().unwrap().into();
            glib::g_free(ptr as glib::gpointer);

            s
        }
    }
}

impl fmt::Debug for TagList {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(&self.to_string())
    }
}

impl PartialEq for TagList {
    fn eq(&self, other: &TagList) -> bool {
        (unsafe { gst::gst_tag_list_is_equal(self.0, other.0) } == glib::GTRUE)
    }
}

unsafe impl Sync for TagList {}
unsafe impl Send for TagList {}

pub struct TagIterator<'a, T: Tag<'a>> {
    taglist: &'a TagList,
    idx: u32,
    size: u32,
    phantom: PhantomData<T>,
}

impl<'a, T: Tag<'a>> TagIterator<'a, T> {
    fn new(taglist: &'a TagList) -> TagIterator<'a, T> {
        TagIterator {
            taglist: taglist,
            idx: 0,
            size: taglist.get_size::<T>(),
            phantom: PhantomData,
        }
    }
}

impl<'a, T: Tag<'a>> Iterator for TagIterator<'a, T> {
    type Item = TypedValueRef<'a, T::TagType>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.idx >= self.size {
            return None;
        }

        let item = self.taglist.get_index::<T>(self.idx);
        self.idx += 1;

        item
    }
}

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

    fn init() {
        unsafe {
            gst::gst_init(ptr::null_mut(), ptr::null_mut());
        }
    }

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

        let mut tags = TagList::new();
        assert_eq!(tags.to_string(), "taglist;");
        {
            let tags = tags.get_mut().unwrap();
            tags.add::<Title>("some title".into(), MergeMode::Append);
            tags.add::<Duration>((1000u64 * 1000 * 1000 * 120).into(), MergeMode::Append);
        }
        assert_eq!(tags.to_string(),
                   "taglist, title=(string)\"some\\ title\", duration=(guint64)120000000000;");
    }

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

        let mut tags = TagList::new();
        assert_eq!(tags.to_string(), "taglist;");
        {
            let tags = tags.get_mut().unwrap();
            tags.add::<Title>("some title".into(), MergeMode::Append);
            tags.add::<Duration>((1000u64 * 1000 * 1000 * 120).into(), MergeMode::Append);
        }

        assert_eq!(tags.get::<Title>().unwrap().get(), "some title");
        assert_eq!(tags.get::<Duration>().unwrap().get(),
                   (1000u64 * 1000 * 1000 * 120));
        assert_eq!(tags.get_index::<Title>(0).unwrap().get(), "some title");
        assert_eq!(tags.get_index::<Duration>(0).unwrap().get(),
                   (1000u64 * 1000 * 1000 * 120));
    }
}
<|endoftext|>


<commit_before>extern crate time;
extern crate serde;
extern crate serde_json;
extern crate mongodb;

use super::worker_pool;
use super::cove;

use std::thread;
use std::sync::Arc;
use self::serde_json::Value;
use self::mongodb::db::{Database, ThreadedDatabase};
use self::mongodb::coll::options::FindOneAndUpdateOptions;

///
/// Holds a program object
///
pub struct Program {
    pub data: String,
    pub program_id: u64
}

///
/// Does the actual ingestion
///
pub fn ingest(first_time: bool, db: &Database, num_workers: usize) {
    let total_programs = get_total_programs();
    let mut updated_date = Arc::new(String::from(""));

    if !first_time {
        let current_time = time::now_utc();
        updated_date = Arc::new(format!("{}-{}-{}", 1900 + current_time.tm_year, current_time.tm_mon, current_time.tm_mday));
    }

    let mut worker_pool = worker_pool::WorkerPool::new(num_workers);

    for i in (0..total_programs).step_by(200) {
        let shared_updated_date = updated_date.clone();
        let shared_db = db.clone();

        worker_pool.add_worker(thread::spawn(move || {
            let programs = get_programs(i);
            let mut worker_pool = worker_pool::WorkerPool::new(num_workers);
            let coll = shared_db.collection("programs");

            for program in programs {
                let program_id = program.program_id;
                let program_data = program.data.clone();

                let filter = doc! {
                    "program_id" => program_id
                };

                let update = doc! {
                    "program_id" => program_id,
                    "data" => program_data
                };

                let mut options = FindOneAndUpdateOptions::new();
                options.upsert = true;

                coll.find_one_and_replace(filter, update, Some(options)).expect("Can't insert program into database!");
                let shared_updated_date = shared_updated_date.clone();
                let shared_db = shared_db.clone();

                worker_pool.add_worker(thread::spawn(move || {
                    let total_videos = get_video_count_for_program(&shared_updated_date, &program);
                    let mut worker_pool = worker_pool::WorkerPool::new(num_workers);
                    let program = Arc::new(program);

                    for j in (0..total_videos).step_by(200) {
                        let shared_program = program.clone();
                        let shared_updated_date = shared_updated_date.clone();
                        let shared_db = shared_db.clone();

                        worker_pool.add_worker(thread::spawn(move || {
                            get_videos(&shared_updated_date, j, &shared_program, &shared_db);
                        }));
                    }

                    worker_pool.wait_for_children();
                }));
            }

            worker_pool.wait_for_children();
        }));
    }

    worker_pool.wait_for_children();
}

///
/// Gets the total programs to break them up
///
fn get_total_programs() -> u64 {
    cove::video_api("programs", vec![]).as_object().unwrap().get("count").unwrap().as_u64().unwrap()
}

///
/// Gets all the shows from COVE
///
fn get_programs(start_index: u64) -> Vec<Program> {
    let cove_data = cove::video_api("programs", vec![["limit_start", start_index.to_string().as_str()]]);
    let programs: &Vec<Value> = cove_data.as_object().unwrap().get("results").unwrap().as_array().unwrap();
    let mut programs_data = vec![];

    for program in programs {
        let program_uri = program.as_object().unwrap().get("resource_uri").unwrap().as_str().unwrap();
        let program_id: u64 = program_uri.split("/").nth(4).unwrap().parse().unwrap();

        // Do the mongo insert.

        programs_data.push(Program {data: program.to_string(), program_id: program_id});
    }

    programs_data
}

///
/// Gets the total videos for a program so they can be chunked
///
fn get_video_count_for_program<'a>(updated_date: &Arc<String>, program: &Program) -> u64 {
    let program_id = program.program_id.to_string();
    let mut params = vec![
        ["filter_program", program_id.as_str()],
        ["exclude_type", "Other"]
    ];

    if updated_date.as_str() != "" {
        params.push(["filter_record_last_updated_datetime__gt", updated_date.as_str()]);
    }

    cove::video_api("videos", params).as_object().unwrap().get("count").unwrap().as_u64().unwrap()
}

///
/// Gets all videos from COVE for a program, 200 at a time
///
fn get_videos<'a>(updated_date: &Arc<String>, start_index: u64, program: &Arc<Program>, db: &Database) {
    let program_id = program.program_id.to_string();
    let str_start_index = start_index.to_string();
    let mut params = vec![
        ["filter_program", program_id.as_str()],
        ["limit_start", str_start_index.as_str()],
        ["exclude_type", "Other"]
    ];

    if updated_date.as_str() != "" {
        params.push(["filter_record_last_updated_datetime__gt", updated_date.as_str()]);
    }

    let cove_data = cove::video_api("videos", params);
    let videos:&Vec<Value> = cove_data.as_object().unwrap().get("results").unwrap().as_array().unwrap();
    let coll = db.collection("videos");

    for video in videos {

        //
        // Not everything has a tp_media_object_id.  Ignore the ones that don't.
        //
        match video.as_object().unwrap().get("tp_media_object_id").unwrap().as_u64() {
            Some(tp_media_object_id) => {
                let video_data = video.to_string();

                let filter = doc! {
                    "tp_media_object_id" => tp_media_object_id
                };

                let update = doc! {
                    "tp_media_object_id" => tp_media_object_id,
                    "data" => video_data
                };

                let mut options = FindOneAndUpdateOptions::new();
                options.upsert = true;

                coll.find_one_and_replace(filter, update, Some(options)).expect("Can't insert video into database!");
            },
            None => {}
        };
    }
}<commit_msg>Removed incorrect comment<commit_after>extern crate time;
extern crate serde;
extern crate serde_json;
extern crate mongodb;

use super::worker_pool;
use super::cove;

use std::thread;
use std::sync::Arc;
use self::serde_json::Value;
use self::mongodb::db::{Database, ThreadedDatabase};
use self::mongodb::coll::options::FindOneAndUpdateOptions;

///
/// Holds a program object
///
pub struct Program {
    pub data: String,
    pub program_id: u64
}

///
/// Does the actual ingestion
///
pub fn ingest(first_time: bool, db: &Database, num_workers: usize) {
    let total_programs = get_total_programs();
    let mut updated_date = Arc::new(String::from(""));

    if !first_time {
        let current_time = time::now_utc();
        updated_date = Arc::new(format!("{}-{}-{}", 1900 + current_time.tm_year, current_time.tm_mon, current_time.tm_mday));
    }

    let mut worker_pool = worker_pool::WorkerPool::new(num_workers);

    for i in (0..total_programs).step_by(200) {
        let shared_updated_date = updated_date.clone();
        let shared_db = db.clone();

        worker_pool.add_worker(thread::spawn(move || {
            let programs = get_programs(i);
            let mut worker_pool = worker_pool::WorkerPool::new(num_workers);
            let coll = shared_db.collection("programs");

            for program in programs {
                let program_id = program.program_id;
                let program_data = program.data.clone();

                let filter = doc! {
                    "program_id" => program_id
                };

                let update = doc! {
                    "program_id" => program_id,
                    "data" => program_data
                };

                let mut options = FindOneAndUpdateOptions::new();
                options.upsert = true;

                coll.find_one_and_replace(filter, update, Some(options)).expect("Can't insert program into database!");
                let shared_updated_date = shared_updated_date.clone();
                let shared_db = shared_db.clone();

                worker_pool.add_worker(thread::spawn(move || {
                    let total_videos = get_video_count_for_program(&shared_updated_date, &program);
                    let mut worker_pool = worker_pool::WorkerPool::new(num_workers);
                    let program = Arc::new(program);

                    for j in (0..total_videos).step_by(200) {
                        let shared_program = program.clone();
                        let shared_updated_date = shared_updated_date.clone();
                        let shared_db = shared_db.clone();

                        worker_pool.add_worker(thread::spawn(move || {
                            get_videos(&shared_updated_date, j, &shared_program, &shared_db);
                        }));
                    }

                    worker_pool.wait_for_children();
                }));
            }

            worker_pool.wait_for_children();
        }));
    }

    worker_pool.wait_for_children();
}

///
/// Gets the total programs to break them up
///
fn get_total_programs() -> u64 {
    cove::video_api("programs", vec![]).as_object().unwrap().get("count").unwrap().as_u64().unwrap()
}

///
/// Gets all the shows from COVE
///
fn get_programs(start_index: u64) -> Vec<Program> {
    let cove_data = cove::video_api("programs", vec![["limit_start", start_index.to_string().as_str()]]);
    let programs: &Vec<Value> = cove_data.as_object().unwrap().get("results").unwrap().as_array().unwrap();
    let mut programs_data = vec![];

    for program in programs {
        let program_uri = program.as_object().unwrap().get("resource_uri").unwrap().as_str().unwrap();
        let program_id: u64 = program_uri.split("/").nth(4).unwrap().parse().unwrap();

        programs_data.push(Program {data: program.to_string(), program_id: program_id});
    }

    programs_data
}

///
/// Gets the total videos for a program so they can be chunked
///
fn get_video_count_for_program<'a>(updated_date: &Arc<String>, program: &Program) -> u64 {
    let program_id = program.program_id.to_string();
    let mut params = vec![
        ["filter_program", program_id.as_str()],
        ["exclude_type", "Other"]
    ];

    if updated_date.as_str() != "" {
        params.push(["filter_record_last_updated_datetime__gt", updated_date.as_str()]);
    }

    cove::video_api("videos", params).as_object().unwrap().get("count").unwrap().as_u64().unwrap()
}

///
/// Gets all videos from COVE for a program, 200 at a time
///
fn get_videos<'a>(updated_date: &Arc<String>, start_index: u64, program: &Arc<Program>, db: &Database) {
    let program_id = program.program_id.to_string();
    let str_start_index = start_index.to_string();
    let mut params = vec![
        ["filter_program", program_id.as_str()],
        ["limit_start", str_start_index.as_str()],
        ["exclude_type", "Other"]
    ];

    if updated_date.as_str() != "" {
        params.push(["filter_record_last_updated_datetime__gt", updated_date.as_str()]);
    }

    let cove_data = cove::video_api("videos", params);
    let videos:&Vec<Value> = cove_data.as_object().unwrap().get("results").unwrap().as_array().unwrap();
    let coll = db.collection("videos");

    for video in videos {

        //
        // Not everything has a tp_media_object_id.  Ignore the ones that don't.
        //
        match video.as_object().unwrap().get("tp_media_object_id").unwrap().as_u64() {
            Some(tp_media_object_id) => {
                let video_data = video.to_string();

                let filter = doc! {
                    "tp_media_object_id" => tp_media_object_id
                };

                let update = doc! {
                    "tp_media_object_id" => tp_media_object_id,
                    "data" => video_data
                };

                let mut options = FindOneAndUpdateOptions::new();
                options.upsert = true;

                coll.find_one_and_replace(filter, update, Some(options)).expect("Can't insert video into database!");
            },
            None => {}
        };
    }
}<|endoftext|>


<commit_before>/// Directory Hierarchy For A Test Collection
///
/// ```
/// [base]
///   |- [Set 1]
///         |- test_X.fl
///         |- test_Y.fl
///         |-    ...
///   |- [Set 2]
///   |- [Set ..]
///   ..
///   |- [Set N]
/// ```
///
/// A test collection consists of multiple test sets, each of which contains
/// multiple test units. All test units in the same test set shares the test ways,
/// including how to assert an actual result against an the expected result,
/// how to handle input data. In most cases, an input data is a source code.
/// A file name in a test set is used as a test name.

use std::fmt;
use std::ffi::OsStr;
use std::io;
use std::path::{Path, PathBuf};

use difference::{diff, print_diff};
use getopts::{self, Matches, Options};

use util;

pub struct RunEnv {
  program_name: String,
  args: Vec<String>,
  cwd: PathBuf,
  exec_path: PathBuf
}

impl RunEnv {
  pub fn new(args: Vec<String>, cwd: PathBuf, exec_path: PathBuf) -> RunEnv {
    RunEnv {
      program_name: args[0].clone(),
      args: args,
      cwd: cwd,
      exec_path: exec_path
    }
  }

  pub fn program_name(&self) -> &str {
    &self.program_name
  }

  pub fn args(&self) -> &Vec<String> {
    &self.args
  }

  pub fn cwd(&self) -> &Path {
    self.cwd.as_path()
  }

  pub fn exec_path(&self) -> &Path {
    self.exec_path.as_path()
  }
}

pub enum DriverErr {
  Help,
  IncompletedParameters,
  TestFailure,
  OptionParsing(getopts::Fail),
  IoError(io::Error),
  InputDirNotExist(PathBuf)
}

impl From<getopts::Fail> for DriverErr {
  fn from(err: getopts::Fail) -> DriverErr {
    DriverErr::OptionParsing(err)
  }
}

impl From<io::Error> for DriverErr {
  fn from(err: io::Error) -> DriverErr {
    DriverErr::IoError(err)
  }
}

pub type DriverResult<T> = Result<T, DriverErr>;

pub struct TestDriver<'a> {
  env: &'a RunEnv,
  matches: Matches,
  in_base_path: PathBuf,
  out_base_path: PathBuf,
  test_sets: Vec<(Box<TestSet<'a>>)>
}

impl<'a> TestDriver<'a> {
  pub fn new(env: &'a RunEnv, opts: &'a Options, sets: Vec<Box<TestSet<'a>>>)
      -> DriverResult<TestDriver<'a>> {

    let matches = opts.parse(&env.args()[1..])?;
    if matches.opt_present("h") {
      return Err(DriverErr::Help);
    }
    Self::check_requred_params(&matches)?;

    let (in_base_path, out_base_path) = Self::check_and_get_dirs(env.cwd(), &matches)?;

    Ok(TestDriver {
      env: env,
      matches: matches,
      in_base_path: in_base_path,
      out_base_path: out_base_path,
      test_sets: sets
    })
  }

  pub fn add_testset(&mut self, set: Box<TestSet<'a>>) {
    self.test_sets.push(set);
  }

  pub fn env(&self) -> &RunEnv {
    self.env
  }

  pub fn in_base_dir(&self) -> &Path {
    self.in_base_path.as_path()
  }

  pub fn out_base_dir(&self) -> &Path {
    self.out_base_path.as_path()
  }

  fn check_requred_params(matches: &Matches) -> DriverResult<()> {
    if matches.free.len() == 2 {
      Ok(())
    } else {
      Err(DriverErr::IncompletedParameters)
    }
  }

  fn check_and_get_dirs(cwd: &Path, matches: &Matches) -> DriverResult<(PathBuf, PathBuf)> {
    let in_base_path = Self::check_input_path(util::absolute_path(cwd, &matches.free[0]))?;
    Ok((in_base_path, util::absolute_path(cwd, &matches.free[1])))
  }

  fn check_input_path(path: PathBuf) -> DriverResult<PathBuf> {
    if path.exists() {
      Ok(path)
    } else {
      Err(DriverErr::InputDirNotExist(path))
    }
  }

  pub fn run(&self) -> DriverResult<()> {
    for p in self.test_sets.iter() {
      println!("{}", p.name());
      if self.matches.opt_present(p.name()) {
        p.run_all(self)?
      }
    }

    Ok(())
  }
}

fn list_files(dir: &Path, extention: &str) -> io::Result<Vec<PathBuf>> {
  Ok(util::list_files(dir)?
       .map(|e| e.ok().unwrap().path())
       .filter(|p| p.extension().unwrap().to_str().unwrap() == extention)
       .collect::<Vec<PathBuf>>())

}

fn extract_test_name<'a>(file_name: &'a Path) -> &'a str{
  file_name.file_stem().unwrap().to_str().unwrap()
}

impl<'a> fmt::Display for OsStrDisplay<'a> {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", self.s.unwrap().to_str().unwrap())
  }
}

pub struct OsStrDisplay<'a> {
  s: Option<&'a OsStr>
}

pub fn display<'a>(s: Option<&'a OsStr>) -> OsStrDisplay {
  OsStrDisplay {s: s}
}

pub trait TestSet<'a> {
  fn name(&self) -> &str;

  fn assert(&self, edit_dist: i32, expected: &str, result: &str) -> DriverResult<()> {
    if edit_dist > 0 {
      println!(" Failed, difference:");
      print_diff(expected, result, "");
      println!("");
    } else {
      println!(" Ok")
    }

    Ok(())
  }

  fn transform(&self, &Path) -> DriverResult<String>;

  fn in_dir(&self, driver: &TestDriver) -> PathBuf {
    let mut in_dir = PathBuf::from(driver.in_base_dir());
    in_dir.push(self.name());
    in_dir
  }

  fn out_dir(&self, driver: &TestDriver) -> PathBuf {
    let mut in_dir = PathBuf::from(driver.out_base_dir());
    in_dir.push(self.name());
    in_dir
  }

  fn run_all(&self, driver: &TestDriver) -> DriverResult<()> {
    for p in list_files(self.in_dir(driver).as_path(), "fl")? {
      self.run_unit(driver, p.as_path())?;
    }
    Ok(())
  }

  fn run_unit(&self, driver: &TestDriver, input: &Path) -> DriverResult<()> {
    let test_name = extract_test_name(input);

    // transform an input into a string generated from an output.
    let result = self.transform(input)?;
    self.save_result(driver, test_name, &result)?;

    let expected = self.expected_result(driver, test_name)?;

    print!("Testing {} ... ", display(input.file_name()));
    let (dist, _) = diff(&result, &expected, "");

    self.assert(dist, &expected, &result)
  }

  fn save_result(&self, driver: &TestDriver, test_name: &str, result: &str) -> DriverResult<()> {
    let mut save_path = self.out_dir(driver);
    save_path.push(format!("{}.{}", test_name, "result"));
    Ok(util::str_to_file(save_path.as_path(), result)?)

  }

  fn expected_result(&self, driver: &TestDriver, test_name: &str) -> DriverResult<String> {
    let mut expected_path = self.in_dir(driver);
    expected_path.push(format!("{}.{}", test_name, "result"));
    Ok(util::file_to_string(expected_path.as_path())?)
  }
}<commit_msg>Removed unnecessary prints.<commit_after>/// Directory Hierarchy For A Test Collection
///
/// ```
/// [base]
///   |- [Set 1]
///         |- test_X.fl
///         |- test_Y.fl
///         |-    ...
///   |- [Set 2]
///   |- [Set ..]
///   ..
///   |- [Set N]
/// ```
///
/// A test collection consists of multiple test sets, each of which contains
/// multiple test units. All test units in the same test set shares the test ways,
/// including how to assert an actual result against an the expected result,
/// how to handle input data. In most cases, an input data is a source code.
/// A file name in a test set is used as a test name.

use std::fmt;
use std::ffi::OsStr;
use std::io;
use std::path::{Path, PathBuf};

use difference::{diff, print_diff};
use getopts::{self, Matches, Options};

use util;

pub struct RunEnv {
  program_name: String,
  args: Vec<String>,
  cwd: PathBuf,
  exec_path: PathBuf
}

impl RunEnv {
  pub fn new(args: Vec<String>, cwd: PathBuf, exec_path: PathBuf) -> RunEnv {
    RunEnv {
      program_name: args[0].clone(),
      args: args,
      cwd: cwd,
      exec_path: exec_path
    }
  }

  pub fn program_name(&self) -> &str {
    &self.program_name
  }

  pub fn args(&self) -> &Vec<String> {
    &self.args
  }

  pub fn cwd(&self) -> &Path {
    self.cwd.as_path()
  }

  pub fn exec_path(&self) -> &Path {
    self.exec_path.as_path()
  }
}

pub enum DriverErr {
  Help,
  IncompletedParameters,
  TestFailure,
  OptionParsing(getopts::Fail),
  IoError(io::Error),
  InputDirNotExist(PathBuf)
}

impl From<getopts::Fail> for DriverErr {
  fn from(err: getopts::Fail) -> DriverErr {
    DriverErr::OptionParsing(err)
  }
}

impl From<io::Error> for DriverErr {
  fn from(err: io::Error) -> DriverErr {
    DriverErr::IoError(err)
  }
}

pub type DriverResult<T> = Result<T, DriverErr>;

pub struct TestDriver<'a> {
  env: &'a RunEnv,
  matches: Matches,
  in_base_path: PathBuf,
  out_base_path: PathBuf,
  test_sets: Vec<(Box<TestSet<'a>>)>
}

impl<'a> TestDriver<'a> {
  pub fn new(env: &'a RunEnv, opts: &'a Options, sets: Vec<Box<TestSet<'a>>>)
      -> DriverResult<TestDriver<'a>> {

    let matches = opts.parse(&env.args()[1..])?;
    if matches.opt_present("h") {
      return Err(DriverErr::Help);
    }
    Self::check_requred_params(&matches)?;

    let (in_base_path, out_base_path) = Self::check_and_get_dirs(env.cwd(), &matches)?;
    // create the output base dir if not exists
    util::mkdir(&out_base_path)?;

    Ok(TestDriver {
      env: env,
      matches: matches,
      in_base_path: in_base_path,
      out_base_path: out_base_path,
      test_sets: sets
    })
  }

  pub fn add_testset(&mut self, set: Box<TestSet<'a>>) {
    self.test_sets.push(set);
  }

  pub fn env(&self) -> &RunEnv {
    self.env
  }

  pub fn in_base_dir(&self) -> &Path {
    self.in_base_path.as_path()
  }

  pub fn out_base_dir(&self) -> &Path {
    self.out_base_path.as_path()
  }

  fn check_requred_params(matches: &Matches) -> DriverResult<()> {
    if matches.free.len() == 2 {
      Ok(())
    } else {
      Err(DriverErr::IncompletedParameters)
    }
  }

  fn check_and_get_dirs(cwd: &Path, matches: &Matches) -> DriverResult<(PathBuf, PathBuf)> {
    let in_base_path = Self::check_input_path(util::absolute_path(cwd, &matches.free[0]))?;
    Ok((in_base_path, util::absolute_path(cwd, &matches.free[1])))
  }

  fn check_input_path(path: PathBuf) -> DriverResult<PathBuf> {
    if path.exists() {
      Ok(path)
    } else {
      Err(DriverErr::InputDirNotExist(path))
    }
  }

  pub fn run(&self) -> DriverResult<()> {
    for p in self.test_sets.iter() {
      if self.matches.opt_present(p.name()) {
        p.run_all(self)?
      }
    }

    Ok(())
  }
}

fn list_files(dir: &Path, extention: &str) -> io::Result<Vec<PathBuf>> {
  Ok(util::list_files(dir)?
       .map(|e| e.ok().unwrap().path())
       .filter(|p| p.extension().unwrap().to_str().unwrap() == extention)
       .collect::<Vec<PathBuf>>())

}

fn extract_test_name<'a>(file_name: &'a Path) -> &'a str{
  file_name.file_stem().unwrap().to_str().unwrap()
}

impl<'a> fmt::Display for OsStrDisplay<'a> {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", self.s.unwrap().to_str().unwrap())
  }
}

pub struct OsStrDisplay<'a> {
  s: Option<&'a OsStr>
}

pub fn display<'a>(s: Option<&'a OsStr>) -> OsStrDisplay {
  OsStrDisplay {s: s}
}

pub trait TestSet<'a> {
  fn name(&self) -> &str;

  fn assert(&self, edit_dist: i32, expected: &str, result: &str) -> DriverResult<()> {
    if edit_dist > 0 {
      println!(" Failed, difference:");
      print_diff(expected, result, "");
      println!("");
    } else {
      println!(" Ok")
    }

    Ok(())
  }

  fn transform(&self, &Path) -> DriverResult<String>;

  fn in_dir(&self, driver: &TestDriver) -> PathBuf {
    let mut in_dir = PathBuf::from(driver.in_base_dir());
    in_dir.push(self.name());
    in_dir
  }

  fn out_dir(&self, driver: &TestDriver) -> PathBuf {
    let mut in_dir = PathBuf::from(driver.out_base_dir());
    in_dir.push(self.name());
    in_dir
  }

  fn run_all(&self, driver: &TestDriver) -> DriverResult<()> {
    for p in list_files(self.in_dir(driver).as_path(), "fl")? {
      self.run_unit(driver, p.as_path())?;
    }
    Ok(())
  }

  fn run_unit(&self, driver: &TestDriver, input: &Path) -> DriverResult<()> {
    let test_name = extract_test_name(input);

    // transform an input into a string generated from an output.
    print!("Testing {}::{} ... ", self.name(), display(input.file_name()));
    let result = self.transform(input)?;

    self.save_result(driver, test_name, &result)?;
    let expected = self.expected_result(driver, test_name)?;

    let (dist, _) = diff(&result, &expected, "");

    self.assert(dist, &expected, &result)
  }

  fn save_result(&self, driver: &TestDriver, test_name: &str, result: &str) -> DriverResult<()> {
    let mut save_path = self.out_dir(driver);
    util::mkdir(save_path.as_path())?;
    save_path.push(format!("{}.{}", test_name, "result"));
    Ok(util::str_to_file(save_path.as_path(), result)?)
  }

  fn expected_result(&self, driver: &TestDriver, test_name: &str) -> DriverResult<String> {
    let mut expected_path = self.in_dir(driver);
    expected_path.push(format!("{}.{}", test_name, "result"));
    Ok(util::file_to_string(expected_path.as_path())?)
  }
}<|endoftext|>


<commit_before>use std::io::stdin;
use std::io::stdout;
use std::io::Write;
use std::str;
use std::time::Duration;

use zookeeper::{Watcher, WatchedEvent, ZooKeeper};

struct MyWatcher;

impl Watcher for MyWatcher {
    fn handle(&self, _e: &WatchedEvent) {
        // println!("{:?}", e)
    }
}

pub struct Shell {
    hosts: String,
    zk: Option<ZooKeeper>,
    session_timeout: u64,
}

// are we connected?
macro_rules! fetch_zk {
    ($e:expr) => (
        match $e {
            Some(ref __zk) => __zk,
            _ => {
                println!("Not connected.");
                return;
            }
        })
}

macro_rules! check_args {
    ($args:ident, $count:expr) => (
        if $args.len() != $count {
            println!("Wrong number or arguments");
            return;
        })
}

impl Shell {
    pub fn new(hosts: &str) -> Shell {
        Shell {
            hosts: hosts.to_string(),
            zk: None,
            session_timeout: 5
        }
    }

    pub fn run(&mut self) {
        if !self.hosts.is_empty() {
            println!("Connecting...");
            let zk = ZooKeeper::connect(
                &self.hosts,
                Duration::from_secs(self.session_timeout),
                MyWatcher).unwrap();
            self.zk = Some(zk);
        }

        loop {
            let mut line = String::new();

            print!("> ");
            let _ = stdout().flush();

            stdin()
                .read_line(&mut line)
                .ok()
                .expect("Failed to read line");

            let pieces: Vec<&str>  = line.trim().split_whitespace().collect();

            if pieces.len() == 0 {
                continue;
            }

            // dispatch the command
            let args = pieces[1..].to_vec();
            match pieces[0] {
                "get" => self.get(args),
                "set" => self.set(args),
                unknown => println!("Unknown command: {}", unknown)
            }
        }

    }

    fn get(&mut self, args: Vec<&str>) {
        check_args!(args, 1);
        let zk = fetch_zk!(self.zk);

        let data = zk.get_data(args[0], false);
        if data.is_ok() {
            let (bytes, _) = data.unwrap();
            println!("{}", str::from_utf8(&bytes[..]).unwrap().to_string());
        }
    }

    fn set(&mut self, args: Vec<&str>) {
        check_args!(args, 2);
        let zk = fetch_zk!(self.zk);

        let path = args[0];
        let data = args[1].as_bytes().to_vec();
        let ret = zk.set_data(path, data, -1);
        match ret {
            Ok(_) => (),
            Err(e) => println!("set failed: {}", e)
        }
    }
}
<commit_msg>refactor(macros): improve check_args<commit_after>use std::io::stdin;
use std::io::stdout;
use std::io::Write;
use std::str;
use std::time::Duration;

use zookeeper::{Watcher, WatchedEvent, ZooKeeper};

struct MyWatcher;

impl Watcher for MyWatcher {
    fn handle(&self, _e: &WatchedEvent) {
        // println!("{:?}", e)
    }
}

pub struct Shell {
    hosts: String,
    zk: Option<ZooKeeper>,
    session_timeout: u64,
}

// are we connected?
macro_rules! fetch_zk {
    ($e:expr) => (
        match $e {
            Some(ref __zk) => __zk,
            _ => {
                println!("Not connected.");
                return;
            }
        })
}

macro_rules! check_args {
    ($args:ident, $count:expr, $params:expr) => (
        if $args.len() != $count {
            println!("Wrong number of arguments, expected parameters: {}", $params);
            return;
        })
}

impl Shell {
    pub fn new(hosts: &str) -> Shell {
        Shell {
            hosts: hosts.to_string(),
            zk: None,
            session_timeout: 5
        }
    }

    pub fn run(&mut self) {
        if !self.hosts.is_empty() {
            println!("Connecting...");
            let zk = ZooKeeper::connect(
                &self.hosts,
                Duration::from_secs(self.session_timeout),
                MyWatcher).unwrap();
            self.zk = Some(zk);
        }

        loop {
            let mut line = String::new();

            print!("> ");
            let _ = stdout().flush();

            stdin()
                .read_line(&mut line)
                .ok()
                .expect("Failed to read line");

            let pieces: Vec<&str>  = line.trim().split_whitespace().collect();

            if pieces.len() == 0 {
                continue;
            }

            // dispatch the command
            let args = pieces[1..].to_vec();
            match pieces[0] {
                "get" => self.get(args),
                "set" => self.set(args),
                unknown => println!("Unknown command: {}", unknown)
            }
        }

    }

    fn get(&mut self, args: Vec<&str>) {
        check_args!(args, 1, "<path>");
        let zk = fetch_zk!(self.zk);

        let data = zk.get_data(args[0], false);
        if data.is_ok() {
            let (bytes, _) = data.unwrap();
            println!("{}", str::from_utf8(&bytes[..]).unwrap().to_string());
        }
    }

    fn set(&mut self, args: Vec<&str>) {
        check_args!(args, 2, "<path> <data>");
        let zk = fetch_zk!(self.zk);

        let path = args[0];
        let data = args[1].as_bytes().to_vec();
        let ret = zk.set_data(path, data, -1);
        match ret {
            Ok(_) => (),
            Err(e) => println!("set failed: {}", e)
        }
    }
}
<|endoftext|>


<commit_before>#![crate_id(name="tail", vers="1.0.0", author="Morten Olsen Lysgaard")]
/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Morten Olsen Lysgaard <morten@lysgaard.no>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

extern crate getopts;

use std::os;
use std::char;
use std::io::{stdin};
use std::io::BufferedReader;
use std::io::fs::File;
use std::path::Path;
use getopts::{optopt, optflag, getopts, usage};
use std::collections::Deque;
use std::collections::ringbuf::RingBuf;
use std::io::timer::sleep;

static PROGRAM: &'static str = "tail";

#[allow(dead_code)]
fn main () { uumain(os::args()); }

pub fn uumain(args: Vec<String>) {
    let mut line_count = 10u;
    let mut sleep_sec = 1000u;

    // handle obsolete -number syntax
    let options = match obsolete(args.tail()) {
        (args, Some(n)) => { line_count = n; args },
        (args, None) => args
    };

    let args = options;

    let possible_options = [
        optopt("n", "number", "Number of lines to print", "n"),
        optflag("f", "follow", "Print the file as it grows"),
        optopt("s", "sleep-interval", "Number or seconds to sleep between polling the file when running with -f", "n"),
        optflag("h", "help", "help"),
        optflag("V", "version", "version"),
    ];

    let given_options = match getopts(args.as_slice(), possible_options) {
        Ok (m) => { m }
        Err(_) => {
            println!("{:s}", usage(PROGRAM, possible_options));
            return
        }
    };

    if given_options.opt_present("h") {
        println!("{:s}", usage(PROGRAM, possible_options));
        return;
    }
    if given_options.opt_present("V") { version(); return }

    let follow = given_options.opt_present("f");
    if follow {
        match given_options.opt_str("s") {
            Some(n) => {
                let parsed : Option<u64> = from_str(n.as_slice());
                match parsed {
                    Some(m) => { sleep_sec = m*1000 }
                    None => {}
                }
            }
            None => {}
        };
    }

    match given_options.opt_str("n") {
        Some(n) => {
            match from_str(n.as_slice()) {
                Some(m) => { line_count = m }
                None => {}
            }
        }
        None => {}
    };

    let files = given_options.free;

    if files.is_empty() {
        let mut buffer = BufferedReader::new(stdin());
        tail(&mut buffer, line_count, follow, sleep_sec);
    } else {
        let mut multiple = false;
        let mut firstime = true;

        if files.len() > 1 {
            multiple = true;
        }

        for file in files.iter() {
            if multiple {
                if !firstime { println!(""); }
                println!("==> {:s} <==", file.as_slice());
            }
            firstime = false;

            let path = Path::new(file.as_slice());
            let reader = File::open(&path).unwrap();
            let mut buffer = BufferedReader::new(reader);
            tail(&mut buffer, line_count, follow, sleep_sec);
        }
    }
}

// It searches for an option in the form of -123123
//
// In case is found, the options vector will get rid of that object so that
// getopts works correctly.
fn obsolete (options: &[String]) -> (Vec<String>, Option<uint>) {
    let mut options: Vec<String> = Vec::from_slice(options);
    let mut a = 0;
    let b = options.len();

    while a < b {
        let current = options.get(a).clone();
        let current = current.as_slice();

        if current.len() > 1 && current[0] == '-' as u8 {
            let len = current.len();
            for pos in range(1, len) {
                // Ensure that the argument is only made out of digits
                if !char::is_digit(current.char_at(pos)) { break; }

                // If this is the last number
                if pos == len - 1 {
                    options.remove(a);
                    let number : Option<uint> = from_str(current.slice(1,len));
                    return (options, Some(number.unwrap()));
                }
            }
        }

        a += 1;
    };

    (options, None)
}

fn tail<T: Reader> (reader: &mut BufferedReader<T>, line_count:uint, follow:bool, sleep_sec:u64) {
    // read trough each line and store them in a ringbuffer that always contains
    // line_count lines. When reaching the end of file, output the lines in the
    // ringbuf.
    let mut ringbuf : RingBuf<String> = RingBuf::new();
    for io_line in reader.lines(){
        match io_line {
            Ok(line) => {
                if line_count<=ringbuf.len(){
                  ringbuf.pop_front();
                }
                ringbuf.push_back(line);
            }
            Err(err) => fail!(err)
        }
    }
    for line in ringbuf.iter() {
        print!("{}", line);
    }

    // if we follow the file, sleep a bit and print the rest if the file has grown.
    while follow {
        sleep(sleep_sec);
        for io_line in reader.lines() {
            match io_line {
                Ok(line) => print!("{}", line),
                Err(err) => fail!(err)
            }
        }
    }
}

fn version () {
    println!("tail version 0.0.1");
}
<commit_msg>tail: fix build by changing uint to u64<commit_after>#![crate_id(name="tail", vers="1.0.0", author="Morten Olsen Lysgaard")]
/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Morten Olsen Lysgaard <morten@lysgaard.no>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

extern crate getopts;

use std::os;
use std::char;
use std::io::{stdin};
use std::io::BufferedReader;
use std::io::fs::File;
use std::path::Path;
use getopts::{optopt, optflag, getopts, usage};
use std::collections::Deque;
use std::collections::ringbuf::RingBuf;
use std::io::timer::sleep;

static PROGRAM: &'static str = "tail";

#[allow(dead_code)]
fn main () { uumain(os::args()); }

pub fn uumain(args: Vec<String>) {
    let mut line_count = 10u;
    let mut sleep_sec = 1000u64;

    // handle obsolete -number syntax
    let options = match obsolete(args.tail()) {
        (args, Some(n)) => { line_count = n; args },
        (args, None) => args
    };

    let args = options;

    let possible_options = [
        optopt("n", "number", "Number of lines to print", "n"),
        optflag("f", "follow", "Print the file as it grows"),
        optopt("s", "sleep-interval", "Number or seconds to sleep between polling the file when running with -f", "n"),
        optflag("h", "help", "help"),
        optflag("V", "version", "version"),
    ];

    let given_options = match getopts(args.as_slice(), possible_options) {
        Ok (m) => { m }
        Err(_) => {
            println!("{:s}", usage(PROGRAM, possible_options));
            return
        }
    };

    if given_options.opt_present("h") {
        println!("{:s}", usage(PROGRAM, possible_options));
        return;
    }
    if given_options.opt_present("V") { version(); return }

    let follow = given_options.opt_present("f");
    if follow {
        match given_options.opt_str("s") {
            Some(n) => {
                let parsed : Option<u64> = from_str(n.as_slice());
                match parsed {
                    Some(m) => { sleep_sec = m*1000 }
                    None => {}
                }
            }
            None => {}
        };
    }

    match given_options.opt_str("n") {
        Some(n) => {
            match from_str(n.as_slice()) {
                Some(m) => { line_count = m }
                None => {}
            }
        }
        None => {}
    };

    let files = given_options.free;

    if files.is_empty() {
        let mut buffer = BufferedReader::new(stdin());
        tail(&mut buffer, line_count, follow, sleep_sec);
    } else {
        let mut multiple = false;
        let mut firstime = true;

        if files.len() > 1 {
            multiple = true;
        }

        for file in files.iter() {
            if multiple {
                if !firstime { println!(""); }
                println!("==> {:s} <==", file.as_slice());
            }
            firstime = false;

            let path = Path::new(file.as_slice());
            let reader = File::open(&path).unwrap();
            let mut buffer = BufferedReader::new(reader);
            tail(&mut buffer, line_count, follow, sleep_sec);
        }
    }
}

// It searches for an option in the form of -123123
//
// In case is found, the options vector will get rid of that object so that
// getopts works correctly.
fn obsolete (options: &[String]) -> (Vec<String>, Option<uint>) {
    let mut options: Vec<String> = Vec::from_slice(options);
    let mut a = 0;
    let b = options.len();

    while a < b {
        let current = options.get(a).clone();
        let current = current.as_slice();

        if current.len() > 1 && current[0] == '-' as u8 {
            let len = current.len();
            for pos in range(1, len) {
                // Ensure that the argument is only made out of digits
                if !char::is_digit(current.char_at(pos)) { break; }

                // If this is the last number
                if pos == len - 1 {
                    options.remove(a);
                    let number : Option<uint> = from_str(current.slice(1,len));
                    return (options, Some(number.unwrap()));
                }
            }
        }

        a += 1;
    };

    (options, None)
}

fn tail<T: Reader> (reader: &mut BufferedReader<T>, line_count:uint, follow:bool, sleep_sec:u64) {
    // read trough each line and store them in a ringbuffer that always contains
    // line_count lines. When reaching the end of file, output the lines in the
    // ringbuf.
    let mut ringbuf : RingBuf<String> = RingBuf::new();
    for io_line in reader.lines(){
        match io_line {
            Ok(line) => {
                if line_count<=ringbuf.len(){
                  ringbuf.pop_front();
                }
                ringbuf.push_back(line);
            }
            Err(err) => fail!(err)
        }
    }
    for line in ringbuf.iter() {
        print!("{}", line);
    }

    // if we follow the file, sleep a bit and print the rest if the file has grown.
    while follow {
        sleep(sleep_sec);
        for io_line in reader.lines() {
            match io_line {
                Ok(line) => print!("{}", line),
                Err(err) => fail!(err)
            }
        }
    }
}

fn version () {
    println!("tail version 0.0.1");
}
<|endoftext|>


<commit_before>use std::fmt;
use UndoCmd;

/// `UndoStack` maintains a stack of `UndoCmd`s that can be undone and redone by using methods
/// on the `UndoStack`.
///
/// `UndoStack` will notice when it's state changes to either dirty or clean, and the user can
/// set methods that should be called for either state change. This is useful for example if
/// you want to automatically enable or disable undo or redo buttons based on there are any
/// more actions that can be undone or redone.
pub struct UndoStack<'a> {
    // All commands on the stack.
    stack: Vec<Box<UndoCmd + 'a>>,
    // Current position in the stack.
    idx: usize,
    // Max amount of commands allowed on the stack.
    limit: Option<usize>,
    // Called when the state changes from dirty to clean.
    on_clean: Box<FnMut() + 'a>,
    // Called when the state changes from clean to dirty.
    on_dirty: Box<FnMut() + 'a>,
}

impl<'a> UndoStack<'a> {
    /// Creates a new `UndoStack`.
    #[inline]
    pub fn new() -> Self {
        UndoStack {
            stack: Vec::new(),
            idx: 0,
            limit: None,
            on_clean: Box::new(|| {}),
            on_dirty: Box::new(|| {}),
        }
    }

    /// Creates a new `UndoStack` with the specified capacity.
    #[inline]
    pub fn with_capacity(capacity: usize) -> Self {
        UndoStack {
            stack: Vec::with_capacity(capacity),
            idx: 0,
            limit: None,
            on_clean: Box::new(|| {}),
            on_dirty: Box::new(|| {}),
        }
    }

    /// Returns the capacity of the `UndoStack`.
    #[inline]
    pub fn capacity(&self) -> usize {
        self.stack.capacity()
    }

    /// Reserves capacity for at least `additional` more commands to be inserted in the given stack.
    /// The stack may reserve more space to avoid frequent reallocations.
    ///
    /// # Panics
    /// Panics if the new capacity overflows usize.
    #[inline]
    pub fn reserve(&mut self, additional: usize) {
        self.stack.reserve(additional);
    }

    /// Shrinks the capacity of the `UndoStack` as much as possible.
    #[inline]
    pub fn shrink_to_fit(&mut self) {
        self.stack.shrink_to_fit();
    }

    /// Sets the limit on how many `UndoCmd`s can be stored in the stack. If this limit is reached
    /// it will start popping of commands at the bottom of the stack when pushing new commands
    /// on to the stack. No limit is set by default which means it may grow indefinitely.
    ///
    /// The stack will never grow above the limit, but it may remove multiple commands at a
    /// time to increase performance.
    ///
    /// # Panics
    /// Panics if the given limit is zero.
    ///
    /// # Examples
    /// ```
    /// # use undo::{UndoCmd, UndoStack};
    /// # #[derive(Clone, Copy)]
    /// # struct PopCmd {
    /// #   vec: *mut Vec<i32>,
    /// #   e: Option<i32>,
    /// # }
    /// # impl UndoCmd for PopCmd {
    /// #   fn redo(&mut self) {
    /// #       self.e = unsafe {
    /// #           let ref mut vec = *self.vec;
    /// #           vec.pop()
    /// #       }
    /// #   }
    /// #   fn undo(&mut self) {
    /// #       unsafe {
    /// #           let ref mut vec = *self.vec;
    /// #           vec.push(self.e.unwrap());
    /// #       }
    /// #   }
    /// # }
    /// let mut vec = vec![1, 2, 3];
    /// let mut stack = UndoStack::new()
    ///     .limit(2);
    ///
    /// let cmd = PopCmd { vec: &mut vec, e: None };
    /// stack.push(cmd);
    /// stack.push(cmd);
    /// stack.push(cmd); // Pops off the first cmd.
    ///
    /// assert!(vec.is_empty());
    ///
    /// stack.undo();
    /// stack.undo();
    /// stack.undo(); // Does nothing.
    ///
    /// assert_eq!(vec, vec![1, 2]);
    /// ```
    #[inline]
    pub fn limit(mut self, limit: usize) -> Self {
        assert_ne!(limit, 0);

        if limit < self.idx {
            let x = self.idx - limit;
            self.stack.drain(..x);
            self.idx = limit;
            debug_assert_eq!(self.idx, self.stack.len());
        }
        self.limit = Some(limit);
        self
    }

    /// Sets what should happen if the state changes from dirty to clean.
    /// By default the `UndoStack` does nothing when the state changes.
    ///
    /// Note: An empty stack is clean, so the first push will not trigger this method.
    ///
    /// # Examples
    /// ```
    /// # #![allow(unused_variables)]
    /// # use undo::UndoStack;
    /// let mut x = 0;
    /// let stack = UndoStack::new()
    ///     .on_clean(|| x += 1);
    /// ```
    #[inline]
    pub fn on_clean<F>(mut self, f: F) -> Self
        where F: FnMut() + 'a,
    {
        self.on_clean = Box::new(f);
        self
    }

    /// Sets what should happen if the state changes from clean to dirty.
    /// By default the `UndoStack` does nothing when the state changes.
    ///
    /// # Examples
    /// ```
    /// # #![allow(unused_variables)]
    /// # use undo::UndoStack;
    /// let mut x = 0;
    /// let stack = UndoStack::new()
    ///     .on_dirty(|| x += 1);
    /// ```
    #[inline]
    pub fn on_dirty<F>(mut self, f: F) -> Self
        where F: FnMut() + 'a,
    {
        self.on_dirty = Box::new(f);
        self
    }

    /// Returns `true` if the state of the stack is clean, `false` otherwise.
    #[inline]
    pub fn is_clean(&self) -> bool {
        self.idx == self.stack.len()
    }

    /// Returns `true` if the state of the stack is dirty, `false` otherwise.
    #[inline]
    pub fn is_dirty(&self) -> bool {
        !self.is_clean()
    }

    /// Pushes a `UndoCmd` to the top of the `UndoStack` and executes its [`redo`] method.
    /// This pops off all `UndoCmd`s that is above the active command from the `UndoStack`.
    ///
    /// If `cmd`s id is equal to the current top command, the two commands are merged.
    ///
    /// [`redo`]: trait.UndoCmd.html#tymethod.redo
    pub fn push<T>(&mut self, mut cmd: T)
        where T: UndoCmd + 'a,
    {
        let is_dirty = self.is_dirty();
        let len = self.idx;
        // Pop off all elements after len from stack.
        self.stack.truncate(len);
        cmd.redo();

        if len == 0 {
            self.idx += 1;
            self.stack.push(Box::new(cmd));
        } else {
            let idx = len - 1;
            match (cmd.id(), unsafe { self.stack.get_unchecked(idx).id() }) {
                (Some(id1), Some(id2)) if id1 == id2 => {
                    // Merge the command with the one on the top of the stack.
                    let cmd = MergeCmd {
                        cmd1: unsafe {
                            // Unchecked pop.
                            self.stack.set_len(idx);
                            ::std::ptr::read(self.stack.get_unchecked(idx))
                        },
                        cmd2: Box::new(cmd),
                    };
                    self.stack.push(Box::new(cmd));
                },
                _ => {
                    match self.limit {
                        Some(limit) if len == limit => {
                            // Remove ~25% of the stack at once.
                            let x = len / 4 + 1;
                            self.stack.drain(..x);
                            self.idx -= x - 1;
                        },
                        _ => self.idx += 1,
                    }
                    self.stack.push(Box::new(cmd));
                },
            }
        }

        debug_assert_eq!(self.idx, self.stack.len());
        // State is always clean after a push, check if it was dirty before.
        if is_dirty {
            let ref mut f = self.on_clean;
            f();
        }
    }

    /// Calls the [`redo`] method for the active `UndoCmd` and sets the next `UndoCmd` as the new
    /// active one.
    ///
    /// Calling this method when there are no more commands to redo does nothing.
    ///
    /// [`redo`]: trait.UndoCmd.html#tymethod.redo
    pub fn redo(&mut self) {
        if self.idx < self.stack.len() {
            let is_dirty = self.is_dirty();
            unsafe {
                let cmd = self.stack.get_unchecked_mut(self.idx);
                cmd.redo();
            }
            self.idx += 1;
            // Check if stack went from dirty to clean.
            if is_dirty && self.is_clean() {
                let ref mut f = self.on_clean;
                f();
            }
        }
    }

    /// Calls the [`undo`] method for the active `UndoCmd` and sets the previous `UndoCmd` as the
    /// new active one.
    ///
    /// Calling this method when there are no more commands to undo does nothing.
    ///
    /// [`undo`]: trait.UndoCmd.html#tymethod.undo
    pub fn undo(&mut self) {
        if self.idx > 0 {
            let is_clean = self.is_clean();
            self.idx -= 1;
            debug_assert!(self.idx < self.stack.len());
            unsafe {
                let cmd = self.stack.get_unchecked_mut(self.idx);
                cmd.undo();
            }
            // Check if stack went from clean to dirty.
            if is_clean && self.is_dirty() {
                let ref mut f = self.on_dirty;
                f();
            }
        }
    }
}

impl<'a> Default for UndoStack<'a> {
    #[inline]
    fn default() -> Self {
        Self::new()
    }
}

impl<'a> fmt::Debug for UndoStack<'a> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("UndoStack")
            .field("stack", &self.stack)
            .field("idx", &self.idx)
            .finish()
    }
}

struct MergeCmd<'a> {
    cmd1: Box<UndoCmd + 'a>,
    cmd2: Box<UndoCmd + 'a>,
}

impl<'a> UndoCmd for MergeCmd<'a> {
    #[inline]
    fn redo(&mut self) {
        self.cmd1.redo();
        self.cmd2.redo();
    }

    #[inline]
    fn undo(&mut self) {
        self.cmd2.undo();
        self.cmd1.undo();
    }

    #[inline]
    fn id(&self) -> Option<u64> {
        self.cmd1.id()
    }
}

#[cfg(test)]
mod test {
    use {UndoStack, UndoCmd};

    #[derive(Clone, Copy)]
    struct PopCmd {
        vec: *mut Vec<i32>,
        e: Option<i32>,
    }

    impl UndoCmd for PopCmd {
        fn redo(&mut self) {
            self.e = unsafe {
                let ref mut vec = *self.vec;
                vec.pop()
            }
        }

        fn undo(&mut self) {
            unsafe {
                let ref mut vec = *self.vec;
                vec.push(self.e.unwrap());
            }
        }
    }

    #[test]
    fn pop() {
        use std::cell::Cell;

        let x = Cell::new(0);
        let mut vec = vec![1, 2, 3];
        let mut stack = UndoStack::new()
            .on_clean(|| x.set(0))
            .on_dirty(|| x.set(1));

        let cmd = PopCmd { vec: &mut vec, e: None };
        for _ in 0..3 {
            stack.push(cmd);
        }
        assert_eq!(x.get(), 0);
        assert!(vec.is_empty());

        for _ in 0..3 {
            stack.undo();
        }
        assert_eq!(x.get(), 1);
        assert_eq!(vec, vec![1, 2, 3]);

        stack.push(cmd);
        assert_eq!(x.get(), 0);
        assert_eq!(vec, vec![1, 2]);

        stack.undo();
        assert_eq!(x.get(), 1);
        assert_eq!(vec, vec![1, 2, 3]);

        stack.redo();
        assert_eq!(x.get(), 0);
        assert_eq!(vec, vec![1, 2]);
    }

    #[test]
    fn limit() {
        let mut vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
        let mut stack = UndoStack::new();

        let cmd = PopCmd { vec: &mut vec, e: None };

        for _ in 0..6 {
            stack.push(cmd);
        }
        assert_eq!(vec, vec![1, 2, 3, 4]);

        stack = stack.limit(3);
        assert_eq!(stack.stack.len(), 3);

        for _ in 0..6 {
            stack.undo();
        }
        assert_eq!(vec, vec![1, 2, 3, 4, 5, 6, 7]);

        for _ in 0..6 {
            stack.redo();
        }
        assert_eq!(vec, vec![1, 2, 3, 4]);
    }
}
<commit_msg>Add limit to Debug<commit_after>use std::fmt;
use UndoCmd;

/// `UndoStack` maintains a stack of `UndoCmd`s that can be undone and redone by using methods
/// on the `UndoStack`.
///
/// `UndoStack` will notice when it's state changes to either dirty or clean, and the user can
/// set methods that should be called for either state change. This is useful for example if
/// you want to automatically enable or disable undo or redo buttons based on there are any
/// more actions that can be undone or redone.
pub struct UndoStack<'a> {
    // All commands on the stack.
    stack: Vec<Box<UndoCmd + 'a>>,
    // Current position in the stack.
    idx: usize,
    // Max amount of commands allowed on the stack.
    limit: Option<usize>,
    // Called when the state changes from dirty to clean.
    on_clean: Box<FnMut() + 'a>,
    // Called when the state changes from clean to dirty.
    on_dirty: Box<FnMut() + 'a>,
}

impl<'a> UndoStack<'a> {
    /// Creates a new `UndoStack`.
    #[inline]
    pub fn new() -> Self {
        UndoStack {
            stack: Vec::new(),
            idx: 0,
            limit: None,
            on_clean: Box::new(|| {}),
            on_dirty: Box::new(|| {}),
        }
    }

    /// Creates a new `UndoStack` with the specified capacity.
    #[inline]
    pub fn with_capacity(capacity: usize) -> Self {
        UndoStack {
            stack: Vec::with_capacity(capacity),
            idx: 0,
            limit: None,
            on_clean: Box::new(|| {}),
            on_dirty: Box::new(|| {}),
        }
    }

    /// Returns the capacity of the `UndoStack`.
    #[inline]
    pub fn capacity(&self) -> usize {
        self.stack.capacity()
    }

    /// Reserves capacity for at least `additional` more commands to be inserted in the given stack.
    /// The stack may reserve more space to avoid frequent reallocations.
    ///
    /// # Panics
    /// Panics if the new capacity overflows usize.
    #[inline]
    pub fn reserve(&mut self, additional: usize) {
        self.stack.reserve(additional);
    }

    /// Shrinks the capacity of the `UndoStack` as much as possible.
    #[inline]
    pub fn shrink_to_fit(&mut self) {
        self.stack.shrink_to_fit();
    }

    /// Sets the limit on how many `UndoCmd`s can be stored in the stack. If this limit is reached
    /// it will start popping of commands at the bottom of the stack when pushing new commands
    /// on to the stack. No limit is set by default which means it may grow indefinitely.
    ///
    /// The stack will never grow above the limit, but it may remove multiple commands at a
    /// time to increase performance.
    ///
    /// # Panics
    /// Panics if the given limit is zero.
    ///
    /// # Examples
    /// ```
    /// # use undo::{UndoCmd, UndoStack};
    /// # #[derive(Clone, Copy)]
    /// # struct PopCmd {
    /// #   vec: *mut Vec<i32>,
    /// #   e: Option<i32>,
    /// # }
    /// # impl UndoCmd for PopCmd {
    /// #   fn redo(&mut self) {
    /// #       self.e = unsafe {
    /// #           let ref mut vec = *self.vec;
    /// #           vec.pop()
    /// #       }
    /// #   }
    /// #   fn undo(&mut self) {
    /// #       unsafe {
    /// #           let ref mut vec = *self.vec;
    /// #           vec.push(self.e.unwrap());
    /// #       }
    /// #   }
    /// # }
    /// let mut vec = vec![1, 2, 3];
    /// let mut stack = UndoStack::new()
    ///     .limit(2);
    ///
    /// let cmd = PopCmd { vec: &mut vec, e: None };
    /// stack.push(cmd);
    /// stack.push(cmd);
    /// stack.push(cmd); // Pops off the first cmd.
    ///
    /// assert!(vec.is_empty());
    ///
    /// stack.undo();
    /// stack.undo();
    /// stack.undo(); // Does nothing.
    ///
    /// assert_eq!(vec, vec![1, 2]);
    /// ```
    #[inline]
    pub fn limit(mut self, limit: usize) -> Self {
        assert_ne!(limit, 0);

        if limit < self.idx {
            let x = self.idx - limit;
            self.stack.drain(..x);
            self.idx = limit;
            debug_assert_eq!(self.idx, self.stack.len());
        }
        self.limit = Some(limit);
        self
    }

    /// Sets what should happen if the state changes from dirty to clean.
    /// By default the `UndoStack` does nothing when the state changes.
    ///
    /// Note: An empty stack is clean, so the first push will not trigger this method.
    ///
    /// # Examples
    /// ```
    /// # #![allow(unused_variables)]
    /// # use undo::UndoStack;
    /// let mut x = 0;
    /// let stack = UndoStack::new()
    ///     .on_clean(|| x += 1);
    /// ```
    #[inline]
    pub fn on_clean<F>(mut self, f: F) -> Self
        where F: FnMut() + 'a,
    {
        self.on_clean = Box::new(f);
        self
    }

    /// Sets what should happen if the state changes from clean to dirty.
    /// By default the `UndoStack` does nothing when the state changes.
    ///
    /// # Examples
    /// ```
    /// # #![allow(unused_variables)]
    /// # use undo::UndoStack;
    /// let mut x = 0;
    /// let stack = UndoStack::new()
    ///     .on_dirty(|| x += 1);
    /// ```
    #[inline]
    pub fn on_dirty<F>(mut self, f: F) -> Self
        where F: FnMut() + 'a,
    {
        self.on_dirty = Box::new(f);
        self
    }

    /// Returns `true` if the state of the stack is clean, `false` otherwise.
    #[inline]
    pub fn is_clean(&self) -> bool {
        self.idx == self.stack.len()
    }

    /// Returns `true` if the state of the stack is dirty, `false` otherwise.
    #[inline]
    pub fn is_dirty(&self) -> bool {
        !self.is_clean()
    }

    /// Pushes a `UndoCmd` to the top of the `UndoStack` and executes its [`redo`] method.
    /// This pops off all `UndoCmd`s that is above the active command from the `UndoStack`.
    ///
    /// If `cmd`s id is equal to the current top command, the two commands are merged.
    ///
    /// [`redo`]: trait.UndoCmd.html#tymethod.redo
    pub fn push<T>(&mut self, mut cmd: T)
        where T: UndoCmd + 'a,
    {
        let is_dirty = self.is_dirty();
        let len = self.idx;
        // Pop off all elements after len from stack.
        self.stack.truncate(len);
        cmd.redo();

        if len == 0 {
            self.idx += 1;
            self.stack.push(Box::new(cmd));
        } else {
            let idx = len - 1;
            match (cmd.id(), unsafe { self.stack.get_unchecked(idx).id() }) {
                (Some(id1), Some(id2)) if id1 == id2 => {
                    // Merge the command with the one on the top of the stack.
                    let cmd = MergeCmd {
                        cmd1: unsafe {
                            // Unchecked pop.
                            self.stack.set_len(idx);
                            ::std::ptr::read(self.stack.get_unchecked(idx))
                        },
                        cmd2: Box::new(cmd),
                    };
                    self.stack.push(Box::new(cmd));
                },
                _ => {
                    match self.limit {
                        Some(limit) if len == limit => {
                            // Remove ~25% of the stack at once.
                            let x = len / 4 + 1;
                            self.stack.drain(..x);
                            self.idx -= x - 1;
                        },
                        _ => self.idx += 1,
                    }
                    self.stack.push(Box::new(cmd));
                },
            }
        }

        debug_assert_eq!(self.idx, self.stack.len());
        // State is always clean after a push, check if it was dirty before.
        if is_dirty {
            let ref mut f = self.on_clean;
            f();
        }
    }

    /// Calls the [`redo`] method for the active `UndoCmd` and sets the next `UndoCmd` as the new
    /// active one.
    ///
    /// Calling this method when there are no more commands to redo does nothing.
    ///
    /// [`redo`]: trait.UndoCmd.html#tymethod.redo
    pub fn redo(&mut self) {
        if self.idx < self.stack.len() {
            let is_dirty = self.is_dirty();
            unsafe {
                let cmd = self.stack.get_unchecked_mut(self.idx);
                cmd.redo();
            }
            self.idx += 1;
            // Check if stack went from dirty to clean.
            if is_dirty && self.is_clean() {
                let ref mut f = self.on_clean;
                f();
            }
        }
    }

    /// Calls the [`undo`] method for the active `UndoCmd` and sets the previous `UndoCmd` as the
    /// new active one.
    ///
    /// Calling this method when there are no more commands to undo does nothing.
    ///
    /// [`undo`]: trait.UndoCmd.html#tymethod.undo
    pub fn undo(&mut self) {
        if self.idx > 0 {
            let is_clean = self.is_clean();
            self.idx -= 1;
            debug_assert!(self.idx < self.stack.len());
            unsafe {
                let cmd = self.stack.get_unchecked_mut(self.idx);
                cmd.undo();
            }
            // Check if stack went from clean to dirty.
            if is_clean && self.is_dirty() {
                let ref mut f = self.on_dirty;
                f();
            }
        }
    }
}

impl<'a> Default for UndoStack<'a> {
    #[inline]
    fn default() -> Self {
        Self::new()
    }
}

impl<'a> fmt::Debug for UndoStack<'a> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("UndoStack")
            .field("stack", &self.stack)
            .field("idx", &self.idx)
            .field("limit", &self.limit)
            .finish()
    }
}

struct MergeCmd<'a> {
    cmd1: Box<UndoCmd + 'a>,
    cmd2: Box<UndoCmd + 'a>,
}

impl<'a> UndoCmd for MergeCmd<'a> {
    #[inline]
    fn redo(&mut self) {
        self.cmd1.redo();
        self.cmd2.redo();
    }

    #[inline]
    fn undo(&mut self) {
        self.cmd2.undo();
        self.cmd1.undo();
    }

    #[inline]
    fn id(&self) -> Option<u64> {
        self.cmd1.id()
    }
}

#[cfg(test)]
mod test {
    use {UndoStack, UndoCmd};

    #[derive(Clone, Copy)]
    struct PopCmd {
        vec: *mut Vec<i32>,
        e: Option<i32>,
    }

    impl UndoCmd for PopCmd {
        fn redo(&mut self) {
            self.e = unsafe {
                let ref mut vec = *self.vec;
                vec.pop()
            }
        }

        fn undo(&mut self) {
            unsafe {
                let ref mut vec = *self.vec;
                vec.push(self.e.unwrap());
            }
        }
    }

    #[test]
    fn pop() {
        use std::cell::Cell;

        let x = Cell::new(0);
        let mut vec = vec![1, 2, 3];
        let mut stack = UndoStack::new()
            .on_clean(|| x.set(0))
            .on_dirty(|| x.set(1));

        let cmd = PopCmd { vec: &mut vec, e: None };
        for _ in 0..3 {
            stack.push(cmd);
        }
        assert_eq!(x.get(), 0);
        assert!(vec.is_empty());

        for _ in 0..3 {
            stack.undo();
        }
        assert_eq!(x.get(), 1);
        assert_eq!(vec, vec![1, 2, 3]);

        stack.push(cmd);
        assert_eq!(x.get(), 0);
        assert_eq!(vec, vec![1, 2]);

        stack.undo();
        assert_eq!(x.get(), 1);
        assert_eq!(vec, vec![1, 2, 3]);

        stack.redo();
        assert_eq!(x.get(), 0);
        assert_eq!(vec, vec![1, 2]);
    }

    #[test]
    fn limit() {
        let mut vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
        let mut stack = UndoStack::new();

        let cmd = PopCmd { vec: &mut vec, e: None };

        for _ in 0..6 {
            stack.push(cmd);
        }
        assert_eq!(vec, vec![1, 2, 3, 4]);

        stack = stack.limit(3);
        assert_eq!(stack.stack.len(), 3);

        for _ in 0..6 {
            stack.undo();
        }
        assert_eq!(vec, vec![1, 2, 3, 4, 5, 6, 7]);

        for _ in 0..6 {
            stack.redo();
        }
        assert_eq!(vec, vec![1, 2, 3, 4]);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use scope::Scope;

pub struct State<'s> {
    default_ns: String,
    namespaces: HashMap<String, Scope<'s>>,
}

impl<'s> State<'s> {
    pub fn new(default: String) -> State<'s> {
        State {
            default_ns: default,
            namespaces: HashMap::new(),
        }
    }
}
<commit_msg>Start to move eval and expand logic to state module as an experiment<commit_after>use std::collections::HashMap;
use scope::Scope;
use ast::{Expr, EvalResult};
use ast::EvalError::*;

pub struct State<'s> {
    default_ns: String,
    namespaces: HashMap<String, Scope<'s>>,
}

impl<'s> State<'s> {
    pub fn new(default: String) -> State<'s> {
        State {
            default_ns: default,
            namespaces: HashMap::new(),
        }
    }

    pub fn eval(&self, expr: &Expr) -> EvalResult {
        match try!(self.expand(expr)) {
            ref sym_expr @ Expr::Symbol { .. } => {
                self.eval_symbol(sym_expr)
            },
            ref def_expr @ Expr::Def { .. } => {
                self.eval_def(def_expr)
            },
            ref call_expr @ Expr::Call { .. } => {
                self.eval_call(call_expr)
            },
            ref let_expr @ Expr::Let { .. } => {
                self.eval_let(let_expr)
            },
            other_expr => {
                Ok(other_expr)
            },
        }
    }

    fn eval_symbol(&self, expr: &Expr) -> EvalResult {
        Ok(expr.clone())
    }

    fn eval_def(&self, expr: &Expr) -> EvalResult {
        Ok(expr.clone())
    }

    fn eval_call(&self, expr: &Expr) -> EvalResult {
        Ok(expr.clone())
    }

    fn eval_let(&self, expr: &Expr) -> EvalResult {
        Ok(expr.clone())
    }

    fn expand(&self, expr: &Expr) -> EvalResult {
        if let Expr::List(ref l) = *expr {
            if l.len() > 0 {
                if let Expr::Symbol { ref name, .. } = l[0] {
                    match &name[..] {
                        "def" => {
                            return self.expand_def(expr)
                        },
                        "fn" => {
                            return self.expand_fn(expr)
                        },
                        "macro" => {
                            return self.expand_macro(expr)
                        },
                        "quote" => {
                            return self.expand_quote(expr)
                        },
                        "unquote" => {
                            return self.expand_unquote(expr)
                        },
                        "unquote-splicing" => {
                            return self.expand_unquote_splicing(expr)
                        },
                        "let" => {
                            return self.expand_let(expr)
                        },
                        _ => {
                            return self.expand_call(expr)
                        }
                    }
                } else {
                    return Err(DispatchError(l[0].clone()))
                }
            }
        }
        Ok(expr.clone())
    }

    fn expand_def(&self, expr: &Expr) -> EvalResult {
        Ok(expr.clone())
    }

    fn expand_fn(&self, expr: &Expr) -> EvalResult {
        Ok(expr.clone())
    }

    fn expand_macro(&self, expr: &Expr) -> EvalResult {
        Ok(expr.clone())
    }

    fn expand_quote(&self, expr: &Expr) -> EvalResult {
        Ok(expr.clone())
    }

    fn expand_unquote(&self, expr: &Expr) -> EvalResult {
        Ok(expr.clone())
    }

    fn expand_unquote_splicing(&self, expr: &Expr) -> EvalResult {
        Ok(expr.clone())
    }

    fn expand_let(&self, expr: &Expr) -> EvalResult {
        Ok(expr.clone())
    }

    fn expand_call(&self, expr: &Expr) -> EvalResult {
        Ok(expr.clone())
    }

}
<|endoftext|>