<commit_before>#![feature(path_ext)]
extern crate tempfile;
use tempfile::NamedTempFile;
use std::env;
use std::io::{Write, Read, Seek, SeekFrom};
use std::fs::{PathExt, File};

#[test]
fn test_basic() {
    let mut tmpfile = NamedTempFile::new().unwrap();
    write!(tmpfile, "abcde").unwrap();
    tmpfile.seek(SeekFrom::Start(0)).unwrap();
    let mut buf = String::new();
    tmpfile.read_to_string(&mut buf).unwrap();
    assert_eq!("abcde", buf);
}

#[test]
fn test_deleted() {
    let tmpfile = NamedTempFile::new().unwrap();
    let path = tmpfile.path().to_path_buf();
    assert!(path.exists());
    drop(tmpfile);
    assert!(!path.exists());
}

#[test]
fn test_persist() {
    let mut tmpfile = NamedTempFile::new().unwrap();
    let old_path = tmpfile.path().to_path_buf();
    let persist_path = env::temp_dir().join("persisted_temporary_file");
    write!(tmpfile, "abcde").unwrap();
    {
        assert!(old_path.exists());
        let mut f = tmpfile.persist(&persist_path).unwrap();
        assert!(!old_path.exists());

        // Check original file
        f.seek(SeekFrom::Start(0)).unwrap();
        let mut buf = String::new();
        f.read_to_string(&mut buf).unwrap();
        assert_eq!("abcde", buf);
    }

    {
        // Try opening it at the new path.
        let mut f = File::open(&persist_path).unwrap();
        f.seek(SeekFrom::Start(0)).unwrap();
        let mut buf = String::new();
        f.read_to_string(&mut buf).unwrap();
        assert_eq!("abcde", buf);
    }
    std::fs::remove_file(&persist_path).unwrap();
}
<commit_msg>Make tests beta compatible.<commit_after>extern crate tempfile;
use tempfile::NamedTempFile;
use std::env;
use std::io::{Write, Read, Seek, SeekFrom};
use std::fs::File;
use std::path::Path;

fn exists<P: AsRef<Path>>(path: P) -> bool {
    std::fs::metadata(path.as_ref()).is_ok()
}

#[test]
fn test_basic() {
    let mut tmpfile = NamedTempFile::new().unwrap();
    write!(tmpfile, "abcde").unwrap();
    tmpfile.seek(SeekFrom::Start(0)).unwrap();
    let mut buf = String::new();
    tmpfile.read_to_string(&mut buf).unwrap();
    assert_eq!("abcde", buf);
}

#[test]
fn test_deleted() {
    let tmpfile = NamedTempFile::new().unwrap();
    let path = tmpfile.path().to_path_buf();
    assert!(exists(&path));
    drop(tmpfile);
    assert!(!exists(&path));
}

#[test]
fn test_persist() {
    let mut tmpfile = NamedTempFile::new().unwrap();
    let old_path = tmpfile.path().to_path_buf();
    let persist_path = env::temp_dir().join("persisted_temporary_file");
    write!(tmpfile, "abcde").unwrap();
    {
        assert!(exists(&old_path));
        let mut f = tmpfile.persist(&persist_path).unwrap();
        assert!(!exists(&old_path));

        // Check original file
        f.seek(SeekFrom::Start(0)).unwrap();
        let mut buf = String::new();
        f.read_to_string(&mut buf).unwrap();
        assert_eq!("abcde", buf);
    }

    {
        // Try opening it at the new path.
        let mut f = File::open(&persist_path).unwrap();
        f.seek(SeekFrom::Start(0)).unwrap();
        let mut buf = String::new();
        f.read_to_string(&mut buf).unwrap();
        assert_eq!("abcde", buf);
    }
    std::fs::remove_file(&persist_path).unwrap();
}
<|endoftext|>


<commit_before>extern crate dns_parser;
#[macro_use] extern crate log;
extern crate net2;
extern crate mio;
extern crate rotor;
extern crate libc;
extern crate rand;
extern crate multimap;
extern crate nix;
extern crate byteorder;

mod fsm;
use fsm::{AddressFamily, FSM, Command, DEFAULT_TTL};

mod services;
use services::{ServicesInner, Services, ServiceData};
mod net;

use std::sync::mpsc::Sender;
use std::io;
use std::thread;
use dns_parser::Name;
use std::sync::{Arc, Mutex, RwLock};

pub struct Responder {
    handle: Option<thread::JoinHandle<()>>,
    services: Services,
    txs_notifiers: Vec<(Sender<Command>, rotor::Notifier)>,
}

pub struct Service<'a> {
    responder: &'a Responder,
    id: usize,
}

impl Responder {
    pub fn new() -> io::Result<Responder> {
        let txs_notifiers = Arc::new(Mutex::new(Vec::with_capacity(2)));

        let mut hostname = try!(net::gethostname());
        if !hostname.ends_with(".local") {
            hostname.push_str(".local");
        }
        let services = Arc::new(RwLock::new(ServicesInner::new(hostname)));

        let mut config = rotor::Config::new();
        config.slab_capacity(32);
        config.mio().notify_capacity(32);

        let mut loop_ = rotor::Loop::new(&config).unwrap();
        {
            let (fsm, tx) = try!(FSM::new(AddressFamily::Inet, &services));
            let txs_notifiers = txs_notifiers.clone();
            loop_.add_machine_with(move |scope| {
                fsm.register(scope).unwrap();
                txs_notifiers.lock().unwrap()
                    .push((tx, scope.notifier()));
                rotor::Response::ok(fsm)
            }).unwrap();
        }
        {
            let (fsm, tx) = try!(FSM::new(AddressFamily::Inet6, &services));
            let txs_notifiers = txs_notifiers.clone();
            loop_.add_machine_with(move |scope| {
                fsm.register(scope).unwrap();
                txs_notifiers.lock().unwrap()
                    .push((tx, scope.notifier()));
                rotor::Response::ok(fsm)
            }).unwrap();
        }

        let handle = try!(thread::Builder::new().name("mdns-responder".to_owned()).spawn(move || {
            loop_.run(()).unwrap();
        }));

        Ok(Responder {
            handle: Some(handle),
            services: services,
            txs_notifiers: Arc::try_unwrap(txs_notifiers).unwrap()
                .into_inner().unwrap()
        })
    }

    pub fn register(&self, svc_type: String, svc_name: String, port: u16, txt: &[&str]) -> Service {
        let txt = if txt.is_empty() {
            vec![0]
        } else {
            txt.into_iter().flat_map(|entry| {
                let entry = entry.as_bytes();
                if entry.len() > 255 {
                    panic!("{:?} is too long for a TXT record", entry);
                }
                std::iter::once(entry.len() as u8).chain(entry.into_iter().cloned())
            }).collect()
        };

        let svc = ServiceData {
            typ: Name::from_str(format!("{}.local", svc_type)).unwrap(),
            name: Name::from_str(format!("{}.{}.local", svc_name, svc_type)).unwrap(),
            port: port,
            txt: txt,
        };
        self.send_unsolicited(svc.clone(), DEFAULT_TTL, true);

        let id = self.services
            .write().unwrap()
            .register(svc);

        Service {
            responder: self,
            id: id,
        }
    }

    fn send_unsolicited(&self, svc: ServiceData, ttl: u32, include_ip: bool) {
        self.send(Command::SendUnsolicited {
            svc: svc,
            ttl: ttl,
            include_ip: include_ip,
        });
    }

    fn send(&self, cmd: Command) {
        for &(ref tx, ref notifier) in self.txs_notifiers.iter() {
            tx.send(cmd.clone()).expect("responder died");
            notifier.wakeup().unwrap();
        }
    }
}

impl Drop for Responder {
    fn drop(&mut self) {
        self.send(Command::Shutdown);
        self.handle.take().map(|h| h.join());
    }
}

impl <'a> Drop for Service<'a> {
    fn drop(&mut self) {
        let svc = self.responder.services
            .write().unwrap()
            .unregister(self.id);
        self.responder.send_unsolicited(svc, 0, false);
    }
}
<commit_msg>don't fail if if address family (ipv4 or ipv6) not enabled<commit_after>extern crate dns_parser;
#[macro_use] extern crate log;
extern crate net2;
extern crate mio;
extern crate rotor;
extern crate libc;
extern crate rand;
extern crate multimap;
extern crate nix;
extern crate byteorder;

mod fsm;
use fsm::{AddressFamily, FSM, Command, DEFAULT_TTL};

mod services;
use services::{ServicesInner, Services, ServiceData};
mod net;

use std::sync::mpsc::Sender;
use std::io;
use std::thread;
use dns_parser::Name;
use std::sync::{Arc, Mutex, RwLock};

pub struct Responder {
    handle: Option<thread::JoinHandle<()>>,
    services: Services,
    txs_notifiers: Vec<(Sender<Command>, rotor::Notifier)>,
}

pub struct Service<'a> {
    responder: &'a Responder,
    id: usize,
}

