<commit_before>extern crate libc;
extern crate bytes;

use std::{ io, fs };
use std::sync::Arc;
use std::os::unix::io::AsRawFd;
use futures::{ Poll, Stream, Async };
use futures::sync::{ BiLock, BiLockAcquired };
use tokio_io::{ AsyncRead, AsyncWrite };
use tokio_core::net::TcpStream;
use nix;
use self::libc::off_t;
use self::bytes::buf::{ Buf, BufMut };
use ::error;

pub struct BiTcpStream(pub BiLockAcquired<TcpStream>);

impl io::Read for BiTcpStream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl io::Write for BiTcpStream {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}

impl AsyncRead for BiTcpStream {
    unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
        AsyncRead::prepare_uninitialized_buffer(&self.0 as &TcpStream, buf)
    }

    fn read_buf<B: BufMut>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
        AsyncRead::read_buf(&mut self.0 as &mut TcpStream, buf)
    }
}

impl AsyncWrite for BiTcpStream {
    fn shutdown(&mut self) -> Poll<(), io::Error> {
        AsyncWrite::shutdown(&mut self.0 as &mut TcpStream)
    }

    fn write_buf<B: Buf>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
        AsyncWrite::write_buf(&mut self.0 as &mut TcpStream, buf)
    }
}

pub struct SendFileFut {
    pub socket: Arc<BiLock<TcpStream>>,
    pub fd: fs::File,
    pub offset: off_t,
    pub end: usize
}

impl Stream for SendFileFut {
    type Item = usize;
    type Error = error::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        #[cfg(any(target_os = "linux", target_os = "android"))]
        use nix::sys::sendfile::sendfile;

        #[cfg(any(apple, freebsdlike))]
        use self::bsd::sendfile;

        let count = match self.end.checked_sub(self.offset as usize) {
            Some(0) | None => return Ok(Async::Ready(None)),
            Some(count) => count
        };

        let socket = match self.socket.poll_lock() {
            Async::Ready(socket) => socket,
            Async::NotReady => return Ok(Async::NotReady)
        };

        if let Async::NotReady = socket.poll_write() {
            return Ok(Async::NotReady)
        }

        match sendfile(socket.as_raw_fd(), self.fd.as_raw_fd(), Some(&mut self.offset), count) {
            Ok(len) => {
                Ok(Async::Ready(Some(len)))
            },
            Err(ref err) if &nix::Error::Sys(nix::Errno::EAGAIN) == err => {
                // TODO https://github.com/tokio-rs/tokio-core/issues/196
                // socket.need_write();

                Ok(Async::NotReady)
            },
            Err(err) => Err(err.into())
        }
    }
}

#[cfg(any(apple, freebsdlike))]
mod bsd {
    use std::ptr;
    use std::os::unix::io::RawFd;
    use libc::{ off_t, sendfile as __sendfile };
    use nix;

    pub fn sendfile(out_fd: RawFd, in_fd: RawFd, offset: Option<&mut off_t>, count: usize) -> nix::Result<usize> {
        let off =
            if let Some(ref off) = offset { **off }
            else { 0 };
        let mut len = count as _;

        #[cfg(apple)]
        let ret = unsafe { __sendfile(in_fd, out_fd, off, &mut len, ptr::null_mut(), 0) };

        #[cfg(freebsdlike)]
        let ret = unsafe { __sendfile(in_fd, out_fd, off, count, ptr::null_mut(), &mut len, 0) };

        if let Some(offset) = offset {
            *offset += len;
        }

        match ret {
            0 => Ok(len as usize),
            _ => Err(nix::Error::last())
        }
    }
}
<commit_msg>[Fixed] bsd test<commit_after>extern crate bytes;

use std::{ io, fs };
use std::sync::Arc;
use std::os::unix::io::AsRawFd;
use futures::{ Poll, Stream, Async };
use futures::sync::{ BiLock, BiLockAcquired };
use tokio_io::{ AsyncRead, AsyncWrite };
use tokio_core::net::TcpStream;
use nix;
use nix::libc::off_t;
use self::bytes::buf::{ Buf, BufMut };
use ::error;

pub struct BiTcpStream(pub BiLockAcquired<TcpStream>);

impl io::Read for BiTcpStream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl io::Write for BiTcpStream {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}

impl AsyncRead for BiTcpStream {
    unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
        AsyncRead::prepare_uninitialized_buffer(&self.0 as &TcpStream, buf)
    }

    fn read_buf<B: BufMut>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
        AsyncRead::read_buf(&mut self.0 as &mut TcpStream, buf)
    }
}

impl AsyncWrite for BiTcpStream {
    fn shutdown(&mut self) -> Poll<(), io::Error> {
        AsyncWrite::shutdown(&mut self.0 as &mut TcpStream)
    }

    fn write_buf<B: Buf>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
        AsyncWrite::write_buf(&mut self.0 as &mut TcpStream, buf)
    }
}

pub struct SendFileFut {
    pub socket: Arc<BiLock<TcpStream>>,
    pub fd: fs::File,
    pub offset: off_t,
    pub end: usize
}

impl Stream for SendFileFut {
    type Item = usize;
    type Error = error::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        #[cfg(any(target_os = "linux", target_os = "android"))]
        use nix::sys::sendfile::sendfile;

        #[cfg(any(apple, freebsdlike))]
        use self::bsd::sendfile;

        let count = match self.end.checked_sub(self.offset as usize) {
            Some(0) | None => return Ok(Async::Ready(None)),
            Some(count) => count
        };

        let socket = match self.socket.poll_lock() {
            Async::Ready(socket) => socket,
            Async::NotReady => return Ok(Async::NotReady)
        };

        if let Async::NotReady = socket.poll_write() {
            return Ok(Async::NotReady)
        }

        match sendfile(socket.as_raw_fd(), self.fd.as_raw_fd(), Some(&mut self.offset), count) {
            Ok(len) => {
                Ok(Async::Ready(Some(len)))
            },
            Err(ref err) if &nix::Error::Sys(nix::Errno::EAGAIN) == err => {
                // TODO https://github.com/tokio-rs/tokio-core/issues/196
                // socket.need_write();

                Ok(Async::NotReady)
            },
            Err(err) => Err(err.into())
        }
    }
}

#[cfg(any(apple, freebsdlike))]
mod bsd {
    use std::ptr;
    use std::os::unix::io::RawFd;
    use nix::libc::{ off_t, sendfile as __sendfile };
    use nix;

    pub fn sendfile(out_fd: RawFd, in_fd: RawFd, offset: Option<&mut off_t>, count: usize) -> nix::Result<usize> {
        let off =
            if let Some(ref off) = offset { **off }
            else { 0 };
        let mut len = count as _;

        #[cfg(apple)]
        let ret = unsafe { __sendfile(in_fd, out_fd, off, &mut len, ptr::null_mut(), 0) };

        #[cfg(freebsdlike)]
        let ret = unsafe { __sendfile(in_fd, out_fd, off, count, ptr::null_mut(), &mut len, 0) };

        if let Some(offset) = offset {
            *offset += len;
        }

        match ret {
            0 => Ok(len as usize),
            _ => Err(nix::Error::last())
        }
    }
}
<|endoftext|>


<commit_before>use std::cmp::Ordering::{self, Greater, Less, Equal};
use std::collections::HashSet;
use std::fmt::{Debug, Formatter, Error};
use std::hash::Hash;

#[cfg(any(quickcheck, test))]
use quickcheck::{Arbitrary, Gen};

use Crdt;

/// A grow-only set.
#[derive(Default)]
pub struct GSet<T> where T: Eq + Hash {
    elements: HashSet<T>
}

/// An insert operation over `GSet` CRDTs.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct GSetOp<T> {
    element: T
}

impl <T: Hash + Eq + Clone> GSet<T> {

    /// Create a new grow-only set.
    ///
    /// ### Example
    ///
    /// ```
    /// use crdt::set::GSet;
    ///
    /// let mut set = GSet::<i32>::new();
    /// assert!(set.is_empty());
    /// ```
    pub fn new() -> GSet<T> {
        GSet { elements: HashSet::new() }
    }

    /// Insert an element into a grow-only set.
    ///
    /// ### Example
    ///
    /// ```
    /// use crdt::set::GSet;
    ///
    /// let mut set = GSet::new();
    /// set.insert("first-element");
    /// assert!(set.contains(&"first-element"));
    /// ```
    pub fn insert(&mut self, element: T) -> Option<GSetOp<T>> {
        if self.elements.insert(element.clone()) {
            Some(GSetOp { element: element })
        } else {
            None
        }
    }

    /// Returns the number of elements in the set.
    pub fn len(&self) -> usize {
        self.elements.len()
    }

    /// Returns true if the set contains the value.
    pub fn contains(&self, value: &T) -> bool {
        self.elements.contains(value)
    }

    /// Returns true if the set contains no elements.
    pub fn is_empty(&self) -> bool{ self.len() == 0 }

    pub fn is_subset(&self, other: &GSet<T>) -> bool {
        self.elements.is_subset(&other.elements)
    }

    pub fn is_disjoint(&self, other: &GSet<T>) -> bool {
        self.elements.is_disjoint(&other.elements)
    }
}

impl <T> Crdt for GSet<T> where T: Clone + Eq + Hash {

    type Operation = GSetOp<T>;

    /// Merge a replica into the set.
    ///
    /// This method is used to perform state-based replication.
    ///
    /// ##### Example
    ///
    /// ```
    /// # use crdt::set::GSet;
    /// use crdt::Crdt;
    ///
    /// let mut local = GSet::new();
    /// let mut remote = GSet::new();
    ///
    /// local.insert(1i32);
    /// remote.insert(2);
    ///
    /// local.merge(remote);
    /// assert!(local.contains(&2));
    /// ```
    fn merge(&mut self, other: GSet<T>) {
        for element in other.elements.into_iter() {
            self.insert(element);
        }
    }

    /// Apply an insert operation to the set.
    ///
    /// This method is used to perform operation-based replication.
    ///
    /// Applying an operation to a `GSet` is idempotent.
    ///
    /// ##### Example
    ///
    /// ```
    /// # use crdt::set::GSet;
    /// # use crdt::Crdt;
    /// let mut local = GSet::new();
    /// let mut remote = GSet::new();
    ///
    /// let op = remote.insert(13i32).expect("GSet should be empty.");
    ///
    /// local.apply(op);
    /// assert!(local.contains(&13));
    /// ```
    fn apply(&mut self, op: GSetOp<T>) {
        self.insert(op.element);
    }
}

