<commit_before>//! Intrusive reference types.
//!
//! Use of intrusively-reference-counted objects with matching reference types
//! over containers like `std::rc::Rc` or `std::sync::Arc` trades some
//! compile-time safety checks (and likely performance benefits) for greater
//! flexibility in how those objects are allocated and used.
//!
//! `Rc` and `Arc` are themselves references to underlying *containers*; any
//! object passed to either's `::new` will be moved to a newly-allocated block
//! on the heap.  To support weak references the data's reference count is
//! stored in another heap-allocated block, which is freed *only when the last
//! reference, weak *or* strong, is dropped*.  Because these types have
//! a reference count stored separately from the data, it is impossible to
//! create a new `Rc`, `Arc`, or `Weak` pointer thereto without one of
//! the same.
//!
//! In contrast, the types defined here forgo support for weak references and
//! strong guarantees of lifetime-management and memory-safety to allow the
//! developer more flexibility in data storage and manipulation.
//!
//! Since reference counts are stored alongside the data (in types
//! implementing [`RefCountHolder`](trait.RefCountHolder.html)),
//! a stack-allocated value can safely provide references to itself as long as
//! its refcount is non-zero prior to its doing so.  they support references
//! to stack-allocated values and values in memory not managed directly the by
//! [`Reference`](trait.Reference.html) implementation.
use std;
use std::fmt;
use std::mem;
use std::ops::{Deref,DerefMut};
use std::ptr::PtrExt;
use std::clone::Clone;
use std::hash::{Hash, Hasher};
use std::borrow::Borrow;

/// Abstract interface for references to intrusively-refcounted values.
pub trait Reference<T: ?Sized> where T: RefCounted, Self: Sized {
    /// Create a reference from a raw pointer.  The reference-type
    /// implementation should do no management of the referand's refcount for
    /// this operation.
    fn from_obj<U>(obj: &U) -> Self;

    /// Create an intrusive reference from a trait object.  The reference-type
    /// implementation should do no management of the referand's refcount for
    /// this operation.
    fn from_trait_obj(obj: &T) -> Self;

    /// Move the given value into the heap, and return a reference to it.  The value
    /// should have a refcount of zero prior to this function.
    ///
    /// # Examples
    ///
    /// ```rust
    /// #[macro_use]
    /// use cripes::util::intrusive;
    /// use cripes::util::intrusive::{Ref,Reference};
    ///
    /// #[derive(Debug)]
    /// struct Dog { refcount: usize }
    /// default_refcounted_impl!(Dog);
    ///
    /// let x: Ref<_> = Reference::new_on_heap(Dog{refcount: 0});   // x is an intrusive reference to the heap
    ///
    /// println!("{:?}", x);
    /// drop(x);                  // dropping the sole reference to that location
    ///                           // will free the memory.
    /// ```
    fn new_on_heap<U>(mut obj: U) -> Self where Self: Sized, U: Sized + RefCounted {
        {
            let rc = obj.as_refcounted_mut();
            // Zero the reference count: we're "taking ownership" of the object.
            while rc.get_refcount() != 0 { rc.remove_ref(); }

            rc.add_ref();
        }

        // Move the object onto the heap using Box, then discard the box in
        // favor of a hot, sticky, unsafe mess.
        let heaped: *const () = unsafe { std::boxed::into_raw(Box::new(obj)) as *const () };

        if std::mem::size_of::<&T>() == std::mem::size_of::<usize>() {
            // standard pointer
            unsafe { Reference::from_obj(&*(heaped as *const U)) }
        } else {
            // trait object
            unsafe { Reference::from_trait_obj(&*(heaped as *const T)) }
        }
    }
}

/// Describes a type that holds a reference count.
pub trait RefCountHolder {
    /// Fetch the object's current reference count.
    fn get_refcount(&self) -> usize;

    /// Increment the object's reference count, returning the new value.
    fn add_ref(&mut self) -> usize;

    /// Decrement the object's reference count, returning the new value.
    fn remove_ref(&mut self) -> usize;
}

pub trait RefCounted {
    // /// Create a reference to the current object.
    // fn ref_to_self<T: ?Sized>(&self) -> Ref<T> where T: RefCounted, Self: T {
    //     Reference::from_raw(unsafe { std::mem::transmute::<_,*const Self>(self) })
    // }

    fn as_refcounted(&self) -> &RefCountHolder;
    fn as_refcounted_mut(&mut self) -> &mut RefCountHolder;
}

impl<T> RefCounted for T where T: RefCountHolder {
    fn as_refcounted(&self) -> &RefCountHolder { self as &RefCountHolder }
    fn as_refcounted_mut(&mut self) -> &mut RefCountHolder { self as &mut RefCountHolder }
}

/// Create a simple implementation of util::intrusive::RefCountHolder for a type.
#[macro_export]
macro_rules! default_refcounted_impl {
    ($target:ident) => (default_refcounted_impl!($target, refcount););

    ($target:ident, $($mbr:ident).*) => (
        impl ::cripes::util::intrusive::RefCountHolder for $target {
            fn get_refcount(&self) -> usize { self.$($mbr).* }
            fn add_ref(&mut self) -> usize { self.$($mbr).* += 1; self.$($mbr).* }
            fn remove_ref(&mut self) -> usize { self.$($mbr).* -= 1; self.$($mbr).* }
        });
}

/* ****************************************************************
 * MutRef<T>
 */
// This is disabled for now -- we'd need a refcounted `RwLock` type or
// something to make this properly intrusive.  Until that happens, we
// (!!unsafely) implement MutDeref for Ref<T>.

/* /// Mutable, RwLock-protected reference to an intrusively-refcounted object.
pub struct MutRef<T: ?Sized + RefCounted + Send + Sync>(std::sync::RwLock<*mut T>);

impl<T> Reference<T> for MutRef<T>
where T: RefCounted + Send + Sync {
    fn new(ptr: *mut T) -> MutRef<T> { MutRef(std::sync::RwLock::new(ptr)) }
}

impl<T> Deref for MutRef<T>
    where T: RefCounted + Send + Sync {
    type Target = T;
    fn deref(&self) -> &T {
        if (*self).is_null() { panic!("tried to deref a null pointer") }
        match *self { Ref(p) =>  { let val_ref: &T = unsafe { &*p };
                                   val_ref }
        }
    }
}
impl<T: RefCounted> DerefMut for Ref<T> {
    fn deref_mut(&mut self) -> &mut T {
        if (*self).is_null() { panic!("tried to deref a null pointer") }
        match *self { Ref(p) =>  unsafe { std::mem::transmute(&*p) }
        }
    }
}*/

/* ****************************************************************
 * Ref<T>
 */
/// Immutable reference to an intrusively-refcounted object.
pub struct Ref<T: ?Sized>(RefImpl<T>) where T: RefCounted;

/// Private data type for Ref<T>.
enum RefImpl<T: ?Sized> where T: RefCounted {
    /// Invalid reference.
    Empty,

    /// Simple pointer to a concrete type.
    Pointer(*const T),

    /// A fat pointer/trait object.
    TraitObject(std::raw::TraitObject)
}

impl<T: ?Sized> Reference<T> for Ref<T> where T: RefCounted {
    fn from_trait_obj(obj: &T) -> Self {
        Ref(RefImpl::TraitObject(std::mem::transmute(obj)))
    }

    fn from_obj<U>(obj: &U) -> Ref<T> {
        let ptr: *const T =  unsafe { std::mem::transmute(obj) };
        if ptr == (0 as *const T) { Ref(RefImpl::Empty) }
        else { Ref(RefImpl::Pointer(ptr)) }
    }
}

// impl<T: ?Sized, U> Reference<U> for Ref<T> where T: RefCounted {
//     fn from_trait_obj(obj: &T) -> Self {
//         Ref(RefImpl::TraitObject(std::mem::transmute(obj)))
//     }

//     fn from_obj<U>(obj: &U) -> Ref<T> {
//         let ptr: *const T =  unsafe { std::mem::transmute(obj) };
//         if ptr == (0 as *const T) { Ref(RefImpl::Empty) }
//         else { Ref(RefImpl::Pointer(ptr)) }
//     }
// }

impl<T: ?Sized> Ref<T> where T: RefCounted {
    /// Fetch the reference's data pointer as a `usize`.  This is intended
    /// primarily for implementing hashes over trait objects (the standard
    /// library's Hash is not object-safe) by simply hashing the instance's
    /// data pointer.
    pub fn data_id(&self) -> usize {
        match self.0 {
            RefImpl::Empty => 0,
            RefImpl::Pointer(p) => p as usize,
            RefImpl::TraitObject(ref to) => to.data as usize
        }
    }

    /// Create a null Ref.  This is probably not very safe.
    pub fn null() -> Ref<T> {
        Ref(RefImpl::Empty)
   }

    /// Check if the ref's internal pointer is NULL.
    pub fn is_null(&self) -> bool {
        match self.0 {
            RefImpl::Empty => true,
            _ => false
        }
    }

    /// Get the reference count from the underlying object.
    pub fn get_refcount(&self) -> usize {
        match self.0 {
            RefImpl::Empty => 0,
            _ => self.deref().as_refcounted().get_refcount()
        }
    }

    /// Check if this refers to the same object as another Ref.
    pub fn is_same(&self, other: &Ref<T>) -> bool {
        match self.0 {
            RefImpl::Empty => match other.0 { RefImpl::Empty => true, _ => false },
            RefImpl::Pointer(x) => match other.0 { RefImpl::Pointer(y) => x == y, _ => false },
            RefImpl::TraitObject(ref x) => match other.0 {
                RefImpl::TraitObject(ref y) => { x.data == y.data && x.vtable == y.vtable },
                _ => false },
        }
    }
}

/* ================================================================
 * impl Deref
 */