impl Responder {
    pub fn new() -> io::Result<Responder> {
        let txs_notifiers = Arc::new(Mutex::new(Vec::with_capacity(2)));

        let mut hostname = try!(net::gethostname());
        if !hostname.ends_with(".local") {
            hostname.push_str(".local");
        }
        let services = Arc::new(RwLock::new(ServicesInner::new(hostname)));

        let mut config = rotor::Config::new();
        config.slab_capacity(32);
        config.mio().notify_capacity(32);

        let mut loop_ = rotor::Loop::new(&config).unwrap();
        match FSM::new(AddressFamily::Inet, &services) {
            Ok((fsm, tx)) => {
                let txs_notifiers = txs_notifiers.clone();
                loop_.add_machine_with(move |scope| {
                    fsm.register(scope).unwrap();
                    txs_notifiers.lock().unwrap()
                        .push((tx, scope.notifier()));
                    rotor::Response::ok(fsm)
                }).unwrap();
            },
            Err(e) => warn!("Error creating IPv4 UDP socket for mDNS: {}", e)
        }
        match FSM::new(AddressFamily::Inet6, &services) {
            Ok((fsm, tx)) => {
                let txs_notifiers = txs_notifiers.clone();
                loop_.add_machine_with(move |scope| {
                    fsm.register(scope).unwrap();
                    txs_notifiers.lock().unwrap()
                        .push((tx, scope.notifier()));
                    rotor::Response::ok(fsm)
                }).unwrap();
            },
            Err(e) => warn!("Error creating IPv6 UDP socket for mDNS: {}", e)
        }

        let handle = try!(thread::Builder::new().name("mdns-responder".to_owned()).spawn(move || {
            loop_.run(()).unwrap();
        }));

        Ok(Responder {
            handle: Some(handle),
            services: services,
            txs_notifiers: Arc::try_unwrap(txs_notifiers).unwrap()
                .into_inner().unwrap()
        })
    }

    pub fn register(&self, svc_type: String, svc_name: String, port: u16, txt: &[&str]) -> Service {
        let txt = if txt.is_empty() {
            vec![0]
        } else {
            txt.into_iter().flat_map(|entry| {
                let entry = entry.as_bytes();
                if entry.len() > 255 {
                    panic!("{:?} is too long for a TXT record", entry);
                }
                std::iter::once(entry.len() as u8).chain(entry.into_iter().cloned())
            }).collect()
        };

        let svc = ServiceData {
            typ: Name::from_str(format!("{}.local", svc_type)).unwrap(),
            name: Name::from_str(format!("{}.{}.local", svc_name, svc_type)).unwrap(),
            port: port,
            txt: txt,
        };
        self.send_unsolicited(svc.clone(), DEFAULT_TTL, true);

        let id = self.services
            .write().unwrap()
            .register(svc);

        Service {
            responder: self,
            id: id,
        }
    }

    fn send_unsolicited(&self, svc: ServiceData, ttl: u32, include_ip: bool) {
        self.send(Command::SendUnsolicited {
            svc: svc,
            ttl: ttl,
            include_ip: include_ip,
        });
    }

    fn send(&self, cmd: Command) {
        for &(ref tx, ref notifier) in self.txs_notifiers.iter() {
            tx.send(cmd.clone()).expect("responder died");
            notifier.wakeup().unwrap();
        }
    }
}

impl Drop for Responder {
    fn drop(&mut self) {
        self.send(Command::Shutdown);
        self.handle.take().map(|h| h.join());
    }
}

impl <'a> Drop for Service<'a> {
    fn drop(&mut self) {
        let svc = self.responder.services
            .write().unwrap()
            .unregister(self.id);
        self.responder.send_unsolicited(svc, 0, false);
    }
}
<|endoftext|>


<commit_before>//! This crate only provides the `MaybeOwned` enum
//!
//! Take a look at it's documentation for more information.
//!
#![warn(missing_docs)]
use std::ops::Deref;
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use std::fmt;
use std::borrow::Cow;

use self::MaybeOwned::*;

/// This type provides a way to store data to which you either have a
/// reference to or which you do own.
///
/// It provides `From<T>`, `From<&'a T>` implementations and, in difference
/// to `Cow` does _not_ require `ToOwned` to be implemented which makes it
/// compatible with non cloneable data, as a draw back of this it does not
/// know about `ToOwned`. As a consequence of it can't know that `&str` should
/// be the borrowed version of `String` and not `&String` this is especially
/// bad wrt. `Box` as the borrowed version of `Box<T>` would be `&Box<T>`.
///
/// While this crate has some drawbacks compared to `Cow` is has the benefit,
/// that it works with Types which neither implement `Clone` nor `ToOwned`.
/// Another benefit lies in the ability to write API functions which accept
/// a generic parameter `E: Into<MaybeOwned<'a, T>>` as the API consumer can
/// pass `T`, `&'a T` and `MaybeOwned<'a, T>` as argument, without requiring
/// a explicit `Cow::Onwed` or a split into two functions one accepting
/// owed and the other borrowed values.
///
/// # Alternatives
///
/// If you mainly have values implementing `ToOwned` like `&str`/`String`, `Path`/`PathBuf` or
/// `&[T]`/`Vec<T>` using `std::borrow::Cow` might be preferable.
///
/// If you want to be able to treat `&T`, `&mut T`, `Box<T>` and `Arc<T>` the same
/// consider using [`reffers::rbma::RBMA`](https://docs.rs/reffers)
/// (through not all types/platforms are supported because
/// as it relies on the fact that for many pointers the lowest two bits are 0, and stores
/// the discriminant in them, nevertheless this is can only be used with 32bit-aligned data,
/// e.g. using a &u8 _might_ fail). RBMA also allows you to recover a `&mut T` if it was created
/// from `Box<T>`, `&mut T` or a unique `Arc`.
///
///
/// # Examples
///
/// ```
/// # use maybe_owned::MaybeOwned;
/// struct PseudoBigData(u8);
/// fn pseudo_register_fn<'a, E>(_val: E) where E: Into<MaybeOwned<'a, PseudoBigData>> { }
///
/// let data = PseudoBigData(12);
/// let data2 = PseudoBigData(13);
///
/// pseudo_register_fn(&data);
/// pseudo_register_fn(&data);
/// pseudo_register_fn(data2);
/// pseudo_register_fn(MaybeOwned::Owned(PseudoBigData(111)));
/// ```
///
/// ```
/// # use maybe_owned::MaybeOwned;
/// #[repr(C)]
/// struct OpaqueFFI {
///     ref1:  * const u8
///     //we also might want to have PhantomData etc.
/// }
///
/// // does not work as it does not implement `ToOwned`
/// // let _ = Cow::Owned(OpaqueFFI { ref1: 0 as *const u8});
///
/// // ok, MaybeOwned can do this (but can't do &str<->String as tread of)
/// let _ = MaybeOwned::Owned(OpaqueFFI { ref1: 0 as *const u8 });
/// ```
#[derive(Debug)]
pub enum MaybeOwned<'a, T: 'a> {
    /// owns T
    Owned(T),
    /// has a reference to T
    Borrowed(&'a T)
}

impl<'a, T> MaybeOwned<'a, T> {

    /// returns true if the data is owned else false
    pub fn is_owned(&self) -> bool {
        match *self {
            Owned(_) => true,
            Borrowed(_) => false
        }
    }
}

impl<'a, T> Deref for MaybeOwned<'a, T> {
    type Target = T;

    fn deref(&self) -> &T {
        match *self {
            Owned(ref v) => v,
            Borrowed(v) => v
        }
    }
}

impl<'a, T> From<&'a T> for MaybeOwned<'a, T> {
    fn from(v: &'a T) -> MaybeOwned<'a, T> {
        Borrowed(v)
    }
}

impl<'a, T> From<T> for MaybeOwned<'a, T> {
    fn from(v: T) -> MaybeOwned<'a, T> {
        Owned(v)
    }
}

impl<'a, T> From<Cow<'a, T>> for MaybeOwned<'a, T>
    where T: ToOwned<Owned=T>,
{
    fn from(cow: Cow<'a, T>) -> MaybeOwned<'a, T> {
        match cow {
            Cow::Owned(v) => MaybeOwned::Owned(v),
            Cow::Borrowed(v) => MaybeOwned::Borrowed(v),
        }
    }
}

impl<'a, T> Into<Cow<'a, T>> for MaybeOwned<'a, T>
    where T: ToOwned<Owned=T>,
{
    fn into(self) -> Cow<'a, T> {
        match self {
            MaybeOwned::Owned(v) => Cow::Owned(v),
            MaybeOwned::Borrowed(v) => Cow::Borrowed(v),
        }
    }
}

impl<'a, T> Default for MaybeOwned<'a, T> where T: Default {
    fn default() -> Self {
        Owned(T::default())
    }
}

impl<'a, T> Clone for MaybeOwned<'a, T> where T: Clone {
    fn clone(&self) -> MaybeOwned<'a, T> {
        match *self {
            Owned(ref v) => Owned(v.clone()),
            Borrowed(v) => Borrowed(v)
        }
    }
}

impl<'a, 'b, A, B> PartialEq<MaybeOwned<'b, B>> for MaybeOwned<'a, A>
    where A: PartialEq<B>
{
    #[inline]
    fn eq(&self, other: &MaybeOwned<'b, B>) -> bool {
        PartialEq::eq(self.deref(), other.deref())
    }
}

impl<'a, T> Eq for MaybeOwned<'a, T> where T: Eq {}

impl<'a, T> PartialOrd for MaybeOwned<'a, T>
    where T: PartialOrd
{
    #[inline]
    fn partial_cmp(&self, other: &MaybeOwned<'a, T>) -> Option<Ordering> {
        PartialOrd::partial_cmp(self.deref(), other.deref())
    }
}

impl<'a, T> Ord for MaybeOwned<'a, T>
    where T: Ord
{
    #[inline]
    fn cmp(&self, other: &MaybeOwned<'a, T>) -> Ordering {
        Ord::cmp(self.deref(), other.deref())
    }
}

impl<'a, T> Hash for MaybeOwned<'a, T>
    where T: Hash
{
    #[inline]
    fn hash<H: Hasher>(&self, state: &mut H) {
        Hash::hash(self.deref(), state)
    }
}