impl <T: Eq + Hash> PartialEq for GSet<T> {
    fn eq(&self, other: &GSet<T>) -> bool {
        self.elements == other.elements
    }
}

impl <T: Eq + Hash> Eq for GSet<T> {}

impl <T: Eq + Hash> PartialOrd for GSet<T> {
    fn partial_cmp(&self, other: &GSet<T>) -> Option<Ordering> {
        if self.elements == other.elements {
            Some(Equal)
        } else if self.elements.is_subset(&other.elements) {
            Some(Less)
        } else if self.elements.is_superset(&other.elements) {
            Some(Greater)
        } else {
            None
        }
    }
}

impl <T : Eq + Hash + Debug> Debug for GSet<T> {
     fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
         self.elements.fmt(f)
     }
}

impl <T: Clone + Eq + Hash> Clone for GSet<T> {
    fn clone(&self) -> GSet<T> {
        GSet { elements: self.elements.clone() }
    }
}

#[cfg(any(quickcheck, test))]
impl <T> Arbitrary for GSet<T> where T: Arbitrary + Clone + Eq + Hash {
    fn arbitrary<G>(g: &mut G) -> GSet<T> where G: Gen {
        let elements: Vec<T> = Arbitrary::arbitrary(g);
        GSet { elements: elements.into_iter().collect() }
    }
    fn shrink(&self) -> Box<Iterator<Item=GSet<T>> + 'static> {
        let elements: Vec<T> = self.elements.iter().cloned().collect();
        Box::new(elements.shrink().map(|es| GSet { elements: es.into_iter().collect() }))
    }
}

#[cfg(any(quickcheck, test))]
impl <T> Arbitrary for GSetOp<T> where T: Arbitrary {
    fn arbitrary<G: Gen>(g: &mut G) -> GSetOp<T> {
        GSetOp { element: Arbitrary::arbitrary(g) }
    }
    fn shrink(&self) -> Box<Iterator<Item=GSetOp<T>> + 'static> {
        Box::new(self.element.shrink().map(|e| GSetOp { element: e }))
    }
}

#[cfg(test)]
mod test {

    use quickcheck::quickcheck;

    use {Crdt, test};
    use super::{GSet, GSetOp};

    type C = GSet<u32>;
    type O = GSetOp<u32>;

    #[test]
    fn check_apply_is_commutative() {
        quickcheck(test::apply_is_commutative::<C> as fn(C, Vec<O>) -> bool);
    }

    #[test]
    fn check_merge_is_commutative() {
        quickcheck(test::merge_is_commutative::<C> as fn(C, Vec<C>) -> bool);
    }

    #[test]
    fn check_ordering_lte() {
        quickcheck(test::ordering_lte::<C> as fn(C, C) -> bool);
    }

    #[test]
    fn check_ordering_equality() {
        quickcheck(test::ordering_equality::<C> as fn(C, C) -> bool);
    }

    #[quickcheck]
    fn check_local_insert(elements: Vec<u8>) -> bool {
        let mut set = GSet::new();
        for element in elements.clone().into_iter() {
            set.insert(element);
        }

        elements.iter().all(|element| set.contains(element))
    }

    #[quickcheck]
    fn check_ordering_lt(mut a: GSet<u8>, b: GSet<u8>) -> bool {
        a.merge(b.clone());

        let mut i = 0;
        let mut success = None;
        while success.is_none() {
            success = a.insert(i);
            i += 1;
        }
        a > b && b < a
    }
}
<commit_msg>cleanup GSet<commit_after>use std::cmp::Ordering::{self, Greater, Less, Equal};
use std::collections::HashSet;
use std::hash::Hash;

#[cfg(any(quickcheck, test))]
use quickcheck::{Arbitrary, Gen};

use Crdt;

/// A grow-only set.
#[derive(Debug, Default)]
pub struct GSet<T> where T: Eq + Hash {
    elements: HashSet<T>
}

/// An insert operation over `GSet` CRDTs.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct GSetOp<T> {
    element: T
}

impl <T> GSet<T> where T: Clone + Eq + Hash {

    /// Create a new grow-only set.
    ///
    /// ### Example
    ///
    /// ```
    /// use crdt::set::GSet;
    ///
    /// let mut set = GSet::<i32>::new();
    /// assert!(set.is_empty());
    /// ```
    pub fn new() -> GSet<T> {
        GSet { elements: HashSet::new() }
    }

    /// Insert an element into a grow-only set.
    ///
    /// ### Example
    ///
    /// ```
    /// use crdt::set::GSet;
    ///
    /// let mut set = GSet::new();
    /// set.insert("first-element");
    /// assert!(set.contains(&"first-element"));
    /// ```
    pub fn insert(&mut self, element: T) -> Option<GSetOp<T>> {
        if self.elements.insert(element.clone()) {
            Some(GSetOp { element: element })
        } else {
            None
        }
    }

    /// Returns the number of elements in the set.
    pub fn len(&self) -> usize {
        self.elements.len()
    }

    /// Returns true if the set contains the value.
    pub fn contains(&self, value: &T) -> bool {
        self.elements.contains(value)
    }

    /// Returns true if the set contains no elements.
    pub fn is_empty(&self) -> bool { self.elements.is_empty() }

    pub fn is_subset(&self, other: &GSet<T>) -> bool {
        self.elements.is_subset(&other.elements)
    }

    pub fn is_disjoint(&self, other: &GSet<T>) -> bool {
        self.elements.is_disjoint(&other.elements)
    }
}

impl <T> Crdt for GSet<T> where T: Clone + Eq + Hash {

    type Operation = GSetOp<T>;

    /// Merge a replica into the set.
    ///
    /// This method is used to perform state-based replication.
    ///
    /// ##### Example
    ///
    /// ```
    /// # use crdt::set::GSet;
    /// use crdt::Crdt;
    ///
    /// let mut local = GSet::new();
    /// let mut remote = GSet::new();
    ///
    /// local.insert(1i32);
    /// remote.insert(2);
    ///
    /// local.merge(remote);
    /// assert!(local.contains(&2));
    /// ```
    fn merge(&mut self, other: GSet<T>) {
        self.elements.extend(other.elements.into_iter());
    }

    /// Apply an insert operation to the set.
    ///
    /// This method is used to perform operation-based replication.
    ///
    /// Applying an operation to a `GSet` is idempotent.
    ///
    /// ##### Example
    ///
    /// ```
    /// # use crdt::set::GSet;
    /// # use crdt::Crdt;
    /// let mut local = GSet::new();
    /// let mut remote = GSet::new();
    ///
    /// let op = remote.insert(13i32).expect("GSet should be empty.");
    ///
    /// local.apply(op);
    /// assert!(local.contains(&13));
    /// ```
    fn apply(&mut self, op: GSetOp<T>) {
        self.insert(op.element);
    }
}

impl <T> PartialEq for GSet<T> where T: Eq + Hash {
    fn eq(&self, other: &GSet<T>) -> bool {
        self.elements == other.elements
    }
}

impl <T> Eq for GSet<T> where T: Eq + Hash {}

impl <T> PartialOrd for GSet<T> where T: Eq + Hash {
    fn partial_cmp(&self, other: &GSet<T>) -> Option<Ordering> {
        if self.elements == other.elements {
            Some(Equal)
        } else if self.elements.is_subset(&other.elements) {
            Some(Less)
        } else if self.elements.is_superset(&other.elements) {
            Some(Greater)
        } else {
            None
        }
    }
}

impl <T> Clone for GSet<T> where T: Clone + Eq + Hash {
    fn clone(&self) -> GSet<T> {
        GSet { elements: self.elements.clone() }
    }
}

#[cfg(any(quickcheck, test))]
impl <T> Arbitrary for GSet<T> where T: Arbitrary + Clone + Eq + Hash {
    fn arbitrary<G>(g: &mut G) -> GSet<T> where G: Gen {
        let elements: Vec<T> = Arbitrary::arbitrary(g);
        GSet { elements: elements.into_iter().collect() }
    }
    fn shrink(&self) -> Box<Iterator<Item=GSet<T>> + 'static> {
        let elements: Vec<T> = self.elements.iter().cloned().collect();
        Box::new(elements.shrink().map(|es| GSet { elements: es.into_iter().collect() }))
    }
}

#[cfg(any(quickcheck, test))]
impl <T> Arbitrary for GSetOp<T> where T: Arbitrary {
    fn arbitrary<G: Gen>(g: &mut G) -> GSetOp<T> {
        GSetOp { element: Arbitrary::arbitrary(g) }
    }
    fn shrink(&self) -> Box<Iterator<Item=GSetOp<T>> + 'static> {
        Box::new(self.element.shrink().map(|e| GSetOp { element: e }))
    }
}

#[cfg(test)]
mod test {

    use quickcheck::quickcheck;

    use {Crdt, test};
    use super::{GSet, GSetOp};

    type C = GSet<u32>;
    type O = GSetOp<u32>;

    #[test]
    fn check_apply_is_commutative() {
        quickcheck(test::apply_is_commutative::<C> as fn(C, Vec<O>) -> bool);
    }

    #[test]
    fn check_merge_is_commutative() {
        quickcheck(test::merge_is_commutative::<C> as fn(C, Vec<C>) -> bool);
    }

    #[test]
    fn check_ordering_lte() {
        quickcheck(test::ordering_lte::<C> as fn(C, C) -> bool);
    }

    #[test]
    fn check_ordering_equality() {
        quickcheck(test::ordering_equality::<C> as fn(C, C) -> bool);
    }

    #[quickcheck]
    fn check_local_insert(elements: Vec<u8>) -> bool {
        let mut set = GSet::new();
        for element in elements.clone().into_iter() {
            set.insert(element);
        }

        elements.iter().all(|element| set.contains(element))
    }

    #[quickcheck]
    fn check_ordering_lt(mut a: GSet<u8>, b: GSet<u8>) -> bool {
        a.merge(b.clone());

        let mut i = 0;
        let mut success = None;
        while success.is_none() {
            success = a.insert(i);
            i += 1;
        }
        a > b && b < a
    }
}
<|endoftext|>


<commit_before>use std::cmp::min;
use std::fmt::{self, Debug, Formatter};
use std::iter::IteratorExt;
use std::collections::{BTreeMap, RingBuf};
use std::string::ToString;
use serialize::json::Json;

use self::TemplateElement::{RawString, Expression, HelperExpression,
                            HTMLExpression, HelperBlock, Comment};

#[derive(PartialEq, Clone)]
pub struct Template {
    pub elements: Vec<TemplateElement>
}