impl<T: ?Sized> Deref for Ref<T>
where T: RefCounted {
    type Target = T;
    fn deref(&self) -> &T {
        match self.0 {
            RefImpl::Empty => panic!("tried to deref from an empty reference"),
            RefImpl::Pointer(ptr) => unsafe { &*ptr },
            RefImpl::TraitObject(ref obj) => unsafe { std::mem::transmute(obj) }
        }
    }
}

impl<T: ?Sized> DerefMut for Ref<T>
where T: RefCounted {
    fn deref_mut(&mut self) -> &mut T {
        match self.0 {
            RefImpl::Empty => panic!("tried to mutably deref from an empty reference"),
            RefImpl::Pointer(ptr) => unsafe { &mut *std::mem::transmute::<_,*mut T>(ptr) },
            RefImpl::TraitObject(ref obj) => unsafe { std::mem::transmute(obj) }
        }
    }
}

/* ================================================================
 * impl std::borrow::Borrow
 */
impl<T: ?Sized>  Borrow<T> for ::cripes::util::intrusive::Ref<T>
where T: RefCounted {
    fn borrow(&self) -> &T {
        self.deref()
    }
}

// impl<T: ?Sized>  Borrow<::cripes::util::intrusive::Ref<T>> for ::cripes::util::intrusive::Ref<T>
// where T: RefCounted {
//     fn borrow(&self) -> &T {
//         self
//     }
// }

/* ================================================================
 * impl fmt::Display
 */
impl<T: ?Sized> fmt::Display for Ref<T>
where T: RefCounted + fmt::Display {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.0 {
            RefImpl::Pointer(p) => { assert!(! (p == 0 as *const T));
                                     let val: &T = (*self).deref();
                                     val.fmt(f) } }
    }
}

/* ================================================================
 * impl fmt::Debug
 */
impl<T: ?Sized> fmt::Debug for Ref<T>
where T:  RefCounted + fmt::Debug {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let ptr = match self.0 {
            RefImpl::Empty => { return write!(f, "Ref{{Empty}}"); },
            RefImpl::Pointer(p) => p,
            RefImpl::TraitObject(ref obj) => unsafe { std::mem::transmute(obj.data) }};
        let val = (*self).deref();
        try!(write!(f, "Ref {{ 0x{:X}=>", ptr as usize));
        try!(val.fmt(f));
        write!(f, " }}")

    }
}

/* ================================================================
 * impl Clone
 */
impl<T: ?Sized> Clone for Ref<T>
where T: RefCounted {
    fn clone(&self) -> Self {
        match self.0 {
            RefImpl::Empty => Ref(RefImpl::Empty),
            RefImpl::Pointer(p) => Ref(RefImpl::Pointer(p)),
            RefImpl::TraitObject(to) => Ref(RefImpl::TraitObject(to))
        }
    }
}

/* ================================================================
 * impl Drop
 */
#[unsafe_destructor]
impl<T: ?Sized> Drop for Ref<T>
where T: RefCounted {
    fn drop(&mut self) {
        match self.0 {
            RefImpl::Pointer(x) => { assert!((x as usize) != 0);
                                     unsafe {
                                         let _ref =  self.deref_mut().as_refcounted_mut();
                                         if _ref.remove_ref() == 0 {
                                             let boxed = Box::from_raw(mem::transmute::<_,*mut T>(x));
                                             drop(boxed); } } },
            _ => {}
        }
    }
}

/* ================================================================
 * impl Eq, PartialEq<T>, PartialEq<Ref<T>>
 */
impl<T: ?Sized> Eq for Ref<T>
where T: RefCounted + Eq {}

impl<T: ?Sized> PartialEq<T> for Ref<T>
where T: RefCounted + PartialEq<T> {
    fn eq(&self, other: &T) -> bool {
        self.deref() == other
    }
}

impl<T: ?Sized> PartialEq<Ref<T>> for Ref<T>
where T: RefCounted + PartialEq<T> {
    fn eq(&self, other: &Ref<T>) -> bool {
        *self == *other
    }
}

/* ================================================================
 * impl Hash<H>
 */
impl<T: ?Sized> Hash for Ref<T>
where T: RefCounted + Hash {
    fn hash<H: Hasher>(&self, state: &mut H) {
        (*self).deref().hash(state)
    }
}
<commit_msg>util/intrusive: impl `fmt::Display` for all `RefImpl` variants in `Ref<T>`<commit_after>//! Intrusive reference types.
//!
//! Use of intrusively-reference-counted objects with matching reference types
//! over containers like `std::rc::Rc` or `std::sync::Arc` trades some
//! compile-time safety checks (and likely performance benefits) for greater
//! flexibility in how those objects are allocated and used.
//!
//! `Rc` and `Arc` are themselves references to underlying *containers*; any
//! object passed to either's `::new` will be moved to a newly-allocated block
//! on the heap.  To support weak references the data's reference count is
//! stored in another heap-allocated block, which is freed *only when the last
//! reference, weak *or* strong, is dropped*.  Because these types have
//! a reference count stored separately from the data, it is impossible to
//! create a new `Rc`, `Arc`, or `Weak` pointer thereto without one of
//! the same.
//!
//! In contrast, the types defined here forgo support for weak references and
//! strong guarantees of lifetime-management and memory-safety to allow the
//! developer more flexibility in data storage and manipulation.
//!
//! Since reference counts are stored alongside the data (in types
//! implementing [`RefCountHolder`](trait.RefCountHolder.html)),
//! a stack-allocated value can safely provide references to itself as long as
//! its refcount is non-zero prior to its doing so.  they support references
//! to stack-allocated values and values in memory not managed directly the by
//! [`Reference`](trait.Reference.html) implementation.
use std;
use std::fmt;
use std::mem;
use std::ops::{Deref,DerefMut};
use std::ptr::PtrExt;
use std::clone::Clone;
use std::hash::{Hash, Hasher};
use std::borrow::Borrow;

/// Abstract interface for references to intrusively-refcounted values.
pub trait Reference<T: ?Sized> where T: RefCounted, Self: Sized {
    /// Create a reference from a raw pointer.  The reference-type
    /// implementation should do no management of the referand's refcount for
    /// this operation.
    fn from_obj<U>(obj: &U) -> Self;

    /// Create an intrusive reference from a trait object.  The reference-type
    /// implementation should do no management of the referand's refcount for
    /// this operation.
    fn from_trait_obj(obj: &T) -> Self;

    /// Move the given value into the heap, and return a reference to it.  The value
    /// should have a refcount of zero prior to this function.
    ///
    /// # Examples
    ///
    /// ```rust
    /// #[macro_use]
    /// use cripes::util::intrusive;
    /// use cripes::util::intrusive::{Ref,Reference};
    ///
    /// #[derive(Debug)]
    /// struct Dog { refcount: usize }
    /// default_refcounted_impl!(Dog);
    ///
    /// let x: Ref<_> = Reference::new_on_heap(Dog{refcount: 0});   // x is an intrusive reference to the heap
    ///
    /// println!("{:?}", x);
    /// drop(x);                  // dropping the sole reference to that location
    ///                           // will free the memory.
    /// ```
    fn new_on_heap<U>(mut obj: U) -> Self where Self: Sized, U: Sized + RefCounted {
        {
            let rc = obj.as_refcounted_mut();
            // Zero the reference count: we're "taking ownership" of the object.
            while rc.get_refcount() != 0 { rc.remove_ref(); }

            rc.add_ref();
        }

        // Move the object onto the heap using Box, then discard the box in
        // favor of a hot, sticky, unsafe mess.
        let heaped: *const () = unsafe { std::boxed::into_raw(Box::new(obj)) as *const () };

        if std::mem::size_of::<&T>() == std::mem::size_of::<usize>() {
            // standard pointer
            unsafe { Reference::from_obj(&*(heaped as *const U)) }
        } else {
            // trait object
            unsafe { Reference::from_trait_obj(&*(heaped as *const T)) }
        }
    }
}

/// Describes a type that holds a reference count.
pub trait RefCountHolder {
    /// Fetch the object's current reference count.
    fn get_refcount(&self) -> usize;

    /// Increment the object's reference count, returning the new value.
    fn add_ref(&mut self) -> usize;

    /// Decrement the object's reference count, returning the new value.
    fn remove_ref(&mut self) -> usize;
}

pub trait RefCounted {
    // /// Create a reference to the current object.
    // fn ref_to_self<T: ?Sized>(&self) -> Ref<T> where T: RefCounted, Self: T {
    //     Reference::from_raw(unsafe { std::mem::transmute::<_,*const Self>(self) })
    // }

    fn as_refcounted(&self) -> &RefCountHolder;
    fn as_refcounted_mut(&mut self) -> &mut RefCountHolder;
}

impl<T> RefCounted for T where T: RefCountHolder {
    fn as_refcounted(&self) -> &RefCountHolder { self as &RefCountHolder }
    fn as_refcounted_mut(&mut self) -> &mut RefCountHolder { self as &mut RefCountHolder }
}

/// Create a simple implementation of util::intrusive::RefCountHolder for a type.
#[macro_export]
macro_rules! default_refcounted_impl {
    ($target:ident) => (default_refcounted_impl!($target, refcount););

    ($target:ident, $($mbr:ident).*) => (
        impl ::cripes::util::intrusive::RefCountHolder for $target {
            fn get_refcount(&self) -> usize { self.$($mbr).* }
            fn add_ref(&mut self) -> usize { self.$($mbr).* += 1; self.$($mbr).* }
            fn remove_ref(&mut self) -> usize { self.$($mbr).* -= 1; self.$($mbr).* }
        });
}

/* ****************************************************************
 * MutRef<T>
 */
// This is disabled for now -- we'd need a refcounted `RwLock` type or
// something to make this properly intrusive.  Until that happens, we
// (!!unsafely) implement MutDeref for Ref<T>.