impl<'a, T> fmt::Display for MaybeOwned<'a, T>
    where T: fmt::Display
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Owned(ref o) => fmt::Display::fmt(o, f),
            Borrowed(b) => fmt::Display::fmt(b, f),
        }
    }
}

impl<'a, T> MaybeOwned<'a, T> where T: Clone {

    /// Aquires a mutable reference to owned data.
    ///
    /// Clones data if it is not already owned.
    ///
    /// ## Example
    ///
    /// ```
    /// use maybe_owned::MaybeOwned;
    ///
    /// #[derive(Clone, Debug, PartialEq, Eq)]
    /// struct PseudoBigData(u8);
    ///
    /// let data = PseudoBigData(12);
    ///
    /// let mut maybe: MaybeOwned<PseudoBigData> = (&data).into();
    /// assert_eq!(false, maybe.is_owned());
    ///
    /// {
    ///     let reference = maybe.to_mut();
    ///     assert_eq!(&mut PseudoBigData(12), reference);
    /// }
    /// assert!(maybe.is_owned());
    /// ```
    ///
    pub fn to_mut(&mut self) -> &mut T {
        match *self {
            Owned(ref mut v) => v,
            Borrowed(v) => {
                *self = Owned(v.clone());
                match *self {
                    Owned(ref mut v) => v,
                    Borrowed(..) => unreachable!()
                }
            }

        }
    }

    /// Extracts the owned data.
    ///
    /// If the data is borrowed it is cloned before being extracted.
    pub fn into_owned(self) -> T {
        match self {
            Owned(v) => v,
            Borrowed(v) => v.clone()
        }
    }
}

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

    type TestType = Vec<()>;

    fn with_into<'a, I: Into<MaybeOwned<'a, TestType>>>(v: I) -> MaybeOwned<'a, TestType> {
        v.into()
    }

    #[test]
    fn is_owned() {
        let data = TestType::default();
        assert!(MaybeOwned::Owned(data).is_owned());
    }

    #[test]
    fn into_with_owned() {
        //ty check if it accepts references
        let data = TestType::default();
        assert!(with_into(data).is_owned())

    }
    #[test]
    fn into_with_borrow() {
        //ty check if it accepts references
        let data = TestType::default();
        assert!(!with_into(&data).is_owned());
    }

    #[test]
    fn clone_owned() {
        let maybe = MaybeOwned::<TestType>::default();
        assert!(maybe.clone().is_owned());
    }

    #[test]
    fn clone_borrow() {
        let data = TestType::default();
        let maybe: MaybeOwned<TestType> = (&data).into();
        assert!(!maybe.clone().is_owned());
    }

    #[test]
    fn to_mut() {
        let data = TestType::default();
        let mut maybe: MaybeOwned<TestType> = (&data).into();
        assert!(!maybe.is_owned());
        {
            let _mut_ref = maybe.to_mut();
        }
        assert!(maybe.is_owned());
    }

    #[test]
    fn into_inner() {
        let data = vec![1u32,2];
        let maybe: MaybeOwned<Vec<u32>> = (&data).into();
        assert_eq!(data, maybe.into_owned());
    }

    #[test]
    fn has_default() {
        #[derive(Default)]
        struct TestType(u8);
        let _x: MaybeOwned<TestType> = Default::default();
    }

    #[test]
    fn has_clone() {
        #[derive(Clone)]
        struct TestType(u8);
        let _x  = TestType(12).clone();
    }

    #[test]
    fn has_partial_eq() {
        #[derive(PartialEq)]
        struct TestType(f32);

        let n = TestType(33.0);
        let a = MaybeOwned::Owned(TestType(42.0));
        let b = MaybeOwned::Borrowed(&n);
        let c = MaybeOwned::Owned(TestType(33.0));

        assert_eq!(a == b, false);
        assert_eq!(b == c, true);
        assert_eq!(c == a, false);
    }

    #[test]
    fn has_eq() {
        #[derive(PartialEq, Eq)]
        struct TestType(i32);

        let n = TestType(33);
        let a = MaybeOwned::Owned(TestType(42));
        let b = MaybeOwned::Borrowed(&n);
        let c = MaybeOwned::Owned(TestType(33));

        assert_eq!(a == b, false);
        assert_eq!(b == c, true);
        assert_eq!(c == a, false);
    }

    #[test]
    fn has_partial_ord() {
        #[derive(PartialEq, PartialOrd)]
        struct TestType(f32);

        let n = TestType(33.0);
        let a = MaybeOwned::Owned(TestType(42.0));
        let b = MaybeOwned::Borrowed(&n);
        let c = MaybeOwned::Owned(TestType(33.0));

        assert_eq!(a > b, true);
        assert_eq!(b > c, false);
        assert_eq!(a < c, false);
    }

    #[test]
    fn has_ord() {
        #[derive(PartialEq, Eq, PartialOrd, Ord)]
        struct TestType(i32);

        let n = TestType(33);
        let a = MaybeOwned::Owned(TestType(42));
        let b = MaybeOwned::Borrowed(&n);
        let c = MaybeOwned::Owned(TestType(33));

        assert_eq!(a > b, true);
        assert_eq!(b > c, false);
        assert_eq!(a < c, false);
    }

    #[test]
    fn has_hash() {
        use std::collections::HashMap;

        let mut map = HashMap::new();
        map.insert(MaybeOwned::Owned(42), 33);

        assert_eq!(map.get(&MaybeOwned::Borrowed(&42)), Some(&33));
    }

    #[test]
    fn has_display() {
        let n = 33;
        let a = MaybeOwned::Owned(42);
        let b = MaybeOwned::Borrowed(&n);

        let s = format!("{} {}", a, b);

        assert_eq!(s, "42 33");
    }

    #[test]
    fn from_cow() {
        use std::borrow::Cow;

        fn test<'a, V: Into<MaybeOwned<'a, i32>>>(v: V, n: i32) { assert_eq!(*v.into(), n) }

        let n = 33;
        test(Cow::Owned(42), 42);
        test(Cow::Borrowed(&n), n);
    }

    #[test]
    fn into_cow() {
        use std::borrow::Cow;

        fn test<'a, V: Into<Cow<'a, i32>>>(v: V, n: i32) { assert_eq!(*v.into(), n) }

        let n = 33;
        test(MaybeOwned::Owned(42), 42);
        test(MaybeOwned::Borrowed(&n), n);
    }
}
<commit_msg>chore(AsRef): impl AsRef for MaybeOwned<commit_after>//! This crate only provides the `MaybeOwned` enum
//!
//! Take a look at it's documentation for more information.
//!
#![warn(missing_docs)]
use std::ops::Deref;
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use std::fmt;
use std::borrow::Cow;

use self::MaybeOwned::*;

/// This type provides a way to store data to which you either have a
/// reference to or which you do own.
///
/// It provides `From<T>`, `From<&'a T>` implementations and, in difference
/// to `Cow` does _not_ require `ToOwned` to be implemented which makes it
/// compatible with non cloneable data, as a draw back of this it does not
/// know about `ToOwned`. As a consequence of it can't know that `&str` should
/// be the borrowed version of `String` and not `&String` this is especially
/// bad wrt. `Box` as the borrowed version of `Box<T>` would be `&Box<T>`.
///
/// While this crate has some drawbacks compared to `Cow` is has the benefit,
/// that it works with Types which neither implement `Clone` nor `ToOwned`.
/// Another benefit lies in the ability to write API functions which accept
/// a generic parameter `E: Into<MaybeOwned<'a, T>>` as the API consumer can
/// pass `T`, `&'a T` and `MaybeOwned<'a, T>` as argument, without requiring
/// a explicit `Cow::Onwed` or a split into two functions one accepting
/// owed and the other borrowed values.
///
/// # Alternatives
///
/// If you mainly have values implementing `ToOwned` like `&str`/`String`, `Path`/`PathBuf` or
/// `&[T]`/`Vec<T>` using `std::borrow::Cow` might be preferable.
///
/// If you want to be able to treat `&T`, `&mut T`, `Box<T>` and `Arc<T>` the same
/// consider using [`reffers::rbma::RBMA`](https://docs.rs/reffers)
/// (through not all types/platforms are supported because
/// as it relies on the fact that for many pointers the lowest two bits are 0, and stores
/// the discriminant in them, nevertheless this is can only be used with 32bit-aligned data,
/// e.g. using a &u8 _might_ fail). RBMA also allows you to recover a `&mut T` if it was created
/// from `Box<T>`, `&mut T` or a unique `Arc`.
///
///
/// # Examples
///
/// ```
/// # use maybe_owned::MaybeOwned;
/// struct PseudoBigData(u8);
/// fn pseudo_register_fn<'a, E>(_val: E) where E: Into<MaybeOwned<'a, PseudoBigData>> { }
///
/// let data = PseudoBigData(12);
/// let data2 = PseudoBigData(13);
///
/// pseudo_register_fn(&data);
/// pseudo_register_fn(&data);
/// pseudo_register_fn(data2);
/// pseudo_register_fn(MaybeOwned::Owned(PseudoBigData(111)));
/// ```
///
/// ```
/// # use maybe_owned::MaybeOwned;
/// #[repr(C)]
/// struct OpaqueFFI {
///     ref1:  * const u8
///     //we also might want to have PhantomData etc.
/// }
///
/// // does not work as it does not implement `ToOwned`
/// // let _ = Cow::Owned(OpaqueFFI { ref1: 0 as *const u8});
///
/// // ok, MaybeOwned can do this (but can't do &str<->String as tread of)
/// let _ = MaybeOwned::Owned(OpaqueFFI { ref1: 0 as *const u8 });
/// ```
#[derive(Debug)]
pub enum MaybeOwned<'a, T: 'a> {
    /// owns T
    Owned(T),
    /// has a reference to T
    Borrowed(&'a T)
}

impl<'a, T> MaybeOwned<'a, T> {