#[derive(PartialEq, Debug)]
enum ParserState {
    Text,
    HtmlExpression,
    Comment,
    HelperStart,
    HelperEnd,
    Expression,
    Invalid
}

#[derive(PartialEq, Clone)]
pub struct Helper {
    name: String,
    params: Vec<String>,
    hash: BTreeMap<String, Json>,
    template: Option<Template>,
    inverse: Option<Template>,
    block: bool
}

impl Helper {
    pub fn name(&self) -> &String {
        &self.name
    }

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

    pub fn hash(&self) -> &BTreeMap<String, Json> {
        &self.hash
    }

    pub fn template(&self) -> Option<&Template> {
        match self.template {
            Some(ref t) => {
                Some(t)
            },
            None => None
        }
    }

    pub fn inverse(&self) -> Option<&Template> {
        match self.inverse {
            Some(ref t) => {
                Some(t)
            },
            None => None
        }
    }

    pub fn is_block(&self) -> bool {
        self.block
    }
}

impl ToString for Helper {
    fn to_string(&self) -> String {

        let mut buf = String::new();

        if self.block {
            buf.push_str(format!("{{{{#{}", self.name).as_slice());
        } else {
            buf.push_str(format!("{{{{{}", self.name).as_slice());
        }

        for p in self.params.iter() {
            buf.push_str(format!(" {}", p).as_slice());
        }

        for k in self.hash.keys() {
            buf.push_str(format!(" {}={}", k, self.hash.get(k).unwrap()).as_slice());
        }

        buf.push_str("}}");

        if self.block {
            let tpl = self.template();
            if tpl.is_some() {
                buf.push_str(tpl.unwrap().to_string().as_slice());
            }
            let ivs = self.inverse();
            if ivs.is_some() {
                buf.push_str("{{else}}");
                buf.push_str(ivs.unwrap().to_string().as_slice());
            }
            buf.push_str(format!("{{{{/{}}}}}", self.name).as_slice());
        }
        buf
    }
}

#[derive(Debug, Copy)]
pub struct TemplateError;

impl Helper {
    fn parse(source: String, block: bool) -> Result<Helper, TemplateError> {
        let mut tokens = source.split(|&: c: char| c.is_whitespace())
            .filter(|s: &&str| !(*s).is_empty());

        let name = tokens.next();
        match name {
            Some(n) => {
                let mut params: Vec<String> = Vec::new();
                let mut hash: BTreeMap<String, Json> = BTreeMap::new();

                for t in tokens {
                    if t.contains_char('=') {
                        let kv = t.split('=').collect::<Vec<&str>>();
                        hash.insert(kv.get(0).unwrap().to_string(),
                                    kv.get(1).unwrap().parse::<Json>().unwrap());
                    } else {
                        params.push(t.to_string());
                    }
                }

                Ok(Helper{
                    name: n.to_string(),
                    params: params,
                    hash: hash,
                    template: Option::None,
                    inverse: Option::None,
                    block: block
                })
            },
            None =>
                Err(TemplateError)
        }
    }
}

impl Template {
    pub fn compile(source: String) -> Result<Template, TemplateError> {
        let mut helper_stack: RingBuf<Helper> = RingBuf::new();
        let mut template_stack: RingBuf<Template> = RingBuf::new();
        template_stack.push_front(Template{ elements: Vec::new() });

        let mut buffer: String = String::new();
        let mut state = ParserState::Text;

        let mut c:usize = 0;
        let mut ws_omitter = false;
        let source_len = source.chars().count();
        while c < source_len {
            let mut slice = source.slice_chars(c, min(c+3, source_len));
            if slice == "{{~" {
                ws_omitter = true;
                // read another char and remove ~
                slice = source.slice_chars(c, min(c+4, source_len));
                c += 1;
            }
            state = match slice.replace("~", "").as_slice() {
                "{{{" | "{{!" | "{{#" | "{{/" => {
                    c += 2;
                    if !buffer.is_empty() {
                        let mut t = template_stack.front_mut().unwrap();
                        let buf_clone = if ws_omitter {
                            ws_omitter = false;
                            String::from_str(buffer.trim_right())
                        } else {
                            buffer.clone()
                        };
                        t.elements.push(RawString(buf_clone));
                        buffer.clear();
                    }
                    match slice {
                        "{{{" => ParserState::HtmlExpression,
                        "{{!" => ParserState::Comment,
                        "{{#" => ParserState::HelperStart,
                        "{{/" => {
                            let t = template_stack.pop_front().unwrap();
                            let h = helper_stack.front_mut().unwrap();
                            if h.template.is_some() {
                                h.inverse = Some(t);
                            } else {
                                h.template = Some(t);
                            }
                            ParserState::HelperEnd
                        },
                        _ => ParserState::Invalid
                    }
                },
                "}}}" => {
                    c += 2;
                    let mut t = template_stack.front_mut().unwrap();
                    t.elements.push(HTMLExpression(
                        buffer.clone().trim_matches(' ').to_string()));
                    buffer.clear();
                    ParserState::Text
                },
                _ => {
                    match if slice.chars().count() > 2 { slice.slice_chars(0, 2) } else { slice } {
                        "{{" => {
                            c += 1;
                            if !buffer.is_empty() {
                                let mut t = template_stack.front_mut().unwrap();
                                let buf_clone = if ws_omitter {
                                    ws_omitter = false;
                                    String::from_str(buffer.trim_right())
                                } else {
                                    buffer.clone()
                                };
                                t.elements.push(RawString(buf_clone));
                                buffer.clear();
                            }
                            ParserState::Expression
                        },
                        "}}" => {
                            c += 1;
                            match state {
                                ParserState::Expression => {
                                    if !buffer.is_empty() {
                                        // {{else}} within a helper block
                                        if buffer.trim_matches(' ') == "else" {
                                            buffer.clear(); // drop else
                                            let t = template_stack.pop_front().unwrap();
                                            let h = helper_stack.front_mut().unwrap();
                                            h.template = Some(t);
                                            template_stack.push_front(Template{ elements: Vec::new() });
                                            ParserState::Text
                                        } else if buffer.contains_char(' ') {
                                            //inline helper
                                            match Helper::parse(buffer.clone(), false){
                                                Ok(helper) => {
                                                    let mut t = template_stack.front_mut().unwrap();
                                                    t.elements.push(HelperExpression(helper));
                                                    buffer.clear();
                                                    ParserState::Text
                                                },
                                                Err(_) => ParserState::Invalid
                                            }
                                        } else {
                                            let mut t = template_stack.front_mut().unwrap();
                                            t.elements.push(Expression(
                                                buffer.clone().trim_matches(' ').to_string()));
                                            buffer.clear();
                                            ParserState::Text
                                        }

                                    } else {
                                        ParserState::Invalid
                                    }
                                },
                                ParserState::Comment => {
                                    let mut t = template_stack.front_mut().unwrap();
                                    t.elements.push(Comment(buffer.clone()));
                                    buffer.clear();
                                    ParserState::Text
                                },
                                ParserState::HelperStart => {
                                    match Helper::parse(buffer.clone(), true) {
                                        Ok(helper) => {
                                            helper_stack.push_front(helper);
                                            template_stack.push_front(Template{ elements: Vec::new() });

                                            buffer.clear();
                                            ParserState::Text
                                        },
                                        Err(_) => ParserState::Invalid
                                    }
                                },
                                ParserState::HelperEnd => {
                                    let name = buffer.trim_matches(' ').to_string();
                                    if name == helper_stack.front().unwrap().name {
                                        let h = helper_stack.pop_front().unwrap();
                                        let mut t = template_stack.front_mut().unwrap();
                                        t.elements.push(HelperBlock(h));
                                        buffer.clear();
                                        ParserState::Text
                                    } else {
                                        ParserState::Invalid
                                    }
                                },
                                _ => ParserState::Invalid
                            }
                        },
                        _ => {
                            buffer.push(slice.char_at(0));
                            state
                        }
                    }
                }
            };
            if state == ParserState::Invalid {
                return Err(TemplateError);
            }
            c += 1;
        }

        if !buffer.is_empty() {
            let mut t = template_stack.front_mut().unwrap();
            let buf_clone = if ws_omitter {
                String::from_str(buffer.trim_right())
            } else {
                buffer.clone()
            };
            t.elements.push(TemplateElement::RawString(buf_clone));
        }

        if !helper_stack.is_empty() {
            return Err(TemplateError);
        }

        return Ok(template_stack.pop_front().unwrap());
    }
}

impl ToString for Template {
    fn to_string(&self) -> String {
        let mut buf = String::new();
        for v in self.elements.iter() {
            buf.push_str(v.to_string().as_slice());
        }
        buf
    }
}

#[derive(PartialEq, Clone)]
pub enum TemplateElement {
    RawString(String),
    Expression(String),
    HTMLExpression(String),
    HelperExpression(Helper),
    HelperBlock(Helper),
    Comment(String),
}

impl ToString for TemplateElement {
    fn to_string(&self) -> String {
        match *self {
            RawString(ref v) => {
                v.clone()
            },
            Expression(ref v) => {
                // {{ escape to {
                format!("{{{{{}}}}}", v)
            },
            HTMLExpression(ref v) => {
                format!("{{{{{{{}}}}}}}", v)
            },
            HelperExpression(ref helper) => {
                helper.to_string()
            }
            HelperBlock(ref helper) => {
                helper.to_string()
            }
            Comment(ref v) => {
                format!("{{!{}}}", v)
            }
        }
    }
}

impl Debug for TemplateElement {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        try!(writeln!(f, "{:?}", self.to_string()));
        Ok(())
    }
}

#[test]
fn test_parse_helper_start_tag() {
    let source = "if not name compare=1".to_string();
    let h = Helper::parse(source, true).ok().unwrap();

    assert_eq!(h.name, "if".to_string());
    assert_eq!(h.params, vec::<String>!["not".to_string(), "name".to_string()]);

    let key = "compare".to_string();
    let value = h.hash.get(&key).unwrap();
    assert_eq!(value.as_u64().unwrap(), 1u64);
}