/* /// Mutable, RwLock-protected reference to an intrusively-refcounted object.
pub struct MutRef<T: ?Sized + RefCounted + Send + Sync>(std::sync::RwLock<*mut T>);

impl<T> Reference<T> for MutRef<T>
where T: RefCounted + Send + Sync {
    fn new(ptr: *mut T) -> MutRef<T> { MutRef(std::sync::RwLock::new(ptr)) }
}

impl<T> Deref for MutRef<T>
    where T: RefCounted + Send + Sync {
    type Target = T;
    fn deref(&self) -> &T {
        if (*self).is_null() { panic!("tried to deref a null pointer") }
        match *self { Ref(p) =>  { let val_ref: &T = unsafe { &*p };
                                   val_ref }
        }
    }
}
impl<T: RefCounted> DerefMut for Ref<T> {
    fn deref_mut(&mut self) -> &mut T {
        if (*self).is_null() { panic!("tried to deref a null pointer") }
        match *self { Ref(p) =>  unsafe { std::mem::transmute(&*p) }
        }
    }
}*/

/* ****************************************************************
 * Ref<T>
 */
/// Immutable reference to an intrusively-refcounted object.
pub struct Ref<T: ?Sized>(RefImpl<T>) where T: RefCounted;

/// Private data type for Ref<T>.
enum RefImpl<T: ?Sized> where T: RefCounted {
    /// Invalid reference.
    Empty,

    /// Simple pointer to a concrete type.
    Pointer(*const T),

    /// A fat pointer/trait object.
    TraitObject(std::raw::TraitObject)
}

impl<T: ?Sized> Reference<T> for Ref<T> where T: RefCounted {
    fn from_trait_obj(obj: &T) -> Self {
        Ref(RefImpl::TraitObject(std::mem::transmute(obj)))
    }

    fn from_obj<U>(obj: &U) -> Ref<T> {
        let ptr: *const T =  unsafe { std::mem::transmute(obj) };
        if ptr == (0 as *const T) { Ref(RefImpl::Empty) }
        else { Ref(RefImpl::Pointer(ptr)) }
    }
}

// impl<T: ?Sized, U> Reference<U> for Ref<T> where T: RefCounted {
//     fn from_trait_obj(obj: &T) -> Self {
//         Ref(RefImpl::TraitObject(std::mem::transmute(obj)))
//     }

//     fn from_obj<U>(obj: &U) -> Ref<T> {
//         let ptr: *const T =  unsafe { std::mem::transmute(obj) };
//         if ptr == (0 as *const T) { Ref(RefImpl::Empty) }
//         else { Ref(RefImpl::Pointer(ptr)) }
//     }
// }

impl<T: ?Sized> Ref<T> where T: RefCounted {
    /// Fetch the reference's data pointer as a `usize`.  This is intended
    /// primarily for implementing hashes over trait objects (the standard
    /// library's Hash is not object-safe) by simply hashing the instance's
    /// data pointer.
    pub fn data_id(&self) -> usize {
        match self.0 {
            RefImpl::Empty => 0,
            RefImpl::Pointer(p) => p as usize,
            RefImpl::TraitObject(ref to) => to.data as usize
        }
    }

    /// Create a null Ref.  This is probably not very safe.
    pub fn null() -> Ref<T> {
        Ref(RefImpl::Empty)
   }

    /// Check if the ref's internal pointer is NULL.
    pub fn is_null(&self) -> bool {
        match self.0 {
            RefImpl::Empty => true,
            _ => false
        }
    }

    /// Get the reference count from the underlying object.
    pub fn get_refcount(&self) -> usize {
        match self.0 {
            RefImpl::Empty => 0,
            _ => self.deref().as_refcounted().get_refcount()
        }
    }

    /// Check if this refers to the same object as another Ref.
    pub fn is_same(&self, other: &Ref<T>) -> bool {
        match self.0 {
            RefImpl::Empty => match other.0 { RefImpl::Empty => true, _ => false },
            RefImpl::Pointer(x) => match other.0 { RefImpl::Pointer(y) => x == y, _ => false },
            RefImpl::TraitObject(ref x) => match other.0 {
                RefImpl::TraitObject(ref y) => { x.data == y.data && x.vtable == y.vtable },
                _ => false },
        }
    }
}

/* ================================================================
 * impl Deref
 */
impl<T: ?Sized> Deref for Ref<T>
where T: RefCounted {
    type Target = T;
    fn deref(&self) -> &T {
        match self.0 {
            RefImpl::Empty => panic!("tried to deref from an empty reference"),
            RefImpl::Pointer(ptr) => unsafe { &*ptr },
            RefImpl::TraitObject(ref obj) => unsafe { std::mem::transmute(obj) }
        }
    }
}

impl<T: ?Sized> DerefMut for Ref<T>
where T: RefCounted {
    fn deref_mut(&mut self) -> &mut T {
        match self.0 {
            RefImpl::Empty => panic!("tried to mutably deref from an empty reference"),
            RefImpl::Pointer(ptr) => unsafe { &mut *std::mem::transmute::<_,*mut T>(ptr) },
            RefImpl::TraitObject(ref obj) => unsafe { std::mem::transmute(obj) }
        }
    }
}

/* ================================================================
 * impl std::borrow::Borrow
 */
impl<T: ?Sized>  Borrow<T> for ::cripes::util::intrusive::Ref<T>
where T: RefCounted {
    fn borrow(&self) -> &T {
        self.deref()
    }
}

// impl<T: ?Sized>  Borrow<::cripes::util::intrusive::Ref<T>> for ::cripes::util::intrusive::Ref<T>
// where T: RefCounted {
//     fn borrow(&self) -> &T {
//         self
//     }
// }

/* ================================================================
 * impl fmt::Display
 */
impl<T: ?Sized> fmt::Display for Ref<T>
where T: RefCounted + fmt::Display {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.0 {
            RefImpl::Empty => write!(f, ""),
            RefImpl::Pointer(p) => { assert!(! (p == 0 as *const T));
                                     let val: &T = (*self).deref();
                                     val.fmt(f) },
            RefImpl::TraitObject(to) => { let x: &T = unsafe { std::mem::transmute(to) };
                                          x.fmt(f) }
        }
    }
}

/* ================================================================
 * impl fmt::Debug
 */
impl<T: ?Sized> fmt::Debug for Ref<T>
where T:  RefCounted + fmt::Debug {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let ptr = match self.0 {
            RefImpl::Empty => { return write!(f, "Ref{{Empty}}"); },
            RefImpl::Pointer(p) => p,
            RefImpl::TraitObject(ref obj) => unsafe { std::mem::transmute(obj.data) }};
        let val = (*self).deref();
        try!(write!(f, "Ref {{ 0x{:X}=>", ptr as usize));
        try!(val.fmt(f));
        write!(f, " }}")

    }
}

/* ================================================================
 * impl Clone
 */
impl<T: ?Sized> Clone for Ref<T>
where T: RefCounted {
    fn clone(&self) -> Self {
        match self.0 {
            RefImpl::Empty => Ref(RefImpl::Empty),
            RefImpl::Pointer(p) => Ref(RefImpl::Pointer(p)),
            RefImpl::TraitObject(to) => Ref(RefImpl::TraitObject(to))
        }
    }
}

/* ================================================================
 * impl Drop
 */
#[unsafe_destructor]
impl<T: ?Sized> Drop for Ref<T>
where T: RefCounted {
    fn drop(&mut self) {
        match self.0 {
            RefImpl::Pointer(x) => { assert!((x as usize) != 0);
                                     unsafe {
                                         let _ref =  self.deref_mut().as_refcounted_mut();
                                         if _ref.remove_ref() == 0 {
                                             let boxed = Box::from_raw(mem::transmute::<_,*mut T>(x));
                                             drop(boxed); } } },
            _ => {}
        }
    }
}

/* ================================================================
 * impl Eq, PartialEq<T>, PartialEq<Ref<T>>
 */
impl<T: ?Sized> Eq for Ref<T>
where T: RefCounted + Eq {}

impl<T: ?Sized> PartialEq<T> for Ref<T>
where T: RefCounted + PartialEq<T> {
    fn eq(&self, other: &T) -> bool {
        self.deref() == other
    }
}

impl<T: ?Sized> PartialEq<Ref<T>> for Ref<T>
where T: RefCounted + PartialEq<T> {
    fn eq(&self, other: &Ref<T>) -> bool {
        *self == *other
    }
}

/* ================================================================
 * impl Hash<H>
 */
impl<T: ?Sized> Hash for Ref<T>
where T: RefCounted + Hash {
    fn hash<H: Hasher>(&self, state: &mut H) {
        (*self).deref().hash(state)
    }
}
<|endoftext|>


<commit_before>/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Maciej Dziardziel <fiedzia@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE file
 * that was distributed with this source code.
 */

pub static DEFAULT_SIGNAL: usize = 15;

pub struct Signal<'a> {
    pub name: &'a str,
    pub value: usize,
}

/*

Linux Programmer's Manual

 1 HUP      2 INT      3 QUIT     4 ILL      5 TRAP     6 ABRT     7 BUS
 8 FPE      9 KILL    10 USR1    11 SEGV    12 USR2    13 PIPE    14 ALRM
15 TERM    16 STKFLT  17 CHLD    18 CONT    19 STOP    20 TSTP    21 TTIN
22 TTOU    23 URG     24 XCPU    25 XFSZ    26 VTALRM  27 PROF    28 WINCH
29 POLL    30 PWR     31 SYS

*/