    /// returns true if the data is owned else false
    pub fn is_owned(&self) -> bool {
        match *self {
            Owned(_) => true,
            Borrowed(_) => false
        }
    }
}

impl<'a, T> Deref for MaybeOwned<'a, T> {
    type Target = T;

    fn deref(&self) -> &T {
        match *self {
            Owned(ref v) => v,
            Borrowed(v) => v
        }
    }
}

impl<'a, T> AsRef<T> for MaybeOwned<'a, T> {
    fn as_ref(&self) -> &T {
        self
    }
}

impl<'a, T> From<&'a T> for MaybeOwned<'a, T> {
    fn from(v: &'a T) -> MaybeOwned<'a, T> {
        Borrowed(v)
    }
}

impl<'a, T> From<T> for MaybeOwned<'a, T> {
    fn from(v: T) -> MaybeOwned<'a, T> {
        Owned(v)
    }
}

impl<'a, T> From<Cow<'a, T>> for MaybeOwned<'a, T>
    where T: ToOwned<Owned=T>,
{
    fn from(cow: Cow<'a, T>) -> MaybeOwned<'a, T> {
        match cow {
            Cow::Owned(v) => MaybeOwned::Owned(v),
            Cow::Borrowed(v) => MaybeOwned::Borrowed(v),
        }
    }
}

impl<'a, T> Into<Cow<'a, T>> for MaybeOwned<'a, T>
    where T: ToOwned<Owned=T>,
{
    fn into(self) -> Cow<'a, T> {
        match self {
            MaybeOwned::Owned(v) => Cow::Owned(v),
            MaybeOwned::Borrowed(v) => Cow::Borrowed(v),
        }
    }
}

impl<'a, T> Default for MaybeOwned<'a, T> where T: Default {
    fn default() -> Self {
        Owned(T::default())
    }
}

impl<'a, T> Clone for MaybeOwned<'a, T> where T: Clone {
    fn clone(&self) -> MaybeOwned<'a, T> {
        match *self {
            Owned(ref v) => Owned(v.clone()),
            Borrowed(v) => Borrowed(v)
        }
    }
}

impl<'a, 'b, A, B> PartialEq<MaybeOwned<'b, B>> for MaybeOwned<'a, A>
    where A: PartialEq<B>
{
    #[inline]
    fn eq(&self, other: &MaybeOwned<'b, B>) -> bool {
        PartialEq::eq(self.deref(), other.deref())
    }
}

impl<'a, T> Eq for MaybeOwned<'a, T> where T: Eq {}

impl<'a, T> PartialOrd for MaybeOwned<'a, T>
    where T: PartialOrd
{
    #[inline]
    fn partial_cmp(&self, other: &MaybeOwned<'a, T>) -> Option<Ordering> {
        PartialOrd::partial_cmp(self.deref(), other.deref())
    }
}

impl<'a, T> Ord for MaybeOwned<'a, T>
    where T: Ord
{
    #[inline]
    fn cmp(&self, other: &MaybeOwned<'a, T>) -> Ordering {
        Ord::cmp(self.deref(), other.deref())
    }
}

impl<'a, T> Hash for MaybeOwned<'a, T>
    where T: Hash
{
    #[inline]
    fn hash<H: Hasher>(&self, state: &mut H) {
        Hash::hash(self.deref(), state)
    }
}

impl<'a, T> fmt::Display for MaybeOwned<'a, T>
    where T: fmt::Display
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Owned(ref o) => fmt::Display::fmt(o, f),
            Borrowed(b) => fmt::Display::fmt(b, f),
        }
    }
}

impl<'a, T> MaybeOwned<'a, T> where T: Clone {

    /// Aquires a mutable reference to owned data.
    ///
    /// Clones data if it is not already owned.
    ///
    /// ## Example
    ///
    /// ```
    /// use maybe_owned::MaybeOwned;
    ///
    /// #[derive(Clone, Debug, PartialEq, Eq)]
    /// struct PseudoBigData(u8);
    ///
    /// let data = PseudoBigData(12);
    ///
    /// let mut maybe: MaybeOwned<PseudoBigData> = (&data).into();
    /// assert_eq!(false, maybe.is_owned());
    ///
    /// {
    ///     let reference = maybe.to_mut();
    ///     assert_eq!(&mut PseudoBigData(12), reference);
    /// }
    /// assert!(maybe.is_owned());
    /// ```
    ///
    pub fn to_mut(&mut self) -> &mut T {
        match *self {
            Owned(ref mut v) => v,
            Borrowed(v) => {
                *self = Owned(v.clone());
                match *self {
                    Owned(ref mut v) => v,
                    Borrowed(..) => unreachable!()
                }
            }

        }
    }

    /// Extracts the owned data.
    ///
    /// If the data is borrowed it is cloned before being extracted.
    pub fn into_owned(self) -> T {
        match self {
            Owned(v) => v,
            Borrowed(v) => v.clone()
        }
    }
}

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

    type TestType = Vec<()>;

    fn with_into<'a, I: Into<MaybeOwned<'a, TestType>>>(v: I) -> MaybeOwned<'a, TestType> {
        v.into()
    }

    #[test]
    fn is_owned() {
        let data = TestType::default();
        assert!(MaybeOwned::Owned(data).is_owned());
    }

    #[test]
    fn into_with_owned() {
        //ty check if it accepts references
        let data = TestType::default();
        assert!(with_into(data).is_owned())

    }
    #[test]
    fn into_with_borrow() {
        //ty check if it accepts references
        let data = TestType::default();
        assert!(!with_into(&data).is_owned());
    }

    #[test]
    fn clone_owned() {
        let maybe = MaybeOwned::<TestType>::default();
        assert!(maybe.clone().is_owned());
    }

    #[test]
    fn clone_borrow() {
        let data = TestType::default();
        let maybe: MaybeOwned<TestType> = (&data).into();
        assert!(!maybe.clone().is_owned());
    }

    #[test]
    fn to_mut() {
        let data = TestType::default();
        let mut maybe: MaybeOwned<TestType> = (&data).into();
        assert!(!maybe.is_owned());
        {
            let _mut_ref = maybe.to_mut();
        }
        assert!(maybe.is_owned());
    }

    #[test]
    fn into_inner() {
        let data = vec![1u32,2];
        let maybe: MaybeOwned<Vec<u32>> = (&data).into();
        assert_eq!(data, maybe.into_owned());
    }

    #[test]
    fn has_default() {
        #[derive(Default)]
        struct TestType(u8);
        let _x: MaybeOwned<TestType> = Default::default();
    }

    #[test]
    fn has_clone() {
        #[derive(Clone)]
        struct TestType(u8);
        let _x  = TestType(12).clone();
    }

    #[test]
    fn has_partial_eq() {
        #[derive(PartialEq)]
        struct TestType(f32);

        let n = TestType(33.0);
        let a = MaybeOwned::Owned(TestType(42.0));
        let b = MaybeOwned::Borrowed(&n);
        let c = MaybeOwned::Owned(TestType(33.0));

        assert_eq!(a == b, false);
        assert_eq!(b == c, true);
        assert_eq!(c == a, false);
    }

    #[test]
    fn has_eq() {
        #[derive(PartialEq, Eq)]
        struct TestType(i32);

        let n = TestType(33);
        let a = MaybeOwned::Owned(TestType(42));
        let b = MaybeOwned::Borrowed(&n);
        let c = MaybeOwned::Owned(TestType(33));

        assert_eq!(a == b, false);
        assert_eq!(b == c, true);
        assert_eq!(c == a, false);
    }

    #[test]
    fn has_partial_ord() {
        #[derive(PartialEq, PartialOrd)]
        struct TestType(f32);

        let n = TestType(33.0);
        let a = MaybeOwned::Owned(TestType(42.0));
        let b = MaybeOwned::Borrowed(&n);
        let c = MaybeOwned::Owned(TestType(33.0));

        assert_eq!(a > b, true);
        assert_eq!(b > c, false);
        assert_eq!(a < c, false);
    }

    #[test]
    fn has_ord() {
        #[derive(PartialEq, Eq, PartialOrd, Ord)]
        struct TestType(i32);

        let n = TestType(33);
        let a = MaybeOwned::Owned(TestType(42));
        let b = MaybeOwned::Borrowed(&n);
        let c = MaybeOwned::Owned(TestType(33));

        assert_eq!(a > b, true);
        assert_eq!(b > c, false);
        assert_eq!(a < c, false);
    }

    #[test]
    fn has_hash() {
        use std::collections::HashMap;

        let mut map = HashMap::new();
        map.insert(MaybeOwned::Owned(42), 33);

        assert_eq!(map.get(&MaybeOwned::Borrowed(&42)), Some(&33));
    }

    #[test]
    fn has_display() {
        let n = 33;
        let a = MaybeOwned::Owned(42);
        let b = MaybeOwned::Borrowed(&n);

        let s = format!("{} {}", a, b);

        assert_eq!(s, "42 33");
    }

    #[test]
    fn from_cow() {
        use std::borrow::Cow;

        fn test<'a, V: Into<MaybeOwned<'a, i32>>>(v: V, n: i32) { assert_eq!(*v.into(), n) }

        let n = 33;
        test(Cow::Owned(42), 42);
        test(Cow::Borrowed(&n), n);
    }

    #[test]
    fn into_cow() {
        use std::borrow::Cow;

        fn test<'a, V: Into<Cow<'a, i32>>>(v: V, n: i32) { assert_eq!(*v.into(), n) }

        let n = 33;
        test(MaybeOwned::Owned(42), 42);
        test(MaybeOwned::Borrowed(&n), n);
    }

    #[test]
    fn as_ref() {
        let data  = TestType::default();
        let maybe_owned = MaybeOwned::Borrowed(&data);
        let _ref: &TestType = maybe_owned.as_ref();
        assert_eq!(
            &data as *const _ as usize,
            _ref as *const _ as usize
        );
    }
}
<|endoftext|>