#[test]
fn test_parse_template() {
    let source = "
{{title}} 你好
 {{{content}}}
{{#if date}}
good
{{else}}
bad
{{/if}}{{foo bar}}";
    let t = Template::compile(source.to_string()).ok().unwrap();

    assert_eq!(t.elements.len(), 8);
    assert_eq!((*t.elements.get(0).unwrap()).to_string(), "
".to_string());
    assert_eq!((*t.elements.get(1).unwrap()).to_string(),
               Expression("title".to_string()).to_string());

    assert_eq!((*t.elements.get(3).unwrap()).to_string(), "{{{content}}}".to_string());

    match *t.elements.get(5).unwrap() {
        HelperBlock(ref h) => {
            assert_eq!(h.name, "if".to_string());
            assert_eq!(h.params.len(), 1);
            assert_eq!(h.template.as_ref().unwrap().elements.len(), 1);
        },
        _ => {
            panic!("Helper expected here.");
        }
    };

    match *t.elements.get(7).unwrap() {
        HelperExpression(ref h) => {
            assert_eq!(h.name, "foo".to_string());
            assert_eq!(h.params.len(), 1);
            assert_eq!(*(h.params.get(0).unwrap()), "bar".to_string());
        },
        _ => {
            panic!("Helper expression here");
        }
    };
}

#[test]
fn test_helper_to_string() {
    let source = "{{#ifequals name compare=\"hello\"}}hello{{else}}good{{/ifequals}}".to_string();

    let t = Template::compile(source.to_string()).ok().unwrap();

    assert_eq!(t.elements.len(), 1);
    assert_eq!(t.elements.get(0).unwrap().to_string(), source);
}

#[test]
fn test_parse_error() {
    let source = "{{#ifequals name compare=\"hello\"}}hello{{else}}good";

    let t = Template::compile(source.to_string());

    assert!(t.is_err());
}

#[test]
fn test_white_space_omitter_right() {
    let source = "hello     {{~world}}".to_string();
    let t = Template::compile(source).ok().unwrap();

    assert_eq!(t.elements.len(), 2);

    assert_eq!(t.elements[0], RawString(String::from_str("hello")));
    assert_eq!(t.elements[1], Expression(String::from_str("world")));
}
<commit_msg>(feat) added support for left-side whitespace omitting [#10]<commit_after>use std::cmp::min;
use std::fmt::{self, Debug, Formatter};
use std::iter::IteratorExt;
use std::collections::{BTreeMap, RingBuf};
use std::string::ToString;
use serialize::json::Json;

use self::TemplateElement::{RawString, Expression, HelperExpression,
                            HTMLExpression, HelperBlock, Comment};

#[derive(PartialEq, Clone)]
pub struct Template {
    pub elements: Vec<TemplateElement>
}

#[derive(PartialEq, Debug)]
enum ParserState {
    Text,
    HtmlExpression,
    Comment,
    HelperStart,
    HelperEnd,
    Expression,
    Invalid
}

#[derive(PartialEq, Clone)]
pub struct Helper {
    name: String,
    params: Vec<String>,
    hash: BTreeMap<String, Json>,
    template: Option<Template>,
    inverse: Option<Template>,
    block: bool
}

impl Helper {
    pub fn name(&self) -> &String {
        &self.name
    }

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

    pub fn hash(&self) -> &BTreeMap<String, Json> {
        &self.hash
    }

    pub fn template(&self) -> Option<&Template> {
        match self.template {
            Some(ref t) => {
                Some(t)
            },
            None => None
        }
    }

    pub fn inverse(&self) -> Option<&Template> {
        match self.inverse {
            Some(ref t) => {
                Some(t)
            },
            None => None
        }
    }

    pub fn is_block(&self) -> bool {
        self.block
    }
}

impl ToString for Helper {
    fn to_string(&self) -> String {

        let mut buf = String::new();

        if self.block {
            buf.push_str(format!("{{{{#{}", self.name).as_slice());
        } else {
            buf.push_str(format!("{{{{{}", self.name).as_slice());
        }

        for p in self.params.iter() {
            buf.push_str(format!(" {}", p).as_slice());
        }

        for k in self.hash.keys() {
            buf.push_str(format!(" {}={}", k, self.hash.get(k).unwrap()).as_slice());
        }

        buf.push_str("}}");

        if self.block {
            let tpl = self.template();
            if tpl.is_some() {
                buf.push_str(tpl.unwrap().to_string().as_slice());
            }
            let ivs = self.inverse();
            if ivs.is_some() {
                buf.push_str("{{else}}");
                buf.push_str(ivs.unwrap().to_string().as_slice());
            }
            buf.push_str(format!("{{{{/{}}}}}", self.name).as_slice());
        }
        buf
    }
}

#[derive(Debug, Copy)]
pub struct TemplateError;

impl Helper {
    fn parse(source: String, block: bool) -> Result<Helper, TemplateError> {
        let mut tokens = source.split(|&: c: char| c.is_whitespace())
            .filter(|s: &&str| !(*s).is_empty());

        let name = tokens.next();
        match name {
            Some(n) => {
                let mut params: Vec<String> = Vec::new();
                let mut hash: BTreeMap<String, Json> = BTreeMap::new();

                for t in tokens {
                    if t.contains_char('=') {
                        let kv = t.split('=').collect::<Vec<&str>>();
                        hash.insert(kv.get(0).unwrap().to_string(),
                                    kv.get(1).unwrap().parse::<Json>().unwrap());
                    } else {
                        params.push(t.to_string());
                    }
                }

                Ok(Helper{
                    name: n.to_string(),
                    params: params,
                    hash: hash,
                    template: Option::None,
                    inverse: Option::None,
                    block: block
                })
            },
            None =>
                Err(TemplateError)
        }
    }
}

enum WhiteSpaceOmit {
    Left,
    Right,
    None
}

fn process_whitespace(buf: &String, wso: &mut WhiteSpaceOmit) -> String {
    let result = match *wso {
        WhiteSpaceOmit::Left => {
            buf.trim_left().to_string()
        },
        WhiteSpaceOmit::Right => {
            buf.trim_right().to_string()
        },
        WhiteSpaceOmit::None => {
            buf.clone()
        }
    };
    *wso = WhiteSpaceOmit::None;
    result
}

impl Template {
    pub fn compile(source: String) -> Result<Template, TemplateError> {
        let mut helper_stack: RingBuf<Helper> = RingBuf::new();
        let mut template_stack: RingBuf<Template> = RingBuf::new();
        template_stack.push_front(Template{ elements: Vec::new() });

        let mut buffer: String = String::new();
        let mut state = ParserState::Text;

        let mut c:usize = 0;
        let mut ws_omitter = WhiteSpaceOmit::None;
        let source_len = source.chars().count();
        while c < source_len {
            let mut slice = source.slice_chars(c, min(c+3, source_len));
            if slice == "{{~" {
                ws_omitter = WhiteSpaceOmit::Right;
                // read another char and remove ~
                slice = source.slice_chars(c, min(c+4, source_len));
                c += 1;
            }
            if slice == "~}}" {
                ws_omitter = WhiteSpaceOmit::Left;
                c += 1;
                slice = source.slice_chars(c, min(c+4, source_len));
            }
            println!("{}", slice);
            state = match slice.replace("~", "").as_slice() {
                "{{{" | "{{!" | "{{#" | "{{/" => {
                    c += 2;
                    if !buffer.is_empty() {
                        let mut t = template_stack.front_mut().unwrap();
                        let buf_clone = process_whitespace(&buffer, &mut ws_omitter);
                        t.elements.push(RawString(buf_clone));
                        buffer.clear();
                    }
                    match slice {
                        "{{{" => ParserState::HtmlExpression,
                        "{{!" => ParserState::Comment,
                        "{{#" => ParserState::HelperStart,
                        "{{/" => {
                            let t = template_stack.pop_front().unwrap();
                            let h = helper_stack.front_mut().unwrap();
                            if h.template.is_some() {
                                h.inverse = Some(t);
                            } else {
                                h.template = Some(t);
                            }
                            ParserState::HelperEnd
                        },
                        _ => ParserState::Invalid
                    }
                },
                "}}}" => {
                    c += 2;
                    let mut t = template_stack.front_mut().unwrap();
                    t.elements.push(HTMLExpression(
                        buffer.clone().trim_matches(' ').to_string()));
                    buffer.clear();
                    ParserState::Text
                },
                _ => {
                    match if slice.chars().count() > 2 { slice.slice_chars(0, 2) } else { slice } {
                        "{{" => {
                            c += 1;
                            if !buffer.is_empty() {
                                let mut t = template_stack.front_mut().unwrap();
                                let buf_clone = process_whitespace(&buffer, &mut ws_omitter);
                                t.elements.push(RawString(buf_clone));
                                buffer.clear();
                            }
                            ParserState::Expression
                        },
                        "}}" => {
                            c += 1;
                            match state {
                                ParserState::Expression => {
                                    if !buffer.is_empty() {
                                        // {{else}} within a helper block
                                        if buffer.trim_matches(' ') == "else" {
                                            buffer.clear(); // drop else
                                            let t = template_stack.pop_front().unwrap();
                                            let h = helper_stack.front_mut().unwrap();
                                            h.template = Some(t);
                                            template_stack.push_front(Template{ elements: Vec::new() });
                                            ParserState::Text
                                        } else if buffer.contains_char(' ') {
                                            //inline helper
                                            match Helper::parse(buffer.clone(), false){
                                                Ok(helper) => {
                                                    let mut t = template_stack.front_mut().unwrap();
                                                    t.elements.push(HelperExpression(helper));
                                                    buffer.clear();
                                                    ParserState::Text
                                                },
                                                Err(_) => ParserState::Invalid
                                            }
                                        } else {
                                            let mut t = template_stack.front_mut().unwrap();
                                            t.elements.push(Expression(
                                                buffer.clone().trim_matches(' ').to_string()));
                                            buffer.clear();
                                            ParserState::Text
                                        }

                                    } else {
                                        ParserState::Invalid
                                    }
                                },
                                ParserState::Comment => {
                                    let mut t = template_stack.front_mut().unwrap();
                                    t.elements.push(Comment(buffer.clone()));
                                    buffer.clear();
                                    ParserState::Text
                                },
                                ParserState::HelperStart => {
                                    match Helper::parse(buffer.clone(), true) {
                                        Ok(helper) => {
                                            helper_stack.push_front(helper);
                                            template_stack.push_front(Template{ elements: Vec::new() });

                                            buffer.clear();
                                            ParserState::Text
                                        },
                                        Err(_) => ParserState::Invalid
                                    }
                                },
                                ParserState::HelperEnd => {
                                    let name = buffer.trim_matches(' ').to_string();
                                    if name == helper_stack.front().unwrap().name {
                                        let h = helper_stack.pop_front().unwrap();
                                        let mut t = template_stack.front_mut().unwrap();
                                        t.elements.push(HelperBlock(h));
                                        buffer.clear();
                                        ParserState::Text
                                    } else {
                                        ParserState::Invalid
                                    }
                                },
                                _ => ParserState::Invalid
                            }
                        },
                        _ => {
                            buffer.push(slice.char_at(0));
                            state
                        }
                    }
                }
            };
            if state == ParserState::Invalid {
                return Err(TemplateError);
            }
            c += 1;
        }

        if !buffer.is_empty() {
            let mut t = template_stack.front_mut().unwrap();
            let buf_clone = process_whitespace(&buffer, &mut ws_omitter);
            t.elements.push(TemplateElement::RawString(buf_clone));
        }

        if !helper_stack.is_empty() {
            return Err(TemplateError);
        }

        return Ok(template_stack.pop_front().unwrap());
    }
}

impl ToString for Template {
    fn to_string(&self) -> String {
        let mut buf = String::new();
        for v in self.elements.iter() {
            buf.push_str(v.to_string().as_slice());
        }
        buf
    }
}

#[derive(PartialEq, Clone)]
pub enum TemplateElement {
    RawString(String),
    Expression(String),
    HTMLExpression(String),
    HelperExpression(Helper),
    HelperBlock(Helper),
    Comment(String),
}

impl ToString for TemplateElement {
    fn to_string(&self) -> String {
        match *self {
            RawString(ref v) => {
                v.clone()
            },
            Expression(ref v) => {
                // {{ escape to {
                format!("{{{{{}}}}}", v)
            },
            HTMLExpression(ref v) => {
                format!("{{{{{{{}}}}}}}", v)
            },
            HelperExpression(ref helper) => {
                helper.to_string()
            }
            HelperBlock(ref helper) => {
                helper.to_string()
            }
            Comment(ref v) => {
                format!("{{!{}}}", v)
            }
        }
    }
}

impl Debug for TemplateElement {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        try!(writeln!(f, "{:?}", self.to_string()));
        Ok(())
    }
}

#[test]
fn test_parse_helper_start_tag() {
    let source = "if not name compare=1".to_string();
    let h = Helper::parse(source, true).ok().unwrap();

    assert_eq!(h.name, "if".to_string());
    assert_eq!(h.params, vec::<String>!["not".to_string(), "name".to_string()]);

    let key = "compare".to_string();
    let value = h.hash.get(&key).unwrap();
    assert_eq!(value.as_u64().unwrap(), 1u64);
}

#[test]
fn test_parse_template() {
    let source = "
{{title}} 你好
 {{{content}}}
{{#if date}}
good
{{else}}
bad
{{/if}}{{foo bar}}";
    let t = Template::compile(source.to_string()).ok().unwrap();

    assert_eq!(t.elements.len(), 8);
    assert_eq!((*t.elements.get(0).unwrap()).to_string(), "
".to_string());
    assert_eq!((*t.elements.get(1).unwrap()).to_string(),
               Expression("title".to_string()).to_string());

    assert_eq!((*t.elements.get(3).unwrap()).to_string(), "{{{content}}}".to_string());

    match *t.elements.get(5).unwrap() {
        HelperBlock(ref h) => {
            assert_eq!(h.name, "if".to_string());
            assert_eq!(h.params.len(), 1);
            assert_eq!(h.template.as_ref().unwrap().elements.len(), 1);
        },
        _ => {
            panic!("Helper expected here.");
        }
    };

    match *t.elements.get(7).unwrap() {
        HelperExpression(ref h) => {
            assert_eq!(h.name, "foo".to_string());
            assert_eq!(h.params.len(), 1);
            assert_eq!(*(h.params.get(0).unwrap()), "bar".to_string());
        },
        _ => {
            panic!("Helper expression here");
        }
    };
}

#[test]
fn test_helper_to_string() {
    let source = "{{#ifequals name compare=\"hello\"}}hello{{else}}good{{/ifequals}}".to_string();

    let t = Template::compile(source.to_string()).ok().unwrap();

    assert_eq!(t.elements.len(), 1);
    assert_eq!(t.elements.get(0).unwrap().to_string(), source);
}

#[test]
fn test_parse_error() {
    let source = "{{#ifequals name compare=\"hello\"}}hello{{else}}good";

    let t = Template::compile(source.to_string());

    assert!(t.is_err());
}

#[test]
fn test_white_space_omitter() {
    let source = "hello~     {{~world~}} \n  !".to_string();
    let t = Template::compile(source).ok().unwrap();

    assert_eq!(t.elements.len(), 3);

    assert_eq!(t.elements[0], RawString(String::from_str("hello~")));
    assert_eq!(t.elements[1], Expression(String::from_str("world")));
    assert_eq!(t.elements[2], RawString(String::from_str("!")));
}
<|endoftext|>


<commit_before>#![crate_id(name="uutils", vers="1.0.0", author="Michael Gehring")]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Michael Gehring <mg@ebfe.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;

@CRATES@

use std::os;
use std::collections::hashmap::HashMap;

static NAME: &'static str = "uutils";
static VERSION: &'static str = "1.0.0";

fn util_map() -> HashMap<&str, fn(Vec<String>) -> int> {
    fn uutrue(_: Vec<String>) -> int { 0 }
    fn uufalse(_: Vec<String>) -> int { 1 }

    let mut map = HashMap::<&str, fn(Vec<String>) -> int>::new();
    @UTIL_MAP@
    map
}

fn usage(cmap: &HashMap<&str, fn(Vec<String>) -> int>) {
    println!("{} {}", NAME, VERSION);
    println!("");
    println!("Usage:");
    println!("  {} [util [arguments...]", NAME);
    println!("Currently defined functions:");
    let mut utils: Vec<&str> = cmap.keys().map(|&s| s).collect();
    utils.sort();
    for util in utils.iter() {
        println!("\t{}", util);
    }
    println!("");
}

fn main() {
    let umap = util_map();
    let mut args = os::args();

    // try binary name as util name.
    let binary = Path::new(args.get(0).as_slice());
    let binary_as_util = binary.filename_str().unwrap();
    if umap.contains_key(&binary_as_util) {
        let &uumain = umap.get(&binary_as_util);
        os::set_exit_status(uumain(args));
        return
    } else if binary_as_util.starts_with("uutils")
        || binary_as_util.starts_with("busybox") {
            // uutils can be called as either "uutils", "busybox"
            // "uutils-suffix" or "busybox-suffix". Not sure
            // what busybox uses the -suffix pattern for.
    } else {
        println!("{}: applet not found", binary_as_util);
        os::set_exit_status(1);
        return
    }

    // try first arg as util name.
    if args.len() >= 2 {
        args.shift();
        let util = args.get(0).as_slice();
        if umap.contains_key(&util) {
            let &uumain = umap.get(&util);
            os::set_exit_status(uumain(args.clone()));
            return
        } else if args.get(0).as_slice() == "--help" {
            // see if they want help on a specific util
            if args.len() >= 2 {
                let util = args.get(1).as_slice();
                if umap.contains_key(&util) {
                    let &uumain = umap.get(&util);
                    os::set_exit_status(uumain(vec!["--help".to_string()]));
                    return
                } else {
                    println!("{}: applet not found", util);
                    os::set_exit_status(1);
                    return
                }
            }
            usage(&umap);
            os::set_exit_status(0);
            return
        } else {
            println!("{}: applet not found", util);
            os::set_exit_status(1);
            return
        }
    } else {
        // no arguments provided
        usage(&umap);
        os::set_exit_status(0);
        return
    }
}
<commit_msg>uutils: fix hashmap lifetime<commit_after>#![crate_id(name="uutils", vers="1.0.0", author="Michael Gehring")]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Michael Gehring <mg@ebfe.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;

@CRATES@

use std::os;
use std::collections::hashmap::HashMap;

static NAME: &'static str = "uutils";
static VERSION: &'static str = "1.0.0";

fn util_map() -> HashMap<&'static str, fn(Vec<String>) -> int> {
    fn uutrue(_: Vec<String>) -> int { 0 }
    fn uufalse(_: Vec<String>) -> int { 1 }

    let mut map = HashMap::new();
    @UTIL_MAP@
    map
}

fn usage(cmap: &HashMap<&'static str, fn(Vec<String>) -> int>) {
    println!("{} {}", NAME, VERSION);
    println!("");
    println!("Usage:");
    println!("  {} [util [arguments...]", NAME);
    println!("Currently defined functions:");
    let mut utils: Vec<&str> = cmap.keys().map(|&s| s).collect();
    utils.sort();
    for util in utils.iter() {
        println!("\t{}", util);
    }
    println!("");
}

fn main() {
    let umap = util_map();
    let mut args = os::args();

    // try binary name as util name.
    let binary = Path::new(args.get(0).as_slice());
    let binary_as_util = binary.filename_str().unwrap();

    match umap.find_equiv(&binary_as_util) {
        Some(&uumain) => {
            os::set_exit_status(uumain(args));
            return
        }
        None => (),
    }

    if binary_as_util.starts_with("uutils")
        || binary_as_util.starts_with("busybox") {
            // uutils can be called as either "uutils", "busybox"
            // "uutils-suffix" or "busybox-suffix". Not sure
            // what busybox uses the -suffix pattern for.
    } else {
        println!("{}: applet not found", binary_as_util);
        os::set_exit_status(1);
        return
    }

    // try first arg as util name.
    if args.len() >= 2 {
        args.shift();
        let util = args.get(0).as_slice();

        match umap.find_equiv(&util) {
            Some(&uumain) => {
                os::set_exit_status(uumain(args.clone()));
                return
            }
            None => {
                if args.get(0).as_slice() == "--help" {
                    // see if they want help on a specific util
                    if args.len() >= 2 {
                        let util = args.get(1).as_slice();
                        match umap.find_equiv(&util) {
                            Some(&uumain) => {
                                os::set_exit_status(uumain(vec!["--help".to_string()]));
                                return
                            }
                            None => {
                                println!("{}: applet not found", util);
                                os::set_exit_status(1);
                                return
                            }
                        }
                    }
                    usage(&umap);
                    os::set_exit_status(0);
                    return
                } else {
                    println!("{}: applet not found", util);
                    os::set_exit_status(1);
                    return
                }
            }
        }
    } else {
        // no arguments provided
        usage(&umap);
        os::set_exit_status(0);
        return
    }
}
<|endoftext|>


<commit_before>#![feature(phase)]
#[phase(plugin, link)] extern crate log;
extern crate url;

use std::from_str::from_str;
use std::io::net::tcp::{TcpListener,TcpStream};
use std::io::{Acceptor,Listener,Command};
use std::string::String;
use url::Url;

// Note: Error handling could be improved
fn main() {
    let browserCommand = "firefox";
    let address = "127.0.0.1";
    let port = 7777;

    // Prepare a socket listening on localhost:7777
    let listener = TcpListener::bind(address, port).unwrap();
    let mut acceptor = listener.listen().unwrap();

    // Infinite loop to keep handling new connections.
    loop {
        let tcpStream = acceptor.accept().unwrap();
        debug!("Accepted new connection");
        spawn(proc() {
            handle_client(tcpStream, browserCommand)
        });
    }
}

// Accept a new connection and listen for URLs
fn handle_client(tcpStream: TcpStream, browserCommand: &'static str) {
    debug!("Spawned new task");

    let mut tcpStream = tcpStream;

    // Note that as soon as read_to_str() returns, everything sent
    // to the socket after this point will be discarded as once
    // we're done working with the content we've just read, the
    // tcpStream will be freed.
    let message = tcpStream.read_to_str().unwrap();

    // Iterate over the lines in the received message
    for line in message.as_slice().split('\n') {
        debug!("Current line is: {}", line);

        // This tries to convert the line to a Url struct
        let url = from_str(line);

        // On failure it returns None; on success a valid URL
        match url {
            None     => { info!("No Url found") }
            Some(u)  => {
                debug!("Found Url in: {}", line);
                if check_url(&u) {
                    spawn_process(&u, browserCommand)
                }
            }
        }
    }
}

// Check that the URL is actually usable
fn check_url(u: &Url) -> bool {
    (u.scheme == String::from_str("http") ||
     u.scheme == String::from_str("https"))
        && !u.host.is_empty()
}

// Spawn a browser to access the URL
fn spawn_process(u: &Url, command: &'static str) {
    debug!("Spawning process: {} {}", command, u.to_str());
    let mut child = match Command::new(command).arg(u.to_str()).spawn() {
        Ok(child) => child,
        Err(e)    => fail!("Failed to spawn process: {}. {}", command, e),
    };
    child.wait().unwrap().success();
}
<commit_msg>Update for Rust 0.12<commit_after>#![feature(phase)]
#[phase(plugin, link)] extern crate log;
extern crate url;

use std::from_str::from_str;
use std::io::net::tcp::{TcpListener,TcpStream};
use std::io::{Acceptor,Listener,Command};
use std::string::String;
use url::Url;

// Note: Error handling could be improved
fn main() {
    let browser_command = "firefox";
    let address = "127.0.0.1";
    let port = 7777;

    // Prepare a socket listening on localhost:7777
    let listener = TcpListener::bind(address, port).unwrap();
    let mut acceptor = listener.listen().unwrap();

    // Infinite loop to keep handling new connections.
    loop {
        let tcp_stream = acceptor.accept().unwrap();
        debug!("Accepted new connection");
        spawn(proc() {
            handle_client(tcp_stream, browser_command)
        });
    }
}

// Accept a new connection and listen for URLs
fn handle_client(stream: TcpStream, browser_command: &'static str) {
    debug!("Spawned new task");

    let mut tcp_stream = stream;

    // Note that as soon as read_to_str() returns, everything sent
    // to the socket after this point will be discarded as once
    // we're done working with the content we've just read, the
    // tcpStream will be freed.
    let message = tcp_stream.read_to_string().unwrap();

    // Iterate over the lines in the received message
    for line in message.as_slice().split('\n') {
        debug!("Current line is: {}", line);

        // This tries to convert the line to a Url struct
        let url = from_str(line);

        // On failure it returns None; on success a valid URL
        match url {
            None     => { info!("No Url found") }
            Some(u)  => {
                debug!("Found Url in: {}", line);
                if check_url(&u) {
                    spawn_process(&u, browser_command)
                }
            }
        }
    }
}

// Check that the URL is actually usable
fn check_url(u: &Url) -> bool {
    (u.scheme == String::from_str("http") ||
     u.scheme == String::from_str("https"))
        && !u.host.is_empty()
}

// Spawn a browser to access the URL
fn spawn_process(u: &Url, command: &'static str) {
    debug!("Spawning process: {} {}", command, u);
    let url = format!("{}", u);
    let mut child = match Command::new(command).arg(url).spawn() {
        Ok(child) => child,
        Err(e)    => fail!("Failed to spawn process: {}. {}", command, e),
    };
    child.wait().unwrap().success();
}
<|endoftext|>


<commit_before>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Core/device_manager.rs
// - Core device manager

use _common::*;
use sync::Mutex;
use lib::Queue;

module_define!(DeviceManager, [], init)

pub enum IOBinding
{
	IOBindMemory(::memory::virt::AllocHandle),
	IOBindIO(u16,u16),
}

trait BusManager
{
}

pub trait BusDevice
{
	fn get_attr(name: &str) -> u32;
	fn bind_io(block_id: uint) -> IOBinding;
}

pub trait DriverInstance
{
}

struct Device
{
	bus_dev: Box<BusDevice+'static>,
	driver: Option<Box<DriverInstance+'static>>,
	attribs: Vec<u32>,
}

struct Bus
{
	manager: &'static BusManager+'static,
	devices: Vec<Device>,
}

static s_root_busses: Mutex<Queue<Bus>> = mutex_init!(queue_init!());

fn init()
{
	// Do nothing!
}

//pub fn register_bus(devices: Vec<Box<BusDevice+'static>>)
//{
//}

// vim: ft=rust
<commit_msg>Kernel/DevManager - Continued design work, hacked up device register<commit_after>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Core/device_manager.rs
// - Core device manager

use _common::*;
use sync::Mutex;
use lib::Queue;

module_define!(DeviceManager, [], init)

pub enum IOBinding
{
	IOBindMemory(::memory::virt::AllocHandle),
	IOBindIO(u16,u16),
}

pub trait BusManager
{
	fn get_attr_names() -> &[&str];
}

pub trait BusDevice
{
	fn get_attr(name: &str) -> u32;
	fn set_power(state: bool);	// TODO: Power state enum for Off,Standby,Low,On
	fn bind_io(block_id: uint) -> IOBinding;
}

pub trait Driver
{

}

pub trait DriverInstance
{
}

struct Device
{
	bus_dev: Box<BusDevice+'static>,
	driver: Option<Box<DriverInstance+'static>>,
	attribs: Vec<u32>,
}

struct Bus
{
	manager: &'static BusManager+'static,
	devices: Vec<Device>,
}

#[allow(non_upper_case_globals)]
static s_root_busses: Mutex<Queue<Bus>> = mutex_init!(queue_init!());

//#[allow(non_upper_case_globals)]
//static s_driver_list: Mutex<Vec<&'static Driver>> = mutex_init!( Vec::new() );

fn init()
{
	// Do nothing!
}

pub fn register_bus(manager: &'static BusManager+'static, devices: Vec<Box<BusDevice+'static>>)
{
	let bus = Bus {
		manager: manager,
		devices: devices.into_iter().map(|d| Device {
			driver: find_driver(&*d),
			bus_dev: d,
			attribs: Vec::new(),
			}).collect(),
		};
	s_root_busses.lock().push(bus);
}

pub fn register_driver(driver: &'static Driver+'static)
{

}

fn find_driver(bus_dev: &BusDevice) -> Option<Box<DriverInstance+'static>>
{
	/*
	for driver in s_driver_list.lock().items()
	{
		if driver.handles(bus_dev) {
			return Some( driver.bind(bus_dev) );
		}
	}
	*/
	None
}

// vim: ft=rust
<|endoftext|>


<commit_before>use crate::utils::span_lint;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// **What it does:** Detects giving a mutable reference to a function that only
    /// requires an immutable reference.
    ///
    /// **Why is this bad?** The immutable reference rules out all other references
    /// to the value. Also the code misleads about the intent of the call site.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```ignore
    /// my_vec.push(&mut value)
    /// ```
    pub UNNECESSARY_MUT_PASSED,
    style,
    "an argument passed as a mutable reference although the callee only demands an immutable reference"
}

declare_lint_pass!(UnnecessaryMutPassed => [UNNECESSARY_MUT_PASSED]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnecessaryMutPassed {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
        match e.kind {
            ExprKind::Call(ref fn_expr, ref arguments) => {
                if let ExprKind::Path(ref path) = fn_expr.kind {
                    check_arguments(
                        cx,
                        arguments,
                        cx.tables.expr_ty(fn_expr),
                        &rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)),
                    );
                }
            },
            ExprKind::MethodCall(ref path, _, ref arguments) => {
                let def_id = cx.tables.type_dependent_def_id(e.hir_id).unwrap();
                let substs = cx.tables.node_substs(e.hir_id);
                let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs);
                check_arguments(cx, arguments, method_type, &path.ident.as_str())
            },
            _ => (),
        }
    }
}

fn check_arguments<'a, 'tcx>(
    cx: &LateContext<'a, 'tcx>,
    arguments: &[Expr<'_>],
    type_definition: Ty<'tcx>,
    name: &str,
) {
    match type_definition.kind {
        ty::FnDef(..) | ty::FnPtr(_) => {
            let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs();
            for (argument, parameter) in arguments.iter().zip(parameters.iter()) {
                match parameter.kind {
                    ty::Ref(_, _, Mutability::Not)
                    | ty::RawPtr(ty::TypeAndMut {
                        mutbl: Mutability::Not, ..
                    }) => {
                        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = argument.kind {
                            span_lint(
                                cx,
                                UNNECESSARY_MUT_PASSED,
                                argument.span,
                                &format!("The function/method `{}` doesn't need a mutable reference", name),
                            );
                        }
                    },
                    _ => (),
                }
            }
        },
        _ => (),
    }
}
<commit_msg>Clarify the documentation of the `unnecessary_mut_passed` lint<commit_after>use crate::utils::span_lint;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// **What it does:** Detects passing a mutable reference to a function that only
    /// requires an immutable reference.
    ///
    /// **Why is this bad?** The immutable reference rules out all other references
    /// to the value. Also the code misleads about the intent of the call site.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```ignore
    /// my_vec.push(&mut value)
    /// ```
    pub UNNECESSARY_MUT_PASSED,
    style,
    "an argument passed as a mutable reference although the callee only demands an immutable reference"
}

declare_lint_pass!(UnnecessaryMutPassed => [UNNECESSARY_MUT_PASSED]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnecessaryMutPassed {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
        match e.kind {
            ExprKind::Call(ref fn_expr, ref arguments) => {
                if let ExprKind::Path(ref path) = fn_expr.kind {
                    check_arguments(
                        cx,
                        arguments,
                        cx.tables.expr_ty(fn_expr),
                        &rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)),
                    );
                }
            },
            ExprKind::MethodCall(ref path, _, ref arguments) => {
                let def_id = cx.tables.type_dependent_def_id(e.hir_id).unwrap();
                let substs = cx.tables.node_substs(e.hir_id);
                let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs);
                check_arguments(cx, arguments, method_type, &path.ident.as_str())
            },
            _ => (),
        }
    }
}