#[cfg(target_os = "linux")]
pub static ALL_SIGNALS: [Signal<'static>; 31] = [
    Signal {
        name: "HUP",
        value: 1,
    },
    Signal {
        name: "INT",
        value: 2,
    },
    Signal {
        name: "QUIT",
        value: 3,
    },
    Signal {
        name: "ILL",
        value: 4,
    },
    Signal {
        name: "TRAP",
        value: 5,
    },
    Signal {
        name: "ABRT",
        value: 6,
    },
    Signal {
        name: "BUS",
        value: 7,
    },
    Signal {
        name: "FPE",
        value: 8,
    },
    Signal {
        name: "KILL",
        value: 9,
    },
    Signal {
        name: "USR1",
        value: 10,
    },
    Signal {
        name: "SEGV",
        value: 11,
    },
    Signal {
        name: "USR2",
        value: 12,
    },
    Signal {
        name: "PIPE",
        value: 13,
    },
    Signal {
        name: "ALRM",
        value: 14,
    },
    Signal {
        name: "TERM",
        value: 15,
    },
    Signal {
        name: "STKFLT",
        value: 16,
    },
    Signal {
        name: "CHLD",
        value: 17,
    },
    Signal {
        name: "CONT",
        value: 18,
    },
    Signal {
        name: "STOP",
        value: 19,
    },
    Signal {
        name: "TSTP",
        value: 20,
    },
    Signal {
        name: "TTIN",
        value: 21,
    },
    Signal {
        name: "TTOU",
        value: 22,
    },
    Signal {
        name: "URG",
        value: 23,
    },
    Signal {
        name: "XCPU",
        value: 24,
    },
    Signal {
        name: "XFSZ",
        value: 25,
    },
    Signal {
        name: "VTALRM",
        value: 26,
    },
    Signal {
        name: "PROF",
        value: 27,
    },
    Signal {
        name: "WINCH",
        value: 28,
    },
    Signal {
        name: "POLL",
        value: 29,
    },
    Signal {
        name: "PWR",
        value: 30,
    },
    Signal {
        name: "SYS",
        value: 31,
    },
];

/*

https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/signal.3.html

No    Name         Default Action       Description
1     SIGHUP       terminate process    terminal line hangup
2     SIGINT       terminate process    interrupt program
3     SIGQUIT      create core image    quit program
4     SIGILL       create core image    illegal instruction
5     SIGTRAP      create core image    trace trap
6     SIGABRT      create core image    abort program (formerly SIGIOT)
7     SIGEMT       create core image    emulate instruction executed
8     SIGFPE       create core image    floating-point exception
9     SIGKILL      terminate process    kill program
10    SIGBUS       create core image    bus error
11    SIGSEGV      create core image    segmentation violation
12    SIGSYS       create core image    non-existent system call invoked
13    SIGPIPE      terminate process    write on a pipe with no reader
14    SIGALRM      terminate process    real-time timer expired
15    SIGTERM      terminate process    software termination signal
16    SIGURG       discard signal       urgent condition present on socket
17    SIGSTOP      stop process         stop (cannot be caught or ignored)
18    SIGTSTP      stop process         stop signal generated from keyboard
19    SIGCONT      discard signal       continue after stop
20    SIGCHLD      discard signal       child status has changed
21    SIGTTIN      stop process         background read attempted from control terminal
22    SIGTTOU      stop process         background write attempted to control terminal
23    SIGIO        discard signal       I/O is possible on a descriptor (see fcntl(2))
24    SIGXCPU      terminate process    cpu time limit exceeded (see setrlimit(2))
25    SIGXFSZ      terminate process    file size limit exceeded (see setrlimit(2))
26    SIGVTALRM    terminate process    virtual time alarm (see setitimer(2))
27    SIGPROF      terminate process    profiling timer alarm (see setitimer(2))
28    SIGWINCH     discard signal       Window size change
29    SIGINFO      discard signal       status request from keyboard
30    SIGUSR1      terminate process    User defined signal 1
31    SIGUSR2      terminate process    User defined signal 2

*/

#[cfg(any(target_os = "macos", target_os = "freebsd"))]
pub static ALL_SIGNALS: [Signal<'static>; 31] = [
    Signal {
        name: "HUP",
        value: 1,
    },
    Signal {
        name: "INT",
        value: 2,
    },
    Signal {
        name: "QUIT",
        value: 3,
    },
    Signal {
        name: "ILL",
        value: 4,
    },
    Signal {
        name: "TRAP",
        value: 5,
    },
    Signal {
        name: "ABRT",
        value: 6,
    },
    Signal {
        name: "EMT",
        value: 7,
    },
    Signal {
        name: "FPE",
        value: 8,
    },
    Signal {
        name: "KILL",
        value: 9,
    },
    Signal {
        name: "BUS",
        value: 10,
    },
    Signal {
        name: "SEGV",
        value: 11,
    },
    Signal {
        name: "SYS",
        value: 12,
    },
    Signal {
        name: "PIPE",
        value: 13,
    },
    Signal {
        name: "ALRM",
        value: 14,
    },
    Signal {
        name: "TERM",
        value: 15,
    },
    Signal {
        name: "URG",
        value: 16,
    },
    Signal {
        name: "STOP",
        value: 17,
    },
    Signal {
        name: "TSTP",
        value: 18,
    },
    Signal {
        name: "CONT",
        value: 19,
    },
    Signal {
        name: "CHLD",
        value: 20,
    },
    Signal {
        name: "TTIN",
        value: 21,
    },
    Signal {
        name: "TTOU",
        value: 22,
    },
    Signal {
        name: "IO",
        value: 23,
    },
    Signal {
        name: "XCPU",
        value: 24,
    },
    Signal {
        name: "XFSZ",
        value: 25,
    },
    Signal {
        name: "VTALRM",
        value: 26,
    },
    Signal {
        name: "PROF",
        value: 27,
    },
    Signal {
        name: "WINCH",
        value: 28,
    },
    Signal {
        name: "INFO",
        value: 29,
    },
    Signal {
        name: "USR1",
        value: 30,
    },
    Signal {
        name: "USR2",
        value: 31,
    },
];

pub fn signal_by_name_or_value(signal_name_or_value: &str) -> Option<usize> {
    if signal_name_or_value == "0" {
        return Some(0);
    }
    for signal in &ALL_SIGNALS {
        let long_name = format!("SIG{}", signal.name);
        if signal.name == signal_name_or_value || (signal_name_or_value == signal.value.to_string())
            || (long_name == signal_name_or_value)
        {
            return Some(signal.value);
        }
    }
    None
}

#[inline(always)]
pub fn is_signal(num: usize) -> bool {
    num < ALL_SIGNALS.len()
}

#[test]
fn signals_all_contiguous() {
    for (i, signal) in ALL_SIGNALS.iter().enumerate() {
        assert_eq!(signal.value, i + 1);
    }
}

#[test]
fn signals_all_are_signal() {
    for signal in &ALL_SIGNALS {
        assert!(is_signal(signal.value));
    }
}

#[test]
fn signal_by_value() {
    assert_eq!(signal_by_name_or_value("0"), Some(0));
    for signal in &ALL_SIGNALS {
        assert_eq!(signal_by_name_or_value(&signal.value.to_string()), Some(signal.value));
    }
}

#[test]
fn signal_by_short_name() {
    for signal in &ALL_SIGNALS {
        assert_eq!(signal_by_name_or_value(signal.name), Some(signal.value));
    }
}

#[test]
fn signal_by_long_name() {
    for signal in &ALL_SIGNALS {
        assert_eq!(signal_by_name_or_value(&format!("SIG{}", signal.name)), Some(signal.value));
    }
}
<commit_msg>Fix is_signal<commit_after>/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Maciej Dziardziel <fiedzia@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE file
 * that was distributed with this source code.
 */

pub static DEFAULT_SIGNAL: usize = 15;

pub struct Signal<'a> {
    pub name: &'a str,
    pub value: usize,
}

/*

Linux Programmer's Manual

 1 HUP      2 INT      3 QUIT     4 ILL      5 TRAP     6 ABRT     7 BUS
 8 FPE      9 KILL    10 USR1    11 SEGV    12 USR2    13 PIPE    14 ALRM
15 TERM    16 STKFLT  17 CHLD    18 CONT    19 STOP    20 TSTP    21 TTIN
22 TTOU    23 URG     24 XCPU    25 XFSZ    26 VTALRM  27 PROF    28 WINCH
29 POLL    30 PWR     31 SYS

*/

#[cfg(target_os = "linux")]
pub static ALL_SIGNALS: [Signal<'static>; 31] = [
    Signal {
        name: "HUP",
        value: 1,
    },
    Signal {
        name: "INT",
        value: 2,
    },
    Signal {
        name: "QUIT",
        value: 3,
    },
    Signal {
        name: "ILL",
        value: 4,
    },
    Signal {
        name: "TRAP",
        value: 5,
    },
    Signal {
        name: "ABRT",
        value: 6,
    },
    Signal {
        name: "BUS",
        value: 7,
    },
    Signal {
        name: "FPE",
        value: 8,
    },
    Signal {
        name: "KILL",
        value: 9,
    },
    Signal {
        name: "USR1",
        value: 10,
    },
    Signal {
        name: "SEGV",
        value: 11,
    },
    Signal {
        name: "USR2",
        value: 12,
    },
    Signal {
        name: "PIPE",
        value: 13,
    },
    Signal {
        name: "ALRM",
        value: 14,
    },
    Signal {
        name: "TERM",
        value: 15,
    },
    Signal {
        name: "STKFLT",
        value: 16,
    },
    Signal {
        name: "CHLD",
        value: 17,
    },
    Signal {
        name: "CONT",
        value: 18,
    },
    Signal {
        name: "STOP",
        value: 19,
    },
    Signal {
        name: "TSTP",
        value: 20,
    },
    Signal {
        name: "TTIN",
        value: 21,
    },
    Signal {
        name: "TTOU",
        value: 22,
    },
    Signal {
        name: "URG",
        value: 23,
    },
    Signal {
        name: "XCPU",
        value: 24,
    },
    Signal {
        name: "XFSZ",
        value: 25,
    },
    Signal {
        name: "VTALRM",
        value: 26,
    },
    Signal {
        name: "PROF",
        value: 27,
    },
    Signal {
        name: "WINCH",
        value: 28,
    },
    Signal {
        name: "POLL",
        value: 29,
    },
    Signal {
        name: "PWR",
        value: 30,
    },
    Signal {
        name: "SYS",
        value: 31,
    },
];