<commit_before>extern crate git2;

use std::path::Path;
use std::path::PathBuf;
use std::fmt::Write;

pub struct GitPromptRepo {
    lg2_repo: Option<git2::Repository>,
    origin_path: PathBuf,
    root_path: PathBuf,
    is_unborn: bool,
    has_head: bool,
    has_checkout: bool,
}

impl GitPromptRepo {
    pub fn new(path_spec: &Path) -> GitPromptRepo {
        let lg2_repo = git2::Repository::discover(path_spec).ok();
        let mut root_path = PathBuf::new();
        let mut is_unborn = false;
        let mut has_head = false;
        let mut has_checkout = false;

        if let Some(ref repo) = lg2_repo {
            root_path = repo.path().to_path_buf();
            if let Err(e) = repo.head() {
                is_unborn = e.code() == git2::ErrorCode::UnbornBranch;
            } else {
                has_head = true;
            }
            has_checkout = !repo.is_bare();
        }

        GitPromptRepo {
            lg2_repo: lg2_repo,
            origin_path: path_spec.to_path_buf(),
            root_path: root_path,
            is_unborn: is_unborn,
            has_head: has_head,
            has_checkout: has_checkout,
        }
    }

    // Assumes has_head is true
    fn head_name(&self) -> String {
        self.lg2_repo
            .as_ref()
            .unwrap()
            .head()
            .unwrap()
            .name()
            .unwrap_or("")
            .trim_left_matches("refs/heads/")
            .to_string()
    }

    fn head_to_branch(&self) -> Option<git2::Branch> {
        if self.has_head {
            let head_name = self.head_name();
            return self.lg2_repo
                .as_ref()
                .unwrap()
                .find_branch(head_name.as_str(), git2::BranchType::Local)
                .ok();
        }
        None
    }

    fn head_branch_has_upstream(&self) -> bool {
        if let Some(branch) = self.head_to_branch() {
            return branch.upstream().is_ok();
        }
        false
    }

    // fn is_remote(&self) -> bool {
    //     match &self.ref_result {
    //         &Ok(ref r) => r.is_remote(),
    //         &Err(_) => false
    //     }
    // }

    fn build_ref_name_for_commit(&self, commit: &git2::Commit, ref_string: &mut String) {
        let oid = commit.id();
        let mut candidate_branch_names = Vec::new();
        let branches_it = self.lg2_repo
            .as_ref()
            .unwrap()
            .branches(Some(git2::BranchType::Remote))
            .unwrap()
            .filter_map(|branch_result| branch_result.ok());
        for branch in branches_it {
            let ref branch_ref = branch.0.get();
            let peeled_obj_res = branch_ref.peel(git2::ObjectType::Commit);
            if peeled_obj_res.is_ok() && peeled_obj_res.unwrap().id() == oid
                && branch_ref.name().is_some()
            {
                candidate_branch_names.push(branch_ref.name().unwrap().to_string());
            }
        }
        ref_string.clear();
        if candidate_branch_names.is_empty() {
            let mut oid_string = "".to_string();
            write!(oid_string, "{}", oid).unwrap();
            oid_string.truncate(8);
            write!(ref_string, "{}", oid_string).unwrap();
        } else {
            write!(
                ref_string,
                "{}",
                find_best_branch_name(candidate_branch_names)
            ).unwrap();
        }
    }

    pub fn ref_name_head(&self) -> String {
        let mut ref_string = "".to_string();
        if self.is_unborn {
            ref_string = "[Unborn]".to_string();
        } else if self.has_head {
            ref_string = self.head_name();
        }
        if ref_string == "HEAD" {
            let head_object = self.lg2_repo
                .as_ref()
                .unwrap()
                .head()
                .unwrap()
                .resolve()
                .unwrap()
                .peel(git2::ObjectType::Commit)
                .unwrap();
            let head_commit = head_object.as_commit().unwrap();
            self.build_ref_name_for_commit(head_commit, &mut ref_string);
        }
        ref_string.to_string()
    }

    pub fn upstream_name(&self) -> String {
        if self.head_branch_has_upstream() {
            let branch = self.head_to_branch().unwrap();
            let upstream_branch = branch.upstream().unwrap();
            if let Ok(Some(name)) = upstream_branch.name() {
                return name.to_string();
            }
        }
        "".to_string()
    }

    pub fn ahead_behind(&self) -> String {
        let mut result = String::from("");
        if self.head_branch_has_upstream() {
            let head_branch = self.head_to_branch().unwrap();
            let upstream_branch = head_branch.upstream().unwrap();
            let head_branch_oid = head_branch.get().resolve().unwrap().target().unwrap();
            let upstream_branch_oid = upstream_branch.get().resolve().unwrap().target().unwrap();
            let repo = self.lg2_repo.as_ref().unwrap();
            let (ahead, behind) = repo.graph_ahead_behind(head_branch_oid, upstream_branch_oid)
                .unwrap();
            if behind > 0 {
                result += "↓·";
                result += &*behind.to_string();
            }
            if ahead > 0 {
                result += "↑·";
                result += &*ahead.to_string();
            }
        }
        result
    }

    pub fn status(&self) -> String {
        let mut result = String::from("");
        if self.has_checkout {
            let mut opts = git2::StatusOptions::new();
            if let Ok(statuses) = self.lg2_repo.as_ref().unwrap().statuses(Some(
                opts.update_index(true)
                    .include_untracked(true)
                    .recurse_untracked_dirs(true),
            )) {
                result += &status_bit_to_string(&statuses, git2::STATUS_INDEX_MODIFIED, "∂");
                result += &status_bit_to_string(&statuses, git2::STATUS_INDEX_NEW, "…");
                result += &status_bit_to_string(&statuses, git2::STATUS_INDEX_DELETED, "✖");
                result +=
                    &status_bit_to_string(&statuses, git2::STATUS_CONFLICTED, "\x1b[31;1m≠");
                result += &status_bit_to_string(&statuses, git2::STATUS_WT_MODIFIED, "\x1b[34m∂");
                result += &status_bit_to_string(&statuses, git2::STATUS_WT_NEW, "\x1b[34m…");
                result += &status_bit_to_string(&statuses, git2::STATUS_WT_DELETED, "\x1b[34m✖");
            }
            if result.is_empty() {
                result = String::from("\x1b[36m√\x1b[m");
            }
        }
        result
    }
}

fn status_bit_to_string(statuses: &git2::Statuses, flag: git2::Status, prefix: &str) -> String {
    let count = statuses
        .iter()
        .filter(|s| s.status().contains(flag))
        .count();
    let mut result = "".to_string();
    if count > 0 {
        write!(result, "{}{}\x1b[m", prefix, count).unwrap();
    }
    result
}

fn abbreviated_remote_branch_name(full_name: &String) -> Option<String> {
    let mut full_name_it = full_name.split('/').peekable();
    if full_name_it.next() == Some("refs") && full_name_it.next() == Some("remotes")
        && full_name_it.peek().is_some()
    {
        let mut abbreviated_name = full_name_it.next().unwrap().to_string();
        if full_name_it.peek().unwrap_or(&"").to_string() == "HEAD".to_string() {
            return None;
        }
        for s in full_name_it {
            abbreviated_name.push('/');
            abbreviated_name.push_str(s);
        }
        if !abbreviated_name.is_empty() {
            return Some(abbreviated_name);
        }
    }
    None
}

fn find_best_branch_name(branch_names: Vec<String>) -> String {
    // Transmute branch names into canonical shortened forms.
    // Also, remove all variants terminating in HEAD
    let working_branch_names: Vec<String> = branch_names
        .iter()
        .filter_map(abbreviated_remote_branch_name)
        .filter(|b| b != "HEAD")
        .collect();

    // Determine the minimum namespace depth of the names and
    // filter the list to include only those at the minimum depth.
    // The theory here is that we want to produce the "simplest" name,
    // and minimizing the depth should be done before, e.g., minimizing
    // the charcount
    let slash_counts: Vec<usize> = working_branch_names
        .iter()
        .map(|s| s.chars().filter(|c| c == &'/').count())
        .collect();
    let min_slashes = slash_counts.iter().min().unwrap();
    let branch_and_count_iter = working_branch_names.into_iter().zip(slash_counts.iter());
    let sifted_branch_names: Vec<String> = branch_and_count_iter
        .filter_map(|t| if t.1 == min_slashes { Some(t.0) } else { None })
        .collect();
    sifted_branch_names[0].clone()
}
<commit_msg>Detect a rebase in progress and change the prompt accordingly.<commit_after>extern crate git2;
// TODO: Investigate ansi_term crate.  https://crates.io/crates/ansi_term

use std::path::Path;
use std::path::PathBuf;
use std::fmt::Write;

pub struct GitPromptRepo {
    lg2_repo: Option<git2::Repository>,
    origin_path: PathBuf,
    root_path: PathBuf,
    is_unborn: bool,
    has_head: bool,
    has_checkout: bool,
}