fn check_arguments<'a, 'tcx>(
    cx: &LateContext<'a, 'tcx>,
    arguments: &[Expr<'_>],
    type_definition: Ty<'tcx>,
    name: &str,
) {
    match type_definition.kind {
        ty::FnDef(..) | ty::FnPtr(_) => {
            let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs();
            for (argument, parameter) in arguments.iter().zip(parameters.iter()) {
                match parameter.kind {
                    ty::Ref(_, _, Mutability::Not)
                    | ty::RawPtr(ty::TypeAndMut {
                        mutbl: Mutability::Not, ..
                    }) => {
                        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = argument.kind {
                            span_lint(
                                cx,
                                UNNECESSARY_MUT_PASSED,
                                argument.span,
                                &format!("The function/method `{}` doesn't need a mutable reference", name),
                            );
                        }
                    },
                    _ => (),
                }
            }
        },
        _ => (),
    }
}
<|endoftext|>


<commit_before>use state::{Selectable, Editable, Named};
use std::cmp;
use std::io::{stdout, Stdout, Write, Result};
use std::iter;
use termion::terminal_size;
use termion::input::MouseTerminal;
use termion::raw::{IntoRawMode, RawTerminal};
use termion::{clear, style, cursor, color};

pub struct View {
    stdout: MouseTerminal<RawTerminal<Stdout>>,
    message: Option<String>,
    is_prompt: bool,
    line_offset: u16,
}

const TAB_LENGTH: usize = 4;

impl View {
    pub fn new() -> Result<View> {
        let mut stdout = MouseTerminal::from(stdout().into_raw_mode().unwrap());
        try!(write!(stdout, "{}{}", clear::All, cursor::Show));
        Ok(View {
            stdout: stdout,
            message: None,
            is_prompt: false,
            line_offset: 0,
        })
    }

    pub fn message(&mut self, message: &str) {
        self.is_prompt = false;
        self.message = Some(String::from(message));
    }

    pub fn prompt(&mut self, prompt: &str, message: &str) {
        self.is_prompt = true;
        let msg = String::from(prompt) + message;
        self.message = Some(msg);
    }

    pub fn quiet(&mut self) {
        self.is_prompt = false;
        self.message = None;
    }

    pub fn center_view(&mut self, line: usize) {
        self.line_offset = match line.checked_sub(self.lines_height() as usize / 2) {
            None => 0,
            Some(val) => val as u16,
        }
    }

    pub fn adjust_view(&mut self, line: usize) {
        let line = line as u16;
        if line < self.line_offset {
            self.line_offset = line;
        } else if line + 1 >= self.line_offset + self.lines_height() {
            self.line_offset = 1 + line - self.lines_height();
        }
    }

    pub fn scroll_view<T: Editable>(&mut self, offset: isize, content: &T) {
        self.line_offset =
            cmp::min(cmp::max((self.line_offset as isize) + offset, 0),
                     (content.line_count() as isize) - 1) as u16;

    }

    pub fn render<T>(&mut self, content: &T) -> Result<()>
        where T: Editable + Named + Selectable
    {
        try!(write!(self.stdout, "{}", clear::All));
        try!(self.paint_lines(content));
        try!(self.paint_status(content));
        try!(self.paint_message());
        try!(self.paint_cursor(content));
        try!(self.stdout.flush());
        Ok(())
    }

    pub fn translate_coordinates<T>(&self, content: &T, x: u16, y: u16) -> (usize, usize)
        where T: Editable
    {
        let line = y as isize + self.line_offset as isize - 1;
        let col = cmp::max(0,
                           x as isize - self.line_number_width(content.line_count()) as isize - 2);
        (line as usize, col as usize)
    }

    fn paint_message(&mut self) -> Result<()> {
        if let Some(ref message) = self.message {
            let y = self.lines_height() + 1;
            try!(write!(self.stdout, "{}{}", cursor::Goto(1, 1 + y as u16), message));
            try!(self.stdout.flush());
        }
        Ok(())
    }

    fn paint_cursor<T>(&mut self, content: &T) -> Result<()>
        where T: Editable + Selectable
    {
        if (content.line() as u16) < self.line_offset ||
           content.line() as u16 >= self.line_offset + self.lines_height() ||
           content.sel().is_some() {
            try!(write!(self.stdout, "{}", cursor::Hide));
            return Ok(());
        }

        // in the case of a prompt, the cursor should be drawn in the message line
        let (x, y) = if self.is_prompt {
            (self.message.clone().unwrap().len() as u16, self.lines_height() + 1)
        } else {
            let (a, b) = self.cursor_pos(content);
            (a as u16, b as u16)
        };
        try!(write!(self.stdout,
                    "{}{}",
                    cursor::Show,
                    cursor::Goto(1 + x, 1 + y)));
        Ok(())
    }

    fn paint_status<T>(&mut self, content: &T) -> Result<()>
        where T: Editable + Named
    {
        let line = content.line();
        let column = content.col();
        let line_count = content.line_count();
        let advance = ((line + 1) as f64 / line_count as f64 * 100.0).floor();

        let (screen_width, _) = terminal_size().unwrap();
        let empty_line = (0..screen_width).map(|_| ' ').collect::<String>();
        let y = self.lines_height() as u16;

        try!(write!(self.stdout,
                    "{}{}{}{}{}{}",
                    color::Fg(color::White),
                    style::Invert,
                    cursor::Goto(1, 1 + y),
                    empty_line,
                    cursor::Goto(1, 1 + y),
                    content.name()));

        let position_info = format!("{}% {}/{}: {}", advance, line + 1, line_count, column);
        let x = screen_width - position_info.len() as u16;
        try!(write!(self.stdout,
                    "{}{}{}",
                    cursor::Goto(1 + x, 1 + y),
                    position_info,
                    style::Reset));
        Ok(())
    }

    fn paint_lines<T>(&mut self, content: &T) -> Result<()>
        where T: Editable + Selectable
    {
        let line_offset = self.line_offset;
        let line_height = self.lines_height();
        let line_count = content.line_count();

        let window_it = content.iter()
                .take(content.len() - 1) // the last endline should be invisible to the user
                .scan(('\n', 0, 1), |state, k| {
                    {
                        let (ref mut c, ref mut chars, ref mut lines) = *state;
                        if *c == '\n' {
                            *lines += 1;
                        }
                        *chars += 1;
                        *c = k;
                    }
                    Some(*state)
                })
                .skip_while(|&(_, _, lines)| lines <= 1 + line_offset)
                .take_while(|&(_, _, lines)| lines <= 1 + line_offset + line_height);

        {
            let line_start = self.line_number_width(line_count) as u16 + 1;
            try!(write!(self.stdout,
                        "{}{}{}{}{}",
                        color::Fg(color::White),
                        cursor::Goto(1, 1),
                        line_offset + 1,
                        style::Reset,
                        cursor::Goto(1 + line_start, 1)));
        }
        let mut y = 1;
        let mut line_len = 0;
        for (c, chars, lines) in window_it {
            if c == '\n' {
                if line_len == 0 && content.in_sel(chars) {
                    try!(write!(self.stdout, "{} {}", color::Bg(color::White), style::Reset));
                }
                line_len = 0;
                let line_start = self.line_number_width(line_count) as u16 + 1;
                try!(write!(self.stdout,
                            "{}{}{}{}{}",
                            color::Fg(color::White),
                            cursor::Goto(1, 1 + y),
                            lines,
                            style::Reset,
                            cursor::Goto(1 + line_start, 1 + y)));
                y += 1;
            } else {
                if content.in_sel(chars) {
                    try!(write!(self.stdout, "{}", style::Invert));
                }
                if c == '\t' {
                    // FIXME: we should probably use gotos instead of relying on character length
                    try!(write!(self.stdout,
                                "{}",
                                iter::repeat(" ").take(TAB_LENGTH).collect::<String>()));
                } else {
                    try!(write!(self.stdout, "{}", c));
                }
                try!(write!(self.stdout, "{}", style::Reset));
                line_len += 1;
            }
        }
        Ok(())
    }

    fn cursor_pos<T: Editable>(&self, content: &T) -> (usize, usize) {
        // TODO: column offsetting for long lines
        let line = content.line();
        let first_line = self.line_offset;
        let y = line - first_line as usize;
        // we can't trust the actual column because tabs have variable length
        let column = content.visual_col(TAB_LENGTH);
        ((self.line_number_width(content.line_count()) as usize + 1 + column), y)
    }

    fn line_number_width(&self, line_count: usize) -> u16 {
        let max_in_window = self.line_offset + self.lines_height() + 2;
        let max = cmp::min(max_in_window, line_count as u16);
        max.to_string().len() as u16
    }

    fn status_height(&self) -> u16 {
        2
    }

    pub fn lines_height(&self) -> u16 {
        let (_, screen_height) = terminal_size().unwrap();
        cmp::max(screen_height, self.status_height()) - self.status_height()
    }
}
<commit_msg>fixed color of empty selected lines<commit_after>use state::{Selectable, Editable, Named};
use std::cmp;
use std::io::{stdout, Stdout, Write, Result};
use std::iter;
use termion::terminal_size;
use termion::input::MouseTerminal;
use termion::raw::{IntoRawMode, RawTerminal};
use termion::{clear, style, cursor, color};

pub struct View {
    stdout: MouseTerminal<RawTerminal<Stdout>>,
    message: Option<String>,
    is_prompt: bool,
    line_offset: u16,
}

const TAB_LENGTH: usize = 4;

impl View {
    pub fn new() -> Result<View> {
        let mut stdout = MouseTerminal::from(stdout().into_raw_mode().unwrap());
        try!(write!(stdout, "{}{}", clear::All, cursor::Show));
        Ok(View {
            stdout: stdout,
            message: None,
            is_prompt: false,
            line_offset: 0,
        })
    }

    pub fn message(&mut self, message: &str) {
        self.is_prompt = false;
        self.message = Some(String::from(message));
    }

    pub fn prompt(&mut self, prompt: &str, message: &str) {
        self.is_prompt = true;
        let msg = String::from(prompt) + message;
        self.message = Some(msg);
    }

    pub fn quiet(&mut self) {
        self.is_prompt = false;
        self.message = None;
    }

    pub fn center_view(&mut self, line: usize) {
        self.line_offset = match line.checked_sub(self.lines_height() as usize / 2) {
            None => 0,
            Some(val) => val as u16,
        }
    }

    pub fn adjust_view(&mut self, line: usize) {
        let line = line as u16;
        if line < self.line_offset {
            self.line_offset = line;
        } else if line + 1 >= self.line_offset + self.lines_height() {
            self.line_offset = 1 + line - self.lines_height();
        }
    }

    pub fn scroll_view<T: Editable>(&mut self, offset: isize, content: &T) {
        self.line_offset =
            cmp::min(cmp::max((self.line_offset as isize) + offset, 0),
                     (content.line_count() as isize) - 1) as u16;

    }

    pub fn render<T>(&mut self, content: &T) -> Result<()>
        where T: Editable + Named + Selectable
    {
        try!(write!(self.stdout, "{}", clear::All));
        try!(self.paint_lines(content));
        try!(self.paint_status(content));
        try!(self.paint_message());
        try!(self.paint_cursor(content));
        try!(self.stdout.flush());
        Ok(())
    }

    pub fn translate_coordinates<T>(&self, content: &T, x: u16, y: u16) -> (usize, usize)
        where T: Editable
    {
        let line = y as isize + self.line_offset as isize - 1;
        let col = cmp::max(0,
                           x as isize - self.line_number_width(content.line_count()) as isize - 2);
        (line as usize, col as usize)
    }

    fn paint_message(&mut self) -> Result<()> {
        if let Some(ref message) = self.message {
            let y = self.lines_height() + 1;
            try!(write!(self.stdout, "{}{}", cursor::Goto(1, 1 + y as u16), message));
            try!(self.stdout.flush());
        }
        Ok(())
    }

    fn paint_cursor<T>(&mut self, content: &T) -> Result<()>
        where T: Editable + Selectable
    {
        if (content.line() as u16) < self.line_offset ||
           content.line() as u16 >= self.line_offset + self.lines_height() ||
           content.sel().is_some() {
            try!(write!(self.stdout, "{}", cursor::Hide));
            return Ok(());
        }

        // in the case of a prompt, the cursor should be drawn in the message line
        let (x, y) = if self.is_prompt {
            (self.message.clone().unwrap().len() as u16, self.lines_height() + 1)
        } else {
            let (a, b) = self.cursor_pos(content);
            (a as u16, b as u16)
        };
        try!(write!(self.stdout,
                    "{}{}",
                    cursor::Show,
                    cursor::Goto(1 + x, 1 + y)));
        Ok(())
    }

    fn paint_status<T>(&mut self, content: &T) -> Result<()>
        where T: Editable + Named
    {
        let line = content.line();
        let column = content.col();
        let line_count = content.line_count();
        let advance = ((line + 1) as f64 / line_count as f64 * 100.0).floor();

        let (screen_width, _) = terminal_size().unwrap();
        let empty_line = (0..screen_width).map(|_| ' ').collect::<String>();
        let y = self.lines_height() as u16;

        try!(write!(self.stdout,
                    "{}{}{}{}{}{}",
                    color::Fg(color::White),
                    style::Invert,
                    cursor::Goto(1, 1 + y),
                    empty_line,
                    cursor::Goto(1, 1 + y),
                    content.name()));

        let position_info = format!("{}% {}/{}: {}", advance, line + 1, line_count, column);
        let x = screen_width - position_info.len() as u16;
        try!(write!(self.stdout,
                    "{}{}{}",
                    cursor::Goto(1 + x, 1 + y),
                    position_info,
                    style::Reset));
        Ok(())
    }

    fn paint_lines<T>(&mut self, content: &T) -> Result<()>
        where T: Editable + Selectable
    {
        let line_offset = self.line_offset;
        let line_height = self.lines_height();
        let line_count = content.line_count();

        let window_it = content.iter()
                .take(content.len() - 1) // the last endline should be invisible to the user
                .scan(('\n', 0, 1), |state, k| {
                    {
                        let (ref mut c, ref mut chars, ref mut lines) = *state;
                        if *c == '\n' {
                            *lines += 1;
                        }
                        *chars += 1;
                        *c = k;
                    }
                    Some(*state)
                })
                .skip_while(|&(_, _, lines)| lines <= 1 + line_offset)
                .take_while(|&(_, _, lines)| lines <= 1 + line_offset + line_height);

        {
            let line_start = self.line_number_width(line_count) as u16 + 1;
            try!(write!(self.stdout,
                        "{}{}{}{}{}",
                        color::Fg(color::White),
                        cursor::Goto(1, 1),
                        line_offset + 1,
                        style::Reset,
                        cursor::Goto(1 + line_start, 1)));
        }
        let mut y = 1;
        let mut line_len = 0;
        for (c, chars, lines) in window_it {
            if c == '\n' {
                if line_len == 0 && content.in_sel(chars) {
                    try!(write!(self.stdout, "{} {}", style::Invert, style::Reset));
                }
                line_len = 0;
                let line_start = self.line_number_width(line_count) as u16 + 1;
                try!(write!(self.stdout,
                            "{}{}{}{}{}",
                            color::Fg(color::White),
                            cursor::Goto(1, 1 + y),
                            lines,
                            style::Reset,
                            cursor::Goto(1 + line_start, 1 + y)));
                y += 1;
            } else {
                if content.in_sel(chars) {
                    try!(write!(self.stdout, "{}", style::Invert));
                }
                if c == '\t' {
                    // FIXME: we should probably use gotos instead of relying on character length
                    try!(write!(self.stdout,
                                "{}",
                                iter::repeat(" ").take(TAB_LENGTH).collect::<String>()));
                } else {
                    try!(write!(self.stdout, "{}", c));
                }
                try!(write!(self.stdout, "{}", style::Reset));
                line_len += 1;
            }
        }
        Ok(())
    }

    fn cursor_pos<T: Editable>(&self, content: &T) -> (usize, usize) {
        // TODO: column offsetting for long lines
        let line = content.line();
        let first_line = self.line_offset;
        let y = line - first_line as usize;
        // we can't trust the actual column because tabs have variable length
        let column = content.visual_col(TAB_LENGTH);
        ((self.line_number_width(content.line_count()) as usize + 1 + column), y)
    }

    fn line_number_width(&self, line_count: usize) -> u16 {
        let max_in_window = self.line_offset + self.lines_height() + 2;
        let max = cmp::min(max_in_window, line_count as u16);
        max.to_string().len() as u16
    }

    fn status_height(&self) -> u16 {
        2
    }

    pub fn lines_height(&self) -> u16 {
        let (_, screen_height) = terminal_size().unwrap();
        cmp::max(screen_height, self.status_height()) - self.status_height()
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

mod board;
mod sys;

use imgui::{ImGuiCond, Ui};
use imgui_sys;

use model::{Color, FieldCoord, Model};
use vec2::Vec2;
use self::board::board;
pub use self::sys::run;

pub enum Event {
    Click(FieldCoord),
    NewGame,
}

pub fn draw(ui: &Ui, size: (f32, f32), model: &Model) -> Option<Event> {
    unsafe {
        imgui_sys::igPushStyleVar(imgui_sys::ImGuiStyleVar::WindowRounding, 0.0);
    }

    let mut event = None;

    ui.main_menu_bar(|| {
        ui.menu(im_str!("Game")).build(|| {
            if ui.menu_item(im_str!("New game")).build() {
                event = Some(Event::NewGame);
            }
        });
    });

    ui.window(im_str!("Coerceo"))
        .size(size, ImGuiCond::Always)
        .position((0.0, 19.0), ImGuiCond::Once)
        .title_bar(false)
        .resizable(false)
        .movable(false)
        .build(|| {
            ui.text("Welcome to Coerceo!");

            if event.is_none() {
                event = board(model, Vec2::new(size.0 - 16.0, size.1 - 100.0));
            } else {
                board(model, Vec2::new(size.0 - 16.0, size.1 - 100.0));
            }

            if model.white_pieces == 0 {
                ui.text("Black wins!");
            } else if model.black_pieces == 0 {
                ui.text("White wins!");
            } else {
                match model.turn {
                    Color::White => ui.text("It's white's turn."),
                    Color::Black => ui.text("It's black's turn."),
                }

                ui.text(format!(
                    "White has {} piece(s) left and {} captured hex(es).",
                    model.white_pieces, model.white_hexes,
                ));
                ui.text(format!(
                    "Black has {} piece(s) left and {} captured hex(es).",
                    model.black_pieces, model.black_hexes,
                ));
            }
        });

    unsafe {
        imgui_sys::igPopStyleVar(1);
    }

    event
}
<commit_msg>Replace .is_none checking with insert_if_empty<commit_after>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

mod board;
mod sys;

use imgui::{ImGuiCond, Ui};
use imgui_sys;

use model::{Color, FieldCoord, Model};
use vec2::Vec2;
use self::board::board;
pub use self::sys::run;

pub enum Event {
    Click(FieldCoord),
    NewGame,
}

pub fn draw(ui: &Ui, size: (f32, f32), model: &Model) -> Option<Event> {
    unsafe {
        imgui_sys::igPushStyleVar(imgui_sys::ImGuiStyleVar::WindowRounding, 0.0);
    }

    let mut event = None;

    ui.main_menu_bar(|| {
        ui.menu(im_str!("Game")).build(|| {
            if ui.menu_item(im_str!("New game")).build() {
                insert_if_empty(&mut event, Some(Event::NewGame));
            }
        });
    });

    ui.window(im_str!("Coerceo"))
        .size(size, ImGuiCond::Always)
        .position((0.0, 19.0), ImGuiCond::Once)
        .title_bar(false)
        .resizable(false)
        .movable(false)
        .build(|| {
            ui.text("Welcome to Coerceo!");

            insert_if_empty(
                &mut event,
                board(model, Vec2::new(size.0 - 16.0, size.1 - 100.0)),
            );

            if model.white_pieces == 0 {
                ui.text("Black wins!");
            } else if model.black_pieces == 0 {
                ui.text("White wins!");
            } else {
                match model.turn {
                    Color::White => ui.text("It's white's turn."),
                    Color::Black => ui.text("It's black's turn."),
                }

                ui.text(format!(
                    "White has {} piece(s) left and {} captured hex(es).",
                    model.white_pieces, model.white_hexes,
                ));
                ui.text(format!(
                    "Black has {} piece(s) left and {} captured hex(es).",
                    model.black_pieces, model.black_hexes,
                ));
            }
        });

    unsafe {
        imgui_sys::igPopStyleVar(1);
    }

    event
}

fn insert_if_empty<T>(a: &mut Option<T>, b: Option<T>) {
    if a.is_none() {
        *a = b;
    }
}
<|endoftext|>