/*

https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/signal.3.html

No    Name         Default Action       Description
1     SIGHUP       terminate process    terminal line hangup
2     SIGINT       terminate process    interrupt program
3     SIGQUIT      create core image    quit program
4     SIGILL       create core image    illegal instruction
5     SIGTRAP      create core image    trace trap
6     SIGABRT      create core image    abort program (formerly SIGIOT)
7     SIGEMT       create core image    emulate instruction executed
8     SIGFPE       create core image    floating-point exception
9     SIGKILL      terminate process    kill program
10    SIGBUS       create core image    bus error
11    SIGSEGV      create core image    segmentation violation
12    SIGSYS       create core image    non-existent system call invoked
13    SIGPIPE      terminate process    write on a pipe with no reader
14    SIGALRM      terminate process    real-time timer expired
15    SIGTERM      terminate process    software termination signal
16    SIGURG       discard signal       urgent condition present on socket
17    SIGSTOP      stop process         stop (cannot be caught or ignored)
18    SIGTSTP      stop process         stop signal generated from keyboard
19    SIGCONT      discard signal       continue after stop
20    SIGCHLD      discard signal       child status has changed
21    SIGTTIN      stop process         background read attempted from control terminal
22    SIGTTOU      stop process         background write attempted to control terminal
23    SIGIO        discard signal       I/O is possible on a descriptor (see fcntl(2))
24    SIGXCPU      terminate process    cpu time limit exceeded (see setrlimit(2))
25    SIGXFSZ      terminate process    file size limit exceeded (see setrlimit(2))
26    SIGVTALRM    terminate process    virtual time alarm (see setitimer(2))
27    SIGPROF      terminate process    profiling timer alarm (see setitimer(2))
28    SIGWINCH     discard signal       Window size change
29    SIGINFO      discard signal       status request from keyboard
30    SIGUSR1      terminate process    User defined signal 1
31    SIGUSR2      terminate process    User defined signal 2

*/

#[cfg(any(target_os = "macos", target_os = "freebsd"))]
pub static ALL_SIGNALS: [Signal<'static>; 31] = [
    Signal {
        name: "HUP",
        value: 1,
    },
    Signal {
        name: "INT",
        value: 2,
    },
    Signal {
        name: "QUIT",
        value: 3,
    },
    Signal {
        name: "ILL",
        value: 4,
    },
    Signal {
        name: "TRAP",
        value: 5,
    },
    Signal {
        name: "ABRT",
        value: 6,
    },
    Signal {
        name: "EMT",
        value: 7,
    },
    Signal {
        name: "FPE",
        value: 8,
    },
    Signal {
        name: "KILL",
        value: 9,
    },
    Signal {
        name: "BUS",
        value: 10,
    },
    Signal {
        name: "SEGV",
        value: 11,
    },
    Signal {
        name: "SYS",
        value: 12,
    },
    Signal {
        name: "PIPE",
        value: 13,
    },
    Signal {
        name: "ALRM",
        value: 14,
    },
    Signal {
        name: "TERM",
        value: 15,
    },
    Signal {
        name: "URG",
        value: 16,
    },
    Signal {
        name: "STOP",
        value: 17,
    },
    Signal {
        name: "TSTP",
        value: 18,
    },
    Signal {
        name: "CONT",
        value: 19,
    },
    Signal {
        name: "CHLD",
        value: 20,
    },
    Signal {
        name: "TTIN",
        value: 21,
    },
    Signal {
        name: "TTOU",
        value: 22,
    },
    Signal {
        name: "IO",
        value: 23,
    },
    Signal {
        name: "XCPU",
        value: 24,
    },
    Signal {
        name: "XFSZ",
        value: 25,
    },
    Signal {
        name: "VTALRM",
        value: 26,
    },
    Signal {
        name: "PROF",
        value: 27,
    },
    Signal {
        name: "WINCH",
        value: 28,
    },
    Signal {
        name: "INFO",
        value: 29,
    },
    Signal {
        name: "USR1",
        value: 30,
    },
    Signal {
        name: "USR2",
        value: 31,
    },
];

pub fn signal_by_name_or_value(signal_name_or_value: &str) -> Option<usize> {
    if signal_name_or_value == "0" {
        return Some(0);
    }
    for signal in &ALL_SIGNALS {
        let long_name = format!("SIG{}", signal.name);
        if signal.name == signal_name_or_value || (signal_name_or_value == signal.value.to_string())
            || (long_name == signal_name_or_value)
        {
            return Some(signal.value);
        }
    }
    None
}

#[inline(always)]
pub fn is_signal(num: usize) -> bool {
    // Named signals start at 1
    num <= ALL_SIGNALS.len()
}

#[test]
fn signals_all_contiguous() {
    for (i, signal) in ALL_SIGNALS.iter().enumerate() {
        assert_eq!(signal.value, i + 1);
    }
}

#[test]
fn signals_all_are_signal() {
    for signal in &ALL_SIGNALS {
        assert!(is_signal(signal.value));
    }
}

#[test]
fn signal_by_value() {
    assert_eq!(signal_by_name_or_value("0"), Some(0));
    for signal in &ALL_SIGNALS {
        assert_eq!(signal_by_name_or_value(&signal.value.to_string()), Some(signal.value));
    }
}

#[test]
fn signal_by_short_name() {
    for signal in &ALL_SIGNALS {
        assert_eq!(signal_by_name_or_value(signal.name), Some(signal.value));
    }
}

#[test]
fn signal_by_long_name() {
    for signal in &ALL_SIGNALS {
        assert_eq!(signal_by_name_or_value(&format!("SIG{}", signal.name)), Some(signal.value));
    }
}
<|endoftext|>


<commit_before>/* Command Line Argument Parser */
#[macro_use]
extern crate clap;
use clap::AppSettings::SubcommandRequiredElseHelp;

/* For serialization/deserialization of the timesheet */
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

/* For parsing time strings */
#[macro_use]
extern crate nom;

/* For time handling */
extern crate chrono;

/* To open link to report in browser */
extern crate url_open;
extern crate url;

/* For process termination */
use std::process;

use util::{get_seconds, parse_hhmm_to_seconds, set_to_trk_dir, git_commit_trk};

use timesheet::timesheet::Timesheet;

mod util;
mod timesheet;
mod config;