impl GitPromptRepo {
    pub fn new(path_spec: &Path) -> GitPromptRepo {
        let lg2_repo = git2::Repository::discover(path_spec).ok();
        let mut root_path = PathBuf::new();
        let mut is_unborn = false;
        let mut has_head = false;
        let mut has_checkout = false;

        if let Some(ref repo) = lg2_repo {
            root_path = repo.path().to_path_buf();
            if let Err(e) = repo.head() {
                is_unborn = e.code() == git2::ErrorCode::UnbornBranch;
            } else {
                has_head = true;
            }
            has_checkout = !repo.is_bare();
        }

        GitPromptRepo {
            lg2_repo: lg2_repo,
            origin_path: path_spec.to_path_buf(),
            root_path: root_path,
            is_unborn: is_unborn,
            has_head: has_head,
            has_checkout: has_checkout,
        }
    }

    // Assumes has_head is true
    fn head_name(&self) -> String {
        self.lg2_repo
            .as_ref()
            .unwrap()
            .head()
            .unwrap()
            .name()
            .unwrap_or("")
            .trim_left_matches("refs/heads/")
            .to_string()
    }

    fn head_to_branch(&self) -> Option<git2::Branch> {
        if self.has_head {
            let head_name = self.head_name();
            return self.lg2_repo
                .as_ref()
                .unwrap()
                .find_branch(head_name.as_str(), git2::BranchType::Local)
                .ok();
        }
        None
    }

    fn head_branch_has_upstream(&self) -> bool {
        if let Some(branch) = self.head_to_branch() {
            return branch.upstream().is_ok();
        }
        false
    }

    // fn is_remote(&self) -> bool {
    //     match &self.ref_result {
    //         &Ok(ref r) => r.is_remote(),
    //         &Err(_) => false
    //     }
    // }

    fn build_ref_name_for_commit(&self, commit: &git2::Commit, ref_string: &mut String) {
        let oid = commit.id();
        let mut candidate_branch_names = Vec::new();
        let branches_it = self.lg2_repo
            .as_ref()
            .unwrap()
            .branches(Some(git2::BranchType::Remote))
            .unwrap()
            .filter_map(|branch_result| branch_result.ok());
        for branch in branches_it {
            let ref branch_ref = branch.0.get();
            let peeled_obj_res = branch_ref.peel(git2::ObjectType::Commit);
            if peeled_obj_res.is_ok() && peeled_obj_res.unwrap().id() == oid
                && branch_ref.name().is_some()
            {
                candidate_branch_names.push(branch_ref.name().unwrap().to_string());
            }
        }
        ref_string.clear();
        if candidate_branch_names.is_empty() {
            let mut oid_string = "".to_string();
            write!(oid_string, "{}", oid).unwrap();
            oid_string.truncate(8);
            write!(ref_string, "{}", oid_string).unwrap();
        } else {
            write!(
                ref_string,
                "{}",
                find_best_branch_name(candidate_branch_names)
            ).unwrap();
        }
    }

    pub fn ref_name_head(&self) -> String {
        let mut ref_string = "".to_string();
        // TODO: should instead check self.lg2_repo.state()
        if self.is_unborn {
            ref_string = "[Unborn]".to_string();
        } else if let Ok(rebasing_ref) = self.lg2_repo
            .as_ref()
            .unwrap()
            .find_reference("rebase-apply/orig-head")
        {
            // TODO: Should look at .git/rebase-apply/head-name rather than compute rebasing_name as we do here
            let onto_ref = self.lg2_repo
                .as_ref()
                .unwrap()
                .find_reference("rebase-apply/onto")
                .unwrap();
            let mut rebasing_name = String::new();
            let mut onto_name = String::new();
            self.build_ref_name_for_commit(
                rebasing_ref
                    .peel(git2::ObjectType::Commit)
                    .unwrap()
                    .as_commit()
                    .unwrap(),
                &mut rebasing_name,
            );
            self.build_ref_name_for_commit(
                onto_ref
                    .peel(git2::ObjectType::Commit)
                    .unwrap()
                    .as_commit()
                    .unwrap(),
                &mut onto_name,
            );
            write!(
                ref_string,
                "\x1b[1;35m...rebasing\x1b[0m {} \x1b[1;35monto\x1b[0m {}",
                rebasing_name,
                onto_name
            ).unwrap();
        } else if self.has_head {
            ref_string = self.head_name();
        }
        if ref_string == "HEAD" {
            let head_object = self.lg2_repo
                .as_ref()
                .unwrap()
                .head()
                .unwrap()
                .resolve()
                .unwrap()
                .peel(git2::ObjectType::Commit)
                .unwrap();
            let head_commit = head_object.as_commit().unwrap();
            self.build_ref_name_for_commit(head_commit, &mut ref_string);
        }
        ref_string.to_string()
    }

    pub fn upstream_name(&self) -> String {
        if self.head_branch_has_upstream() {
            let branch = self.head_to_branch().unwrap();
            let upstream_branch = branch.upstream().unwrap();
            if let Ok(Some(name)) = upstream_branch.name() {
                return name.to_string();
            }
        }
        "".to_string()
    }

    pub fn ahead_behind(&self) -> String {
        let mut result = String::from("");
        if self.head_branch_has_upstream() {
            let head_branch = self.head_to_branch().unwrap();
            let upstream_branch = head_branch.upstream().unwrap();
            let head_branch_oid = head_branch.get().resolve().unwrap().target().unwrap();
            let upstream_branch_oid = upstream_branch.get().resolve().unwrap().target().unwrap();
            let repo = self.lg2_repo.as_ref().unwrap();
            let (ahead, behind) = repo.graph_ahead_behind(head_branch_oid, upstream_branch_oid)
                .unwrap();
            if behind > 0 {
                result += "↓·";
                result += &*behind.to_string();
            }
            if ahead > 0 {
                result += "↑·";
                result += &*ahead.to_string();
            }
        }
        result
    }

    pub fn status(&self) -> String {
        let mut result = String::from("");
        if self.has_checkout {
            let mut opts = git2::StatusOptions::new();
            if let Ok(statuses) = self.lg2_repo.as_ref().unwrap().statuses(Some(
                opts.update_index(true)
                    .include_untracked(true)
                    .recurse_untracked_dirs(true),
            )) {
                result += &status_bit_to_string(&statuses, git2::STATUS_INDEX_MODIFIED, "∂");
                result += &status_bit_to_string(&statuses, git2::STATUS_INDEX_NEW, "…");
                result += &status_bit_to_string(&statuses, git2::STATUS_INDEX_DELETED, "✖");
                result +=
                    &status_bit_to_string(&statuses, git2::STATUS_CONFLICTED, "\x1b[31;1m≠");
                result += &status_bit_to_string(&statuses, git2::STATUS_WT_MODIFIED, "\x1b[34m∂");
                result += &status_bit_to_string(&statuses, git2::STATUS_WT_NEW, "\x1b[34m…");
                result += &status_bit_to_string(&statuses, git2::STATUS_WT_DELETED, "\x1b[34m✖");
            }
            if result.is_empty() {
                result = String::from("\x1b[36m√\x1b[m");
            }
        }
        result
    }
}

fn status_bit_to_string(statuses: &git2::Statuses, flag: git2::Status, prefix: &str) -> String {
    let count = statuses
        .iter()
        .filter(|s| s.status().contains(flag))
        .count();
    let mut result = "".to_string();
    if count > 0 {
        write!(result, "{}{}\x1b[m", prefix, count).unwrap();
    }
    result
}

fn abbreviated_remote_branch_name(full_name: &String) -> Option<String> {
    let mut full_name_it = full_name.split('/').peekable();
    if full_name_it.next() == Some("refs") && full_name_it.next() == Some("remotes")
        && full_name_it.peek().is_some()
    {
        let mut abbreviated_name = full_name_it.next().unwrap().to_string();
        if full_name_it.peek().unwrap_or(&"").to_string() == "HEAD".to_string() {
            return None;
        }
        for s in full_name_it {
            abbreviated_name.push('/');
            abbreviated_name.push_str(s);
        }
        if !abbreviated_name.is_empty() {
            return Some(abbreviated_name);
        }
    }
    None
}

fn find_best_branch_name(branch_names: Vec<String>) -> String {
    // Transmute branch names into canonical shortened forms.
    // Also, remove all variants terminating in HEAD
    let working_branch_names: Vec<String> = branch_names
        .iter()
        .filter_map(abbreviated_remote_branch_name)
        .filter(|b| b != "HEAD")
        .collect();

    // Determine the minimum namespace depth of the names and
    // filter the list to include only those at the minimum depth.
    // The theory here is that we want to produce the "simplest" name,
    // and minimizing the depth should be done before, e.g., minimizing
    // the charcount
    let slash_counts: Vec<usize> = working_branch_names
        .iter()
        .map(|s| s.chars().filter(|c| c == &'/').count())
        .collect();
    let min_slashes = slash_counts.iter().min().unwrap();
    let branch_and_count_iter = working_branch_names.into_iter().zip(slash_counts.iter());
    let sifted_branch_names: Vec<String> = branch_and_count_iter
        .filter_map(|t| if t.1 == min_slashes { Some(t.0) } else { None })
        .collect();
    sifted_branch_names[0].clone()
}
<|endoftext|>


<commit_before>extern crate toml;
extern crate shellexpand;

// Import traits
use std::io::prelude::Read;

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}

pub fn test() {
    println!("Hello from lib.rs");
}

#[derive(Debug)]
enum OnSuccess {
    Continue,
    Echo {message: String},
    Warn {message: String},
    Abort {message: String},
}

#[derive(Debug)]
enum OnFailure {
    Continue,
    Echo {message: String},
    Warn {message: String},
    Abort {message: String},
}

#[derive(Debug)]
enum Step {
    Echo {name: String, message: String},
    Run {name: String, command: String, arguments: String, on_success: OnSuccess, on_failure: OnFailure},
    Shell {name: String, command: String, on_success: OnSuccess, on_failure: OnFailure},
    SetEnv {variable: String, value: String},
}

fn parse_toml(filename: &str) -> Vec<Step> {

    println!("Reading toml file {}", filename);

    // Read toml file content
    let mut input = String::new();
    std::fs::File::open(&filename).and_then(|mut f| {
        f.read_to_string(&mut input)
    }).unwrap();

    // Pase toml file
    let mut parser = toml::Parser::new(&input);

    let toml = parser.parse().unwrap_or_else(|| {
        for err in &parser.errors {
            let (loline, locol) = parser.to_linecol(err.lo);
            let (hiline, hicol) = parser.to_linecol(err.hi);
            println!("{}:{}:{}-{}:{} error: {}",
                     filename, loline, locol, hiline, hicol, err.desc);
        }
        panic!("Error parsing toml file.");
    });

    // println!("toml: {:#?}", toml);

    // Parse "variables" table
    for (variable, value) in toml.get("variables").unwrap().as_table().unwrap() {
        let value = value.as_str().unwrap();
        // println!("variable: {}   value: {}", variable, value);
        std::env::set_var(variable, value);
    }

    // Parse "steps" table
    let mut steps = Vec::<Step>::with_capacity(10);

    for step in toml.get("step").unwrap().as_slice().unwrap() {
        let action = step.as_table().unwrap().get("action").unwrap().as_str().unwrap();
        let name = step.as_table().unwrap().get("name").unwrap().as_str().unwrap().to_string();
        let details = step.as_table().unwrap().get("details").unwrap().as_table().unwrap();
        let step = match action {
            "echo" => {
                let message = details.get("message").unwrap().as_str().unwrap().to_string();
                Step::Echo {name: name, message: message}
            },
            "run" => {
                let command = details.get("command").unwrap().as_str().unwrap().to_string();

                // Allow non-existing 'arguments' field, defaulting to empty string.
                let arguments_option = details.get("arguments");
                let arguments = match arguments_option {
                    None => String::new(),
                    Some(arguments_toml_value) => arguments_toml_value.as_str().unwrap().to_string(),
                };

                let on_success = match details.get("on_success") {
                    None => {
                        println!("WARNING: No 'on_success' set for {:?}, default of 'OnSuccess::Continue'", name);
                        OnSuccess::Continue
                    },
                    Some(toml_value) => {
                        let table = toml_value.as_table().unwrap();
                        let (message_type, message) = table.iter().nth(0).unwrap();
                        match message_type.as_ref() {
                            "echo" => OnSuccess::Echo {message: message.as_str().unwrap().to_string()},
                            "warn" => OnSuccess::Warn {message: message.as_str().unwrap().to_string()},
                            "abort" => OnSuccess::Abort {message: message.as_str().unwrap().to_string()},
                            _ => unimplemented!()
                        }
                    }
                };

                let on_failure = match details.get("on_failure") {
                    None => {
                        println!("WARNING: No 'on_failure' set for {:?}, default of 'OnFailure::Continue'", name);
                        OnFailure::Continue
                    },
                    Some(toml_value) => {
                        let table = toml_value.as_table().unwrap();
                        let (message_type, message) = table.iter().nth(0).unwrap();
                        match message_type.as_ref() {
                            "echo" => OnFailure::Echo {message: message.as_str().unwrap().to_string()},
                            "warn" => OnFailure::Warn {message: message.as_str().unwrap().to_string()},
                            "abort" => OnFailure::Abort {message: message.as_str().unwrap().to_string()},
                            _ => unimplemented!()
                        }
                    }
                };

                Step::Run {name: name, command: command, arguments: arguments, on_success: on_success, on_failure: on_failure}
            },
            "setenv" => {
                let variable = details.get("variable").unwrap().as_str().unwrap().to_string();
                let value = details.get("value").unwrap().as_str().unwrap().to_string();
                Step::SetEnv {variable: variable, value: value}
            },
            "shell" => {
                let command = details.get("command").unwrap().as_str().unwrap().to_string();
                // FIXME: Add possibility to store stdout/stderr in variable.

                let on_success = match details.get("on_success") {
                    None => {
                        println!("WARNING: No 'on_success' set for {:?}, default of 'OnSuccess::Continue'", name);
                        OnSuccess::Continue
                    },
                    Some(toml_value) => {
                        let table = toml_value.as_table().unwrap();
                        let (message_type, message) = table.iter().nth(0).unwrap();
                        match message_type.as_ref() {
                            "echo" => OnSuccess::Echo {message: message.as_str().unwrap().to_string()},
                            "warn" => OnSuccess::Warn {message: message.as_str().unwrap().to_string()},
                            "abort" => OnSuccess::Abort {message: message.as_str().unwrap().to_string()},
                            _ => unimplemented!()
                        }
                    }
                };

                let on_failure = match details.get("on_failure") {
                    None => {
                        println!("WARNING: No 'on_failure' set for {:?}, default of 'OnFailure::Continue'", name);
                        OnFailure::Continue
                    },
                    Some(toml_value) => {
                        let table = toml_value.as_table().unwrap();
                        let (message_type, message) = table.iter().nth(0).unwrap();
                        match message_type.as_ref() {
                            "echo" => OnFailure::Echo {message: message.as_str().unwrap().to_string()},
                            "warn" => OnFailure::Warn {message: message.as_str().unwrap().to_string()},
                            "abort" => OnFailure::Abort {message: message.as_str().unwrap().to_string()},
                            _ => unimplemented!()
                        }
                    }
                };

                Step::Shell {name: name, command: command, on_success: on_success, on_failure: on_failure}
            },
            _ => {
                println!("Unknown action '{}' in toml file", action);
                unimplemented!();
            },
        };
        // println!("Step: {:?}", step);

        steps.push(step);
    }

    steps
}

pub fn execute_steps(filename: &str) {

    let steps = parse_toml(filename);

    println!("Steps: {:#?}", steps);

    // Loop over steps
    let nb_steps = steps.len();
    for (idx, step) in steps.into_iter().enumerate() {
        // println!("Step {}/{} -- ", idx+1, nb_steps);
        match step {
            Step::Echo {name, message} => {
                // NOTE: If an environment variable is non-existing, the following line will panic.
                println!("{}", shellexpand::full(&message).unwrap());
            },
            Step::Run {name, command, arguments, on_success, on_failure} => {
                // NOTE: If an environment variable is non-existing, the following line will panic.
                let command: String = shellexpand::full(&command).unwrap().to_string();
                // NOTE: If an environment variable is non-existing, the following line will panic.
                let arguments: String = shellexpand::full(&arguments).unwrap().to_string();
                let arguments: Vec<&str> = arguments.split(' ').collect();
                println!("    command: {}   arguments: {:?}", command, arguments);
                let mut output = std::process::Command::new(command)
                                                       .args(&arguments)
                                                       .output()
                                                       .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });

                // println!("output: {:#?}", output);

                // Print stdout
                println!("{}", String::from_utf8_lossy(&output.stdout));
                println!("{}", String::from_utf8_lossy(&output.stderr));

                if output.status.success() {
                    println!("Success!");
                    match on_success {
                        OnSuccess::Continue => {},
                        OnSuccess::Echo {message} => {
                            println!("{}", message);
                        },
                        OnSuccess::Warn {message} => {
                            println!("WARNING: {}", message);
                        },
                        OnSuccess::Abort {message} => {
                            println!("ABORT: {}", message);
                            break;
                        },
                    }
                } else {
                    match on_failure {
                        OnFailure::Continue => {},
                        OnFailure::Echo {message} => {
                            println!("{}", message);
                        },
                        OnFailure::Warn {message} => {
                            println!("WARNING: {}", message);
                        },
                        OnFailure::Abort {message} => {
                            println!("ABORT: {}", message);
                            break;
                        },
                    }
                }
            },
            Step::SetEnv {variable, value} => {
                std::env::set_var(variable, value);
            },
            Step::Shell {name, command, on_success, on_failure} => {
                let shell = "sh";
                println!("    command: {} -c '{}'", shell, command);
                let output = std::process::Command::new(shell)
                                                       .arg("-c")
                                                       .arg(command)
                                                       .output()
                                                       .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });

               // Print stdout
               println!("{}", String::from_utf8_lossy(&output.stdout));
               println!("{}", String::from_utf8_lossy(&output.stderr));

               if output.status.success() {
                   println!("Success!");
                   match on_success {
                       OnSuccess::Continue => {},
                       OnSuccess::Echo {message} => {
                           println!("{}", message);
                       },
                       OnSuccess::Warn {message} => {
                           println!("WARNING: {}", message);
                       },
                       OnSuccess::Abort {message} => {
                           println!("ABORT: {}", message);
                           break;
                       },
                   }
               } else {
                   match on_failure {
                       OnFailure::Continue => {},
                       OnFailure::Echo {message} => {
                           println!("{}", message);
                       },
                       OnFailure::Warn {message} => {
                           println!("WARNING: {}", message);
                       },
                       OnFailure::Abort {message} => {
                           println!("ABORT: {}", message);
                           break;
                       },
                   }
               }
            },
        }
    }

}
<commit_msg>Cleanups<commit_after>extern crate toml;
extern crate shellexpand;

// Import traits
use std::io::prelude::Read;

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}

pub fn test() {
    println!("Hello from lib.rs");
}