fn main() {
    /* Handle command line arguments with clap */
    let arguments = clap_app!(trk =>
        (setting: SubcommandRequiredElseHelp)
        (version: "0.9")
        (author: "Rafael B. <mediumendian@gmail.com>")
        (about: "Create timesheets from git history and meta info")
            /* (@arg CONFIG: -c --config +takes_value "[UNUSED] Sets a custom config file") */
            /* (@arg debug: -d ... "[UNUSED] Sets the level of debugging information") */

            (@subcommand init =>
                (about: "Initialise trk in this directory and give name (should match git user name)")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
                (@arg name: "Optional: user name. Default is git user name if set, empty otherwise.")
            )
            (@subcommand begin =>
                (about: "Begin session")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
                (@arg ago: "Optional: begin in the past, specify how long ago.
                    Time must be after the last event though.")
            )
            (@subcommand end =>
                (about: "End session")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
                (@arg ago: "Optional: end in the past, specify how long ago.
                    Time must be after the last event though.")
                )
            (@subcommand pause =>
                (about: "Pause current session")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg note_text: "Optional: Pause note")
                (@arg ago: "Optional: pause in the past, specify how long ago.
                    Time must be after the last event though.")
            )
            (@subcommand resume =>
                (about: "Resume currently paused session")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg ago: "Optional: resume in the past, specify how long ago.
                    Time must be after the last event though.")
            )
            (@subcommand note =>
                (about: "Add a note about current work or pause")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg note_text: +required "Note text")
                (@arg ago: "Optional: Add a note in the past, specify how long ago.
                    Time must be after the last event though.")
            )
            (@subcommand commit =>
                (about: "Add a commit to the event list")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg hash: +required "Commit hash id")
            )
            (@subcommand branch =>
                (about: "Add a branch to the session's branch list")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg name: +required "branch name")
            )
            (@subcommand set_show_commits =>
                    (about: "Show information about git commits/branches in the report")
                    (version: "0.1")
                    (author: "mediumendian@gmail.com")
                    (@arg on_off: +required "on or off")
            )
            (@subcommand set_repo_url =>
                    (about: "Set git repo url to use for turning commit hashes to links")
                    (version: "0.1")
                    (author: "mediumendian@gmail.com")
                    (@arg url: +required "url to repository")
            )
            (@subcommand status =>
                (about: "Prints the current WIP for session or sheet")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg sheet_or_session: +required "session or sheet")
            )
            (@subcommand report =>
                (about:
"Generate html report for current session or entire sheet and save it to {timesheet|session}.html")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg sheet_or_session: +required "session or sheet")
                (@arg ago: "How long the record should go back")
            )
            (@subcommand clear =>
                (about: "Temporary: clears all sessions and updates all timestamps")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
            )
       )
            .get_matches();

    let sheet = Timesheet::load_from_file();

    /* Gets a value for config if supplied by user, or defaults to "default.conf" */
    /* let config = matches.value_of("config").unwrap_or("default.conf");
    println!("[UNUSED] Value for config: {}", config); */

    /* Special case for init because t_sheet can and should be None before initialisation
     * Also, check for .trk directory only after this */
    if let Some(command) = arguments.subcommand_matches("init") {
        match sheet {
            Some(..) => println!("Already initialised."),
            None => {
                match Timesheet::init(command.value_of("name")) {
                    Some(..) => {
                        println!("Init successful.");
                        git_commit_trk("initialise trk");
                    }
                    None => println!("Could not initialize."),
                }
            }
        }
        return;
    }

    /* Set current dir to the next upper directory containing a .trk directory */
    if !set_to_trk_dir() {
        println!("Fatal: not a .trk directory (or subdirectory of one).");
        process::exit(0);
    }

   /* Special case for clear because t_sheet can be None when clearing (corrupt file) */
    if let Some(command) = arguments.subcommand_matches("clear") {
        match sheet {
            Some(..) => {
                println!("Clearing timesheet.");
                Timesheet::clear();
                git_commit_trk("Cleared timesheet");
            }
            None => {
                match Timesheet::init(command.value_of("name")) {
                    Some(..) => {
                        println!("Reinitialised timesheet.");
                        git_commit_trk("Reinitialised timesheet.");
                    }
                    None => println!("Could not initialize."),
                }
            }
        }
        return;
    }

    /* Ignore commit or branch on uninitialised trk,
     * which occur when post-commit/post-checkout hooks run
     */
    if arguments.subcommand_matches("commit").is_some() ||
       arguments.subcommand_matches("branch").is_some() {
        match sheet {
            Some(..) => {}
            None => process::exit(0),
        }
    }

    /* Variable to hold git commit message */
    let mut message = "No commit message. This is an error!";
    /* Unwrap the timesheet and continue only if timesheet file exists */
    let mut sheet = match sheet {
        Some(file) => file,
        None => {
            println!("No timesheet file! You might have to init first.");
            return;
        }
    };

    match arguments.subcommand() {
        ("begin", Some(arg)) => {
            let timestamp: Option<u64> = parse_hhmm_to_seconds(arg.value_of("ago").unwrap_or(""))
                .map(|ago| get_seconds() - ago);
            sheet.new_session(timestamp);
            message = "begin new session";
        }
        ("end", Some(arg)) => {
            let timestamp: Option<u64> = parse_hhmm_to_seconds(arg.value_of("ago").unwrap_or(""))
                .map(|ago| get_seconds() - ago);
            sheet.end_session(timestamp);
            message = "end session";
        }
        ("pause", Some(arg)) => {
            let timestamp: Option<u64> = parse_hhmm_to_seconds(arg.value_of("ago").unwrap_or(""))
                .map(|ago| get_seconds() - ago);
            let note_text = arg.value_of("note_text");
            match note_text {
                Some(note_text) => sheet.pause(timestamp, Some(note_text.to_string())),
                None => sheet.pause(timestamp, None),
            }
            message = "pause session";
        }

        ("resume", Some(arg)) => {
            let timestamp: Option<u64> = parse_hhmm_to_seconds(arg.value_of("ago").unwrap_or(""))
                .map(|ago| get_seconds() - ago);
            sheet.resume(timestamp);
            message = "resume session";
        }
        ("note", Some(arg)) => {
            let timestamp: Option<u64> = parse_hhmm_to_seconds(arg.value_of("ago").unwrap_or(""))
                .map(|ago| get_seconds() - ago);
            let note_text = arg.value_of("note_text").unwrap();
            sheet.note(timestamp, note_text.to_string());
            message = "add note to session";
        }
        ("commit", Some(arg)) => {
            let commit_hash = arg.value_of("hash").unwrap();
            sheet.add_commit(commit_hash.to_string());
            message = "add commit to session";
        }
        ("branch", Some(arg)) => {
            let branch_name = arg.value_of("name").unwrap();
            sheet.add_branch(branch_name.to_string());
            message = "add branch to branchlist";
        }
        ("status", Some(arg)) => {
            match arg.value_of("sheet_or_session") {
                Some("session") => println!("{}", sheet.last_session_status()),
                Some("sheet") => println!("{}", sheet.timesheet_status()),
                Some(text) => {
                    println!("What do you mean by {}? Should be either 'sheet' or 'session'.",
                             text)
                }
                _ => unreachable!(),
            }
        }
        ("report", Some(arg)) => {
            match arg.value_of("sheet_or_session") {
                Some("session") => sheet.report_last_session(),
                Some("sheet") => {
                    let timestamp: Option<u64> =
                        parse_hhmm_to_seconds(arg.value_of("ago").unwrap_or(""))
                        .map(|ago| get_seconds() - ago);
                    sheet.report_sheet(timestamp);
                }
                Some(text) => {
                    println!("What do you mean by {}? Should be either 'sheet' or 'session'.",
                             text)
                }
                _ => unreachable!(),
            }
        }
        ("set_show_commits", Some(arg)) => {
            match arg.value_of("on_off") {
                Some("on") => sheet.show_commits(true),
                Some("off") => sheet.show_commits(false),
                Some(text) => {
                    println!("What do you mean by {}? Should be either 'on' or 'off'.",
                             text)
                }
                _ => unreachable!(),
            }
        }
        ("set_repo_url", Some(arg)) => {
            match arg.value_of("url") {
                Some(repo_url) => sheet.set_repo_url(repo_url.to_string()),
                None => println!("Could not parse argument of trk set repo_url."),
            }
        }
        _ => unreachable!(),
    }
    sheet.write_files();
    git_commit_trk(message);
}
<commit_msg>return if files should not be touched and commits should not happen<commit_after>/* Command Line Argument Parser */
#[macro_use]
extern crate clap;
use clap::AppSettings::SubcommandRequiredElseHelp;

/* For serialization/deserialization of the timesheet */
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

/* For parsing time strings */
#[macro_use]
extern crate nom;

/* For time handling */
extern crate chrono;

/* To open link to report in browser */
extern crate url_open;
extern crate url;

/* For process termination */
use std::process;

use util::{get_seconds, parse_hhmm_to_seconds, set_to_trk_dir, git_commit_trk};

use timesheet::timesheet::Timesheet;

mod util;
mod timesheet;
mod config;

fn main() {
    /* Handle command line arguments with clap */
    let arguments = clap_app!(trk =>
        (setting: SubcommandRequiredElseHelp)
        (version: "0.9")
        (author: "Rafael B. <mediumendian@gmail.com>")
        (about: "Create timesheets from git history and meta info")
            /* (@arg CONFIG: -c --config +takes_value "[UNUSED] Sets a custom config file") */
            /* (@arg debug: -d ... "[UNUSED] Sets the level of debugging information") */

            (@subcommand init =>
                (about: "Initialise trk in this directory and give name (should match git user name)")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
                (@arg name: "Optional: user name. Default is git user name if set, empty otherwise.")
            )
            (@subcommand begin =>
                (about: "Begin session")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
                (@arg ago: "Optional: begin in the past, specify how long ago.
                    Time must be after the last event though.")
            )
            (@subcommand end =>
                (about: "End session")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
                (@arg ago: "Optional: end in the past, specify how long ago.
                    Time must be after the last event though.")
                )
            (@subcommand pause =>
                (about: "Pause current session")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg note_text: "Optional: Pause note")
                (@arg ago: "Optional: pause in the past, specify how long ago.
                    Time must be after the last event though.")
            )
            (@subcommand resume =>
                (about: "Resume currently paused session")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg ago: "Optional: resume in the past, specify how long ago.
                    Time must be after the last event though.")
            )
            (@subcommand note =>
                (about: "Add a note about current work or pause")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg note_text: +required "Note text")
                (@arg ago: "Optional: Add a note in the past, specify how long ago.
                    Time must be after the last event though.")
            )
            (@subcommand commit =>
                (about: "Add a commit to the event list")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg hash: +required "Commit hash id")
            )
            (@subcommand branch =>
                (about: "Add a branch to the session's branch list")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg name: +required "branch name")
            )
            (@subcommand set_show_commits =>
                    (about: "Show information about git commits/branches in the report")
                    (version: "0.1")
                    (author: "mediumendian@gmail.com")
                    (@arg on_off: +required "on or off")
            )
            (@subcommand set_repo_url =>
                    (about: "Set git repo url to use for turning commit hashes to links")
                    (version: "0.1")
                    (author: "mediumendian@gmail.com")
                    (@arg url: +required "url to repository")
            )
            (@subcommand status =>
                (about: "Prints the current WIP for session or sheet")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg sheet_or_session: +required "session or sheet")
            )
            (@subcommand report =>
                (about:
"Generate html report for current session or entire sheet and save it to {timesheet|session}.html")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg sheet_or_session: +required "session or sheet")
                (@arg ago: "How long the record should go back")
            )
            (@subcommand clear =>
                (about: "Temporary: clears all sessions and updates all timestamps")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
            )
       )
            .get_matches();

    let sheet = Timesheet::load_from_file();

    /* Gets a value for config if supplied by user, or defaults to "default.conf" */
    /* let config = matches.value_of("config").unwrap_or("default.conf");
    println!("[UNUSED] Value for config: {}", config); */

    /* Special case for init because t_sheet can and should be None before initialisation
     * Also, check for .trk directory only after this */
    if let Some(command) = arguments.subcommand_matches("init") {
        match sheet {
            Some(..) => println!("Already initialised."),
            None => {
                match Timesheet::init(command.value_of("name")) {
                    Some(..) => {
                        println!("Init successful.");
                        git_commit_trk("initialise trk");
                    }
                    None => println!("Could not initialize."),
                }
            }
        }
        return;
    }

    /* Set current dir to the next upper directory containing a .trk directory */
    if !set_to_trk_dir() {
        println!("Fatal: not a .trk directory (or subdirectory of one).");
        process::exit(0);
    }

   /* Special case for clear because t_sheet can be None when clearing (corrupt file) */
    if let Some(command) = arguments.subcommand_matches("clear") {
        match sheet {
            Some(..) => {
                println!("Clearing timesheet.");
                Timesheet::clear();
                git_commit_trk("Cleared timesheet");
            }
            None => {
                match Timesheet::init(command.value_of("name")) {
                    Some(..) => {
                        println!("Reinitialised timesheet.");
                        git_commit_trk("Reinitialised timesheet.");
                    }
                    None => println!("Could not initialize."),
                }
            }
        }
        return;
    }

    /* Ignore commit or branch on uninitialised trk,
     * which occur when post-commit/post-checkout hooks run
     */
    if arguments.subcommand_matches("commit").is_some() ||
       arguments.subcommand_matches("branch").is_some() {
        match sheet {
            Some(..) => {}
            None => process::exit(0),
        }
    }

    /* Variable to hold git commit message */
    let message;
    /* Unwrap the timesheet and continue only if timesheet file exists */
    let mut sheet = match sheet {
        Some(file) => file,
        None => {
            println!("No timesheet file! You might have to init first.");
            return;
        }
    };

    match arguments.subcommand() {
        ("begin", Some(arg)) => {
            let timestamp: Option<u64> = parse_hhmm_to_seconds(arg.value_of("ago").unwrap_or(""))
                .map(|ago| get_seconds() - ago);
            sheet.new_session(timestamp);
            message = "begin new session";
        }
        ("end", Some(arg)) => {
            let timestamp: Option<u64> = parse_hhmm_to_seconds(arg.value_of("ago").unwrap_or(""))
                .map(|ago| get_seconds() - ago);
            sheet.end_session(timestamp);
            message = "end session";
        }
        ("pause", Some(arg)) => {
            let timestamp: Option<u64> = parse_hhmm_to_seconds(arg.value_of("ago").unwrap_or(""))
                .map(|ago| get_seconds() - ago);
            let note_text = arg.value_of("note_text");
            match note_text {
                Some(note_text) => sheet.pause(timestamp, Some(note_text.to_string())),
                None => sheet.pause(timestamp, None),
            }
            message = "pause session";
        }

        ("resume", Some(arg)) => {
            let timestamp: Option<u64> = parse_hhmm_to_seconds(arg.value_of("ago").unwrap_or(""))
                .map(|ago| get_seconds() - ago);
            sheet.resume(timestamp);
            message = "resume session";
        }
        ("note", Some(arg)) => {
            let timestamp: Option<u64> = parse_hhmm_to_seconds(arg.value_of("ago").unwrap_or(""))
                .map(|ago| get_seconds() - ago);
            let note_text = arg.value_of("note_text").unwrap();
            sheet.note(timestamp, note_text.to_string());
            message = "add note to session";
        }
        ("commit", Some(arg)) => {
            let commit_hash = arg.value_of("hash").unwrap();
            sheet.add_commit(commit_hash.to_string());
            message = "add commit to session";
        }
        ("branch", Some(arg)) => {
            let branch_name = arg.value_of("name").unwrap();
            sheet.add_branch(branch_name.to_string());
            message = "add branch to branchlist";
        }
        ("status", Some(arg)) => {
            match arg.value_of("sheet_or_session") {
                Some("session") => println!("{}", sheet.last_session_status()),
                Some("sheet") => println!("{}", sheet.timesheet_status()),
                Some(text) => {
                    println!("What do you mean by {}? Should be either 'sheet' or 'session'.",
                             text)
                }
                _ => unreachable!(),
            }
            return;
        }
        ("report", Some(arg)) => {
            match arg.value_of("sheet_or_session") {
                Some("session") => sheet.report_last_session(),
                Some("sheet") => {
                    let timestamp: Option<u64> =
                        parse_hhmm_to_seconds(arg.value_of("ago").unwrap_or(""))
                        .map(|ago| get_seconds() - ago);
                    sheet.report_sheet(timestamp);
                }
                Some(text) => {
                    println!("What do you mean by {}? Should be either 'sheet' or 'session'.",
                             text)
                }
                _ => unreachable!(),
            }
            return;
        }
        ("set_show_commits", Some(arg)) => {
            match arg.value_of("on_off") {
                Some("on") => sheet.show_commits(true),
                Some("off") => sheet.show_commits(false),
                Some(text) => {
                    println!("What do you mean by {}? Should be either 'on' or 'off'.",
                             text)
                }
                _ => unreachable!(),
            }
            return;
        }
        ("set_repo_url", Some(arg)) => {
            match arg.value_of("url") {
                Some(repo_url) => sheet.set_repo_url(repo_url.to_string()),
                None => println!("Could not parse argument of trk set repo_url."),
            }
            return;
        }
        _ => unreachable!(),
    }
    sheet.write_files();
    git_commit_trk(message);
}
<|endoftext|>