#[derive(Debug)]
enum OnSuccess {
    Continue,
    Echo {message: String},
    Warn {message: String},
    Abort {message: String},
}

#[derive(Debug)]
enum OnFailure {
    Continue,
    Echo {message: String},
    Warn {message: String},
    Abort {message: String},
}

#[derive(Debug)]
enum Step {
    Echo {name: String, message: String},
    Run {name: String, command: String, arguments: String, on_success: OnSuccess, on_failure: OnFailure},
    Shell {name: String, command: String, on_success: OnSuccess, on_failure: OnFailure},
    SetEnv {variable: String, value: String},
}

fn parse_toml(filename: &str) -> Vec<Step> {

    println!("Reading toml file {}", filename);

    // Read toml file content
    let mut input = String::new();
    std::fs::File::open(&filename).and_then(|mut f| {
        f.read_to_string(&mut input)
    }).unwrap();

    // Pase toml file
    let mut parser = toml::Parser::new(&input);

    let toml = parser.parse().unwrap_or_else(|| {
        for err in &parser.errors {
            let (loline, locol) = parser.to_linecol(err.lo);
            let (hiline, hicol) = parser.to_linecol(err.hi);
            println!("{}:{}:{}-{}:{} error: {}",
                     filename, loline, locol, hiline, hicol, err.desc);
        }
        panic!("Error parsing toml file.");
    });

    // println!("toml: {:#?}", toml);

    // Parse "variables" table
    for (variable, value) in toml.get("variables").unwrap().as_table().unwrap() {
        let value = value.as_str().unwrap();
        // println!("variable: {}   value: {}", variable, value);
        std::env::set_var(variable, value);
    }

    // Parse "steps" table
    let mut steps = Vec::<Step>::with_capacity(10);

    for step in toml.get("step").unwrap().as_slice().unwrap() {
        let action = step.as_table().unwrap().get("action").unwrap().as_str().unwrap();
        let name = step.as_table().unwrap().get("name").unwrap().as_str().unwrap().to_string();
        let details = step.as_table().unwrap().get("details").unwrap().as_table().unwrap();
        let step = match action {
            "echo" => {
                let message = details.get("message").unwrap().as_str().unwrap().to_string();
                Step::Echo {name: name, message: message}
            },
            "run" => {
                let command = details.get("command").unwrap().as_str().unwrap().to_string();

                // Allow non-existing 'arguments' field, defaulting to empty string.
                let arguments_option = details.get("arguments");
                let arguments = match arguments_option {
                    None => String::new(),
                    Some(arguments_toml_value) => arguments_toml_value.as_str().unwrap().to_string(),
                };

                let on_success = match details.get("on_success") {
                    None => {
                        println!("WARNING: No 'on_success' set for {:?}, default of 'OnSuccess::Continue'", name);
                        OnSuccess::Continue
                    },
                    Some(toml_value) => {
                        let table = toml_value.as_table().unwrap();
                        let (message_type, message) = table.iter().nth(0).unwrap();
                        match message_type.as_ref() {
                            "echo" => OnSuccess::Echo {message: message.as_str().unwrap().to_string()},
                            "warn" => OnSuccess::Warn {message: message.as_str().unwrap().to_string()},
                            "abort" => OnSuccess::Abort {message: message.as_str().unwrap().to_string()},
                            _ => unimplemented!()
                        }
                    }
                };

                let on_failure = match details.get("on_failure") {
                    None => {
                        println!("WARNING: No 'on_failure' set for {:?}, default of 'OnFailure::Continue'", name);
                        OnFailure::Continue
                    },
                    Some(toml_value) => {
                        let table = toml_value.as_table().unwrap();
                        let (message_type, message) = table.iter().nth(0).unwrap();
                        match message_type.as_ref() {
                            "echo" => OnFailure::Echo {message: message.as_str().unwrap().to_string()},
                            "warn" => OnFailure::Warn {message: message.as_str().unwrap().to_string()},
                            "abort" => OnFailure::Abort {message: message.as_str().unwrap().to_string()},
                            _ => unimplemented!()
                        }
                    }
                };

                Step::Run {name: name, command: command, arguments: arguments, on_success: on_success, on_failure: on_failure}
            },
            "setenv" => {
                let variable = details.get("variable").unwrap().as_str().unwrap().to_string();
                let value = details.get("value").unwrap().as_str().unwrap().to_string();
                Step::SetEnv {variable: variable, value: value}
            },
            "shell" => {
                let command = details.get("command").unwrap().as_str().unwrap().to_string();
                // FIXME: Add possibility to store stdout/stderr in variable.

                let on_success = match details.get("on_success") {
                    None => {
                        println!("WARNING: No 'on_success' set for {:?}, default of 'OnSuccess::Continue'", name);
                        OnSuccess::Continue
                    },
                    Some(toml_value) => {
                        let table = toml_value.as_table().unwrap();
                        let (message_type, message) = table.iter().nth(0).unwrap();
                        match message_type.as_ref() {
                            "echo" => OnSuccess::Echo {message: message.as_str().unwrap().to_string()},
                            "warn" => OnSuccess::Warn {message: message.as_str().unwrap().to_string()},
                            "abort" => OnSuccess::Abort {message: message.as_str().unwrap().to_string()},
                            _ => unimplemented!()
                        }
                    }
                };

                let on_failure = match details.get("on_failure") {
                    None => {
                        println!("WARNING: No 'on_failure' set for {:?}, default of 'OnFailure::Continue'", name);
                        OnFailure::Continue
                    },
                    Some(toml_value) => {
                        let table = toml_value.as_table().unwrap();
                        let (message_type, message) = table.iter().nth(0).unwrap();
                        match message_type.as_ref() {
                            "echo" => OnFailure::Echo {message: message.as_str().unwrap().to_string()},
                            "warn" => OnFailure::Warn {message: message.as_str().unwrap().to_string()},
                            "abort" => OnFailure::Abort {message: message.as_str().unwrap().to_string()},
                            _ => unimplemented!()
                        }
                    }
                };

                Step::Shell {name: name, command: command, on_success: on_success, on_failure: on_failure}
            },
            _ => {
                println!("Unknown action '{}' in toml file", action);
                unimplemented!();
            },
        };
        // println!("Step: {:?}", step);

        steps.push(step);
    }

    steps
}

pub fn execute_steps(filename: &str) {

    let steps = parse_toml(filename);

    println!("Steps: {:#?}", steps);

    // Loop over steps
    let nb_steps = steps.len();
    for (idx, step) in steps.into_iter().enumerate() {
        // println!("Step {}/{} -- ", idx+1, nb_steps);
        match step {
            Step::Echo {name, message} => {
                // NOTE: If an environment variable is non-existing, the following line will panic.
                println!("{}", shellexpand::full(&message).unwrap());
            },
            Step::Run {name, command, arguments, on_success, on_failure} => {
                // NOTE: If an environment variable is non-existing, the following line will panic.
                let command: String = shellexpand::full(&command).unwrap().to_string();
                // NOTE: If an environment variable is non-existing, the following line will panic.
                let arguments: String = shellexpand::full(&arguments).unwrap().to_string();
                println!("    command: {} {}", command, arguments);
                let arguments: Vec<&str> = arguments.split(' ').collect();
                // println!("    command: {}   arguments: {:?}", command, arguments);
                let output = std::process::Command::new(command)
                                                       .args(&arguments)
                                                       .output()
                                                       .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });

                // println!("output: {:#?}", output);

                // Print stdout
                println!("{}", String::from_utf8_lossy(&output.stdout));
                println!("{}", String::from_utf8_lossy(&output.stderr));

                if output.status.success() {
                    println!("Success!");
                    match on_success {
                        OnSuccess::Continue => {},
                        OnSuccess::Echo {message} => {
                            println!("{}", message);
                        },
                        OnSuccess::Warn {message} => {
                            println!("WARNING: {}", message);
                        },
                        OnSuccess::Abort {message} => {
                            println!("ABORT: {}", message);
                            break;
                        },
                    }
                } else {
                    match on_failure {
                        OnFailure::Continue => {},
                        OnFailure::Echo {message} => {
                            println!("{}", message);
                        },
                        OnFailure::Warn {message} => {
                            println!("WARNING: {}", message);
                        },
                        OnFailure::Abort {message} => {
                            println!("ABORT: {}", message);
                            break;
                        },
                    }
                }
            },
            Step::SetEnv {variable, value} => {
                std::env::set_var(variable, value);
            },
            Step::Shell {name, command, on_success, on_failure} => {
                let shell = "sh";
                println!("    command: {} -c '{}'", shell, command);
                let output = std::process::Command::new(shell)
                                                       .arg("-c")
                                                       .arg(command)
                                                       .output()
                                                       .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });

               // Print stdout
               println!("{}", String::from_utf8_lossy(&output.stdout));
               println!("{}", String::from_utf8_lossy(&output.stderr));

               if output.status.success() {
                   println!("Success!");
                   match on_success {
                       OnSuccess::Continue => {},
                       OnSuccess::Echo {message} => {
                           println!("{}", message);
                       },
                       OnSuccess::Warn {message} => {
                           println!("WARNING: {}", message);
                       },
                       OnSuccess::Abort {message} => {
                           println!("ABORT: {}", message);
                           break;
                       },
                   }
               } else {
                   match on_failure {
                       OnFailure::Continue => {},
                       OnFailure::Echo {message} => {
                           println!("{}", message);
                       },
                       OnFailure::Warn {message} => {
                           println!("WARNING: {}", message);
                       },
                       OnFailure::Abort {message} => {
                           println!("ABORT: {}", message);
                           break;
                       },
                   }
               }
            },
        }
    }

}
<|endoftext|>