<commit_before>extern crate byteorder;

#[macro_use]
extern crate futures;

#[macro_use]
extern crate tokio_core;

use std::env;
use std::net::SocketAddr;
use std::rc::Rc;
use std::cell::Cell;

use byteorder::{LittleEndian, ByteOrder};
use futures::{Async, Poll, Future};
use futures::stream::Stream;
use tokio_core::io::{read_exact, write_all, Io};
use tokio_core::net::TcpListener;
use tokio_core::reactor::Core;

struct ReadStream<R> where R: ::std::io::Read {
    reader: R,
    buffer: Vec<u8>,
    pos: usize,
    frame_end: Option<u8>,
}

impl <R> ReadStream<R> where R: ::std::io::Read {
    fn new(reader: R) -> ReadStream<R> {
        ReadStream {
            reader: reader,
            buffer: Vec::new(),
            pos: 0,
            frame_end: None,
        }
    }
}

impl <R> Stream for ReadStream<R> where R: ::std::io::Read {
    type Item = Vec<u8>;
    type Error = ::std::io::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        loop {
            if let Some(frame_end) = self.frame_end {
                let n = try_nb!(self.reader.read(&mut self.buffer[self.pos..frame_end as usize]));
                self.pos += n;
                if self.pos == frame_end as usize {
                    self.pos = 0;
                    let result = ::std::mem::replace(&mut self.buffer, Vec::new());
                    return Ok(Async::Ready(Some(result)))
                }
            } else {
                let mut buf = [0u8];
                let n = try_nb!(self.reader.read(&mut buf));
                if n == 0 { // EOF
                    return Ok(Async::Ready(None))
                }

                self.frame_end = Some(buf[0]);
                self.buffer = vec![0; buf[0] as usize];
            }
        }
    }
}

pub fn main() {
    let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
    let addr = addr.parse::<SocketAddr>().unwrap();

    let mut l = Core::new().unwrap();
    let handle = l.handle();

    // Create a TCP listener which will listen for incoming connections
    let socket = TcpListener::bind(&addr, &handle).unwrap();

    // Once we've got the TCP listener, inform that we have it
    println!("Listening on: {}", addr);

    let done = socket.incoming().for_each(move |(socket, _addr)| {
        // what's the spec?
        // first byte: 0 means publisher, 1 means subscriber.

        let header = [0u8; 1];
        let future = read_exact(socket, header).and_then(|(socket, header)| {
            println!("OK {:?}", header);
            match header[0] {
                0 => {
                    // publisher
                    let num_read = Rc::new(Cell::new(0u64));
                    let num_read1 = num_read.clone();
                    let done = futures::lazy(|| Ok(socket.split()))
                        .and_then(|(reader, writer)| {
                            ReadStream::new(reader).for_each(move |buf| {
                                num_read1.set(num_read1.get() + 1);
                                println!("buf {:?}", buf);
                                // TODO send this off to the subscribers.
                                Ok(())
                            }).and_then(move |()| {
                                let mut word = [0u8; 8];
                                <LittleEndian as ByteOrder>::write_u64(&mut word, num_read.get());
                                write_all(writer, word).map(|_| ())
                            })
                        });

                    // When this stream is done, I want the socket back.
                    done

                    // When done receiving message, write back the number received,
                    // as a little-endian u64.
                }
                1 => {
                    //subscriber
                    unimplemented!()
                }
                _ => {
                    // error
                    unimplemented!()
                }
            }
        }).map_err(|e| {
            println!("error: {}", e);
        });
        handle.spawn(future);

        // frame format: first byte is length of body. Then there is body.
        Ok(())
    });

    l.run(done).unwrap();
}
<commit_msg>working on WriteQueue<commit_after>extern crate byteorder;

#[macro_use]
extern crate futures;

#[macro_use]
extern crate tokio_core;

use std::env;
use std::net::SocketAddr;
use std::rc::Rc;
use std::cell::Cell;

use byteorder::{LittleEndian, ByteOrder};
use futures::{Async, Poll, Future};
use futures::stream::Stream;
use tokio_core::io::{read_exact, write_all, Io};
use tokio_core::net::TcpListener;
use tokio_core::reactor::Core;

struct ReadStream<R> where R: ::std::io::Read {
    reader: R,
    buffer: Vec<u8>,
    pos: usize,
    frame_end: Option<u8>,
}

impl <R> ReadStream<R> where R: ::std::io::Read {
    fn new(reader: R) -> ReadStream<R> {
        ReadStream {
            reader: reader,
            buffer: Vec::new(),
            pos: 0,
            frame_end: None,
        }
    }
}

impl <R> Stream for ReadStream<R> where R: ::std::io::Read {
    type Item = Vec<u8>;
    type Error = ::std::io::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        loop {
            if let Some(frame_end) = self.frame_end {
                let n = try_nb!(self.reader.read(&mut self.buffer[self.pos..frame_end as usize]));
                self.pos += n;
                if self.pos == frame_end as usize {
                    self.pos = 0;
                    let result = ::std::mem::replace(&mut self.buffer, Vec::new());
                    return Ok(Async::Ready(Some(result)))
                }
            } else {
                let mut buf = [0u8];
                let n = try_nb!(self.reader.read(&mut buf));
                if n == 0 { // EOF
                    return Ok(Async::Ready(None))
                }

                self.frame_end = Some(buf[0]);
                self.buffer = vec![0; buf[0] as usize];
            }
        }
    }
}

pub fn main() {
    let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
    let addr = addr.parse::<SocketAddr>().unwrap();

    let mut l = Core::new().unwrap();
    let handle = l.handle();

    // Create a TCP listener which will listen for incoming connections
    let socket = TcpListener::bind(&addr, &handle).unwrap();

    // Once we've got the TCP listener, inform that we have it
    println!("Listening on: {}", addr);

    let done = socket.incoming().for_each(move |(socket, _addr)| {
        // what's the spec?
        // first byte: 0 means publisher, 1 means subscriber.

        let header = [0u8; 1];
        let future = read_exact(socket, header).and_then(|(socket, header)| {
            println!("OK {:?}", header);
            match header[0] {
                0 => {
                    // publisher
                    let num_read = Rc::new(Cell::new(0u64));
                    let num_read1 = num_read.clone();
                    let done = futures::lazy(|| Ok(socket.split()))
                        .and_then(|(reader, writer)| {
                            ReadStream::new(reader).for_each(move |buf| {
                                num_read1.set(num_read1.get() + 1);
                                println!("buf {:?}", buf);
                                // TODO send this off to the subscribers.
                                Ok(())
                            }).and_then(move |()| {
                                let mut word = [0u8; 8];
                                <LittleEndian as ByteOrder>::write_u64(&mut word, num_read.get());
                                write_all(writer, word).map(|_| ())
                            })
                        });

                    // When this stream is done, I want the socket back.
                    done

                    // When done receiving message, write back the number received,
                    // as a little-endian u64.
                }
                1 => {
                    //subscriber
                    unimplemented!()
                }
                _ => {
                    // error
                    unimplemented!()
                }
            }
        }).map_err(|e| {
            println!("error: {}", e);
        });
        handle.spawn(future);

        // frame format: first byte is length of body. Then there is body.
        Ok(())
    });

    l.run(done).unwrap();
}

mod write_queue {
    use std::collections::VecDeque;
    use std::rc::Rc;
    use std::cell::RefCell;
    use futures::{self, task, Async, Future, Poll, Complete, Oneshot};
    use tokio_core::io::WriteAll;

    enum State<W> where W: ::std::io::Write {
        WritingHeader(W, Vec<u8>, Complete<Vec<u8>>),
        Writing(WriteAll<W, Vec<u8>>, Complete<Vec<u8>>),
        BetweenWrites(W),
        Empty,
    }

    /// A write of messages being written.
    pub struct WriteQueue<W> where W: ::std::io::Write {
        inner: Rc<RefCell<Inner>>,
        state: State<W>,
    }

    struct Inner {
        queue: VecDeque<(Vec<u8>, Complete<Vec<u8>>)>,
        sender_count: usize,
        task: Option<task::Task>,
    }

    pub struct Sender {
        inner: Rc<RefCell<Inner>>,
    }

    impl Clone for Sender {
        fn clone(&self) -> Sender {
            self.inner.borrow_mut().sender_count += 1;
            Sender { inner: self.inner.clone() }
        }
    }

    impl Drop for Sender {
        fn drop(&mut self) {
            self.inner.borrow_mut().sender_count -= 1;
        }
    }

    pub fn write_queue<W>(writer: W) -> (Sender, WriteQueue<W>)
        where W: ::std::io::Write
    {
        let inner = Rc::new(RefCell::new(Inner {
            queue: VecDeque::new(),
            task: None,
            sender_count: 1,
        }));

        let queue = WriteQueue {
            inner: inner.clone(),
            state: State::BetweenWrites(writer),
        };

        let sender = Sender { inner: inner };

        (sender, queue)
    }

    impl Sender {
        /// Enqueues a message to be written.
        pub fn send(&mut self, message: Vec<u8>) -> Oneshot<Vec<u8>> {
            let (complete, oneshot) = futures::oneshot();
            self.inner.borrow_mut().queue.push_back((message, complete));

            match self.inner.borrow_mut().task.take() {
                Some(t) => t.unpark(),
                None => (),
            }

            oneshot
        }

        /// Returns the number of messages queued to be written, not including any in-progress write.
        pub fn len(&mut self) -> usize {
            self.inner.borrow().queue.len()
        }
    }

    enum IntermediateState<W> where W: ::std::io::Write {
        WriteHeaderDone,
        WriteDone(Vec<u8>, W),
        StartWrite(Vec<u8>, Complete<Vec<u8>>),
        Resolve,
    }

    impl <W> Future for WriteQueue<W> where W: ::std::io::Write {
        type Item = W; // Resolves when all senders have been dropped and all messages written.
        type Error = ::std::io::Error;

        fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
            loop {
                let next = match self.state {
                    State::WritingHeader(ref mut write, ref buf, ref mut _complete) => {
                        let n = try_nb!(write.write(&[buf.len() as u8]));
                        match n {
                            0 => unimplemented!(), // TODO return error premature EOF
                            1 => IntermediateState::WriteHeaderDone,
                            _ => unreachable!(),
                        }
                    }
                    State::Writing(ref mut write, ref mut _complete) => {
                        let (w, m) = try_ready!(Future::poll(write));
                        IntermediateState::WriteDone(m, w)
                    }
                    State::BetweenWrites(ref mut _writer) => {
                        let front = self.inner.borrow_mut().queue.pop_front();
                        match front {
                            Some((m, complete)) => {
                                IntermediateState::StartWrite(m, complete)
                            }
                            None => {
                                let count = self.inner.borrow().sender_count;
                                if count == 0 {
                                    IntermediateState::Resolve
                                } else {
                                    self.inner.borrow_mut().task = Some(task::park());
                                    return Ok(Async::NotReady)
                                }
                            }
                        }
                    }
                    State::Empty => unreachable!(),
                };

                match next {
                    IntermediateState::WriteHeaderDone => {
                        let new_state = match ::std::mem::replace(&mut self.state, State::Empty) {
                            State::WritingHeader(writer, buf, complete) => {
                                State::Writing(::tokio_core::io::write_all(writer, buf), complete)
                            }
                            _ => unreachable!(),
                        };
                        self.state = new_state;
                    }
                    IntermediateState::WriteDone(m, w) => {
                        match ::std::mem::replace(&mut self.state, State::BetweenWrites(w)) {
                            State::Writing(_, complete) => {
                                complete.complete(m)
                            }
                            _ => unreachable!(),
                        }
                    }
                    IntermediateState::StartWrite(m, c) => {
                        let new_state = match ::std::mem::replace(&mut self.state, State::Empty) {
                            State::BetweenWrites(w) => {
                                State::WritingHeader(w, m, c)
                            }
                            _ => unreachable!(),
                        };
                        self.state = new_state;
                    }
                    IntermediateState::Resolve => {
                        match ::std::mem::replace(&mut self.state, State::Empty) {
                            State::BetweenWrites(w) => {
                                return Ok(Async::Ready(w))
                            }
                            _ => unreachable!(),
                        }
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate hetu;

use hetu::ErrorIndexRange;
use hetu::ParseError;
use hetu::Ssn;
use hetu::SsnPattern;
use std::env;
use std::process;

pub fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    if args.len() == 1 && (&args[0] == "-h" || &args[0] == "--help") {
        println!(
            "Validator and generator for Finnish SSN

Usage:
    hetu <ssn>
    hetu -p <pattern>
    hetu [options]

Options:
    -h, --help          Display this message
"
        );
        return;
    }

    if args.len() > 1 && &args[0] == "-p" {
        match SsnPattern::parse(&args[1]) {
            Err(ref err) => {
                eprintln!("Error: {}\n\n  {}\n  {}", err, &args[1], index_arrows(err));
                process::exit(1)
            }
            Ok(pattern) => match Ssn::generate_by_pattern(&pattern) {
                Ok(ref ssn) => println!("{}", ssn),
                Err(ref err) => {
                    eprintln!("Error: {}", err);
                    process::exit(1)
                }
            },
        }
    } else if args.len() == 0 {
        let pattern = SsnPattern::new();
        match Ssn::generate_by_pattern(&pattern) {
            Ok(ref ssn) => println!("{}", ssn),
            Err(ref err) => {
                eprintln!("Error: {}", err);
                process::exit(1)
            }
        }
    } else {
        let ssn: &String = &args[0];
        match Ssn::parse(&ssn) {
            Ok(_) => (),
            Err(ref err) => {
                eprintln!("Error: {}\n\n  {}\n  {}", err, &ssn, index_arrows(err));
                process::exit(1)
            }
        }
    }
}

fn index_arrows(err: &ParseError) -> String {
    let mut res: String = String::new();
    for _ in 0..err.start() {
        res.push_str(" ");
    }
    for _ in err.start()..err.end() {
        res.push_str("^");
    }
    res
}
<commit_msg>Move help to own function<commit_after>extern crate hetu;

use hetu::ErrorIndexRange;
use hetu::ParseError;
use hetu::Ssn;
use hetu::SsnPattern;
use std::env;
use std::process;

pub fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    if args.len() == 1 && (&args[0] == "-h" || &args[0] == "--help") {
        help();
        return;
    }

    if args.len() > 1 && &args[0] == "-p" {
        match SsnPattern::parse(&args[1]) {
            Err(ref err) => {
                eprintln!("Error: {}\n\n  {}\n  {}", err, &args[1], index_arrows(err));
                process::exit(1)
            }
            Ok(pattern) => match Ssn::generate_by_pattern(&pattern) {
                Ok(ref ssn) => println!("{}", ssn),
                Err(ref err) => {
                    eprintln!("Error: {}", err);
                    process::exit(1)
                }
            },
        }
    } else if args.len() == 0 {
        let pattern = SsnPattern::new();
        match Ssn::generate_by_pattern(&pattern) {
            Ok(ref ssn) => println!("{}", ssn),
            Err(ref err) => {
                eprintln!("Error: {}", err);
                process::exit(1)
            }
        }
    } else {
        let ssn: &String = &args[0];
        match Ssn::parse(&ssn) {
            Ok(_) => (),
            Err(ref err) => {
                eprintln!("Error: {}\n\n  {}\n  {}", err, &ssn, index_arrows(err));
                process::exit(1)
            }
        }
    }
}

fn help() {
    println!(
        "Validator and generator for Finnish SSN

Usage:
    hetu <ssn>
    hetu -p <pattern>
    hetu [options]

Options:
    -h, --help          Display this message
"
    );
}

fn index_arrows(err: &ParseError) -> String {
    let mut res: String = String::new();
    for _ in 0..err.start() {
        res.push_str(" ");
    }
    for _ in err.start()..err.end() {
        res.push_str("^");
    }
    res
}
<|endoftext|>