<commit_before>use bson::Bson;
use std::collections::BTreeMap;
use std::iter::{FromIterator, Map};

/// A BSON document represented as an associative BTree Map with insertion ordering.
#[derive(Debug, Clone)]
pub struct OrderedDocument {
    pub keys: Vec<String>,
    document: BTreeMap<String, Bson>,
}

/// An iterator over OrderedDocument entries.
#[derive(Clone)]
pub struct OrderedDocumentIntoIterator {
    ordered_document: OrderedDocument,
    index: usize,
}

/// An owning iterator over OrderedDocument entries.
#[derive(Clone)]
pub struct OrderedDocumentIterator<'a> {
    ordered_document: &'a OrderedDocument,
    index: usize,
}

/// An iterator over an OrderedDocument's keys.
pub struct Keys<'a> {
    inner: Map<OrderedDocumentIterator<'a>, fn((&'a String, &'a Bson)) -> &'a String>
}

/// An iterator over an OrderedDocument's values.
pub struct Values<'a> {
    inner: Map<OrderedDocumentIterator<'a>, fn((&'a String, &'a Bson)) -> &'a Bson>
}

impl<'a> Clone for Keys<'a> {
    fn clone(&self) -> Keys<'a> { Keys { inner: self.inner.clone() } }
}

impl<'a> Iterator for Keys<'a> {
    type Item = &'a String;
    fn next(&mut self) -> Option<(&'a String)> { self.inner.next() }
}

impl<'a> Clone for Values<'a> {
    fn clone(&self) -> Values<'a> { Values { inner: self.inner.clone() } }
}

impl<'a> Iterator for Values<'a> {
    type Item = &'a Bson;

    fn next(&mut self) -> Option<(&'a Bson)> { self.inner.next() }
}

impl IntoIterator for OrderedDocument {
    type Item = (String, Bson);
    type IntoIter = OrderedDocumentIntoIterator;

    fn into_iter(self) -> Self::IntoIter {
        OrderedDocumentIntoIterator { ordered_document: self, index: 0 }
    }
}

impl<'a> IntoIterator for &'a OrderedDocument {
    type Item = (&'a String, &'a Bson);
    type IntoIter = OrderedDocumentIterator<'a>;

    fn into_iter(self) -> Self::IntoIter {
        OrderedDocumentIterator { ordered_document: self, index: 0 }
    }
}

impl FromIterator<(String, Bson)> for OrderedDocument {
    fn from_iter<T: IntoIterator<Item=(String, Bson)>>(iter: T) -> Self {
        let mut doc = OrderedDocument::new();
        for (k, v) in iter {
            doc.insert(k, v.to_owned());
        }
        doc
    }
}

impl<'a> Iterator for OrderedDocumentIntoIterator {
    type Item = (String, Bson);
    fn next(&mut self) -> Option<(String, Bson)> {
        if self.ordered_document.keys.len() <= self.index {
            return None;
        }

        let ref key = self.ordered_document.keys[self.index];
        let val = self.ordered_document.get(&key[..]).unwrap();
        self.index += 1;
        Some((key.to_owned(), val.to_owned()))
    }
}

impl<'a> Iterator for OrderedDocumentIterator<'a> {
    type Item = (&'a String, &'a Bson);
    fn next(&mut self) -> Option<(&'a String, &'a Bson)> {
        if self.ordered_document.keys.len() <= self.index {
            return None;
        }

        let ref key = self.ordered_document.keys[self.index];
        let val = self.ordered_document.get(&key[..]).unwrap();
        self.index += 1;
        Some((key, val))
    }
}

impl OrderedDocument {
    /// Creates a new empty OrderedDocument.
    pub fn new() -> OrderedDocument {
        OrderedDocument {
            keys: Vec::new(),
            document: BTreeMap::new(),
        }
    }

    /// Gets an iterator over the entries of the map.
    pub fn iter<'a>(&'a self) -> OrderedDocumentIterator<'a> {
        self.into_iter()
    }

    /// Clears the document, removing all values.
    pub fn clear(&mut self) {
        self.keys.clear();
        self.document.clear();
    }

    /// Returns a reference to the Bson corresponding to the key.
    pub fn get(&self, key: &str) -> Option<&Bson> {
        self.document.get(key)
    }

    /// Gets a mutable reference to the value in the entry.
    pub fn get_mut(&mut self, key: &str) -> Option<&mut Bson> {
        self.document.get_mut(key)
    }

    /// Returns true if the map contains a value for the specified key.
    pub fn contains_key(&self, key: &str) -> bool {
        self.document.contains_key(key)
    }

    /// Returns the position of the key in the ordered vector, if it exists.
    pub fn position(&self, key: &str) -> Option<usize> {
        self.keys.iter().position(|x| x == key)
    }

    /// Gets a collection of all keys in the document.
    pub fn keys<'a>(&'a self) -> Keys<'a> {
        fn first<A, B>((a, _): (A, B)) -> A { a }
        let first: fn((&'a String, &'a Bson)) -> &'a String = first;

        Keys { inner: self.iter().map(first) }
    }

    /// Gets a collection of all values in the document.
    pub fn values<'a>(&'a self) -> Values<'a> {
        fn second<A, B>((_, b): (A, B)) -> B { b }
        let second: fn((&'a String, &'a Bson)) -> &'a Bson = second;

        Values { inner: self.iter().map(second) }
    }

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

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

    /// Sets the value of the entry with the OccupiedEntry's key,
    /// and returns the entry's old value.
    pub fn insert(&mut self, key: String, val: Bson) -> Option<Bson> {
        let key_slice = &key[..];

        if self.contains_key(key_slice) {
            let position = self.position(key_slice).unwrap();
            self.keys.remove(position);
        }

        self.keys.push(key.to_owned());
        self.document.insert(key.to_owned(), val.to_owned())
    }

    /// Takes the value of the entry out of the document, and returns it.
    pub fn remove(&mut self, key: &str) -> Option<Bson> {
        let position = self.position(key);
        if position.is_some() {
            self.keys.remove(position.unwrap());
        }
        self.document.remove(key)
    }
}

#[cfg(test)]
mod test {
    use super::OrderedDocument;
    use bson::Bson;

    #[test]
    fn ordered_insert() {
        let mut doc = OrderedDocument::new();
        doc.insert("first".to_owned(), Bson::I32(1));
        doc.insert("second".to_owned(), Bson::String("foo".to_owned()));
        doc.insert("alphanumeric".to_owned(), Bson::String("bar".to_owned()));

        let expected_keys = vec!(
            "first".to_owned(),
            "second".to_owned(),
            "alphanumeric".to_owned(),
        );

        let keys: Vec<_> = doc.iter().map(|(key, _)| key.to_owned()).collect();
        assert_eq!(expected_keys, keys);
    }

    #[test]
    fn remove() {
        let mut doc = OrderedDocument::new();
        doc.insert("first".to_owned(), Bson::I32(1));
        doc.insert("second".to_owned(), Bson::String("foo".to_owned()));
        doc.insert("alphanumeric".to_owned(), Bson::String("bar".to_owned()));

        assert!(doc.remove("second").is_some());
        assert!(doc.remove("none").is_none());

        let expected_keys = vec!(
            "first",
            "alphanumeric",
        );

        let keys: Vec<_> = doc.iter().map(|(key, _)| key.to_owned()).collect();
        assert_eq!(expected_keys, keys);
    }
}
<commit_msg>Use Vec iterators internally<commit_after>use bson::Bson;
use std::collections::BTreeMap;
use std::iter::{FromIterator, Map};
use std::vec::IntoIter;
use std::slice;

/// A BSON document represented as an associative BTree Map with insertion ordering.
#[derive(Debug, Clone)]
pub struct OrderedDocument {
    pub keys: Vec<String>,
    document: BTreeMap<String, Bson>,
}

/// An iterator over OrderedDocument entries.
pub struct OrderedDocumentIntoIterator {
    vec_iter: IntoIter<String>,
    document: BTreeMap<String, Bson>,
}

/// An owning iterator over OrderedDocument entries.
pub struct OrderedDocumentIterator<'a> {
    vec_iter: slice::Iter<'a, String>,
    document: &'a BTreeMap<String, Bson>,
}

/// An iterator over an OrderedDocument's keys.
pub struct Keys<'a> {
    inner: Map<OrderedDocumentIterator<'a>, fn((&'a String, &'a Bson)) -> &'a String>
}

/// An iterator over an OrderedDocument's values.
pub struct Values<'a> {
    inner: Map<OrderedDocumentIterator<'a>, fn((&'a String, &'a Bson)) -> &'a Bson>
}

impl<'a> Iterator for Keys<'a> {
    type Item = &'a String;
    fn next(&mut self) -> Option<(&'a String)> { self.inner.next() }
}

impl<'a> Iterator for Values<'a> {
    type Item = &'a Bson;

    fn next(&mut self) -> Option<(&'a Bson)> { self.inner.next() }
}

impl IntoIterator for OrderedDocument {
    type Item = (String, Bson);
    type IntoIter = OrderedDocumentIntoIterator;

    fn into_iter(self) -> Self::IntoIter {
        OrderedDocumentIntoIterator {
            document: self.document,
            vec_iter: self.keys.into_iter()
        }
    }
}

impl<'a> IntoIterator for &'a OrderedDocument {
    type Item = (&'a String, &'a Bson);
    type IntoIter = OrderedDocumentIterator<'a>;

    fn into_iter(self) -> Self::IntoIter {
        let ref keys = self.keys;
        OrderedDocumentIterator {
            vec_iter: keys.into_iter(),
            document: &self.document,
        }
    }
}

impl FromIterator<(String, Bson)> for OrderedDocument {
    fn from_iter<T: IntoIterator<Item=(String, Bson)>>(iter: T) -> Self {
        let mut doc = OrderedDocument::new();
        for (k, v) in iter {
            doc.insert(k, v.to_owned());
        }
        doc
    }
}

impl<'a> Iterator for OrderedDocumentIntoIterator {
    type Item = (String, Bson);
    fn next(&mut self) -> Option<(String, Bson)> {
        match self.vec_iter.next() {
            Some(key) => {
                let val = self.document.get(&key[..]).unwrap();
                Some((key, val.to_owned()))
            },
            None => None,
        }
    }
}

impl<'a> Iterator for OrderedDocumentIterator<'a> {
    type Item = (&'a String, &'a Bson);
    fn next(&mut self) -> Option<(&'a String, &'a Bson)> {
        match self.vec_iter.next() {
            Some(key) => Some((&key, self.document.get(&key[..]).unwrap())),
            None => None,
        }
    }
}

impl OrderedDocument {
    /// Creates a new empty OrderedDocument.
    pub fn new() -> OrderedDocument {
        OrderedDocument {
            keys: Vec::new(),
            document: BTreeMap::new(),
        }
    }

    /// Gets an iterator over the entries of the map.
    pub fn iter<'a>(&'a self) -> OrderedDocumentIterator<'a> {
        self.into_iter()
    }

    /// Clears the document, removing all values.
    pub fn clear(&mut self) {
        self.keys.clear();
        self.document.clear();
    }

    /// Returns a reference to the Bson corresponding to the key.
    pub fn get(&self, key: &str) -> Option<&Bson> {
        self.document.get(key)
    }

    /// Gets a mutable reference to the value in the entry.
    pub fn get_mut(&mut self, key: &str) -> Option<&mut Bson> {
        self.document.get_mut(key)
    }

    /// Returns true if the map contains a value for the specified key.
    pub fn contains_key(&self, key: &str) -> bool {
        self.document.contains_key(key)
    }

    /// Returns the position of the key in the ordered vector, if it exists.
    pub fn position(&self, key: &str) -> Option<usize> {
        self.keys.iter().position(|x| x == key)
    }

    /// Gets a collection of all keys in the document.
    pub fn keys<'a>(&'a self) -> Keys<'a> {
        fn first<A, B>((a, _): (A, B)) -> A { a }
        let first: fn((&'a String, &'a Bson)) -> &'a String = first;

        Keys { inner: self.iter().map(first) }
    }

    /// Gets a collection of all values in the document.
    pub fn values<'a>(&'a self) -> Values<'a> {
        fn second<A, B>((_, b): (A, B)) -> B { b }
        let second: fn((&'a String, &'a Bson)) -> &'a Bson = second;

        Values { inner: self.iter().map(second) }
    }

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

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

    /// Sets the value of the entry with the OccupiedEntry's key,
    /// and returns the entry's old value.
    pub fn insert(&mut self, key: String, val: Bson) -> Option<Bson> {
        let key_slice = &key[..];

        if self.contains_key(key_slice) {
            let position = self.position(key_slice).unwrap();
            self.keys.remove(position);
        }

        self.keys.push(key.to_owned());
        self.document.insert(key.to_owned(), val.to_owned())
    }

    /// Takes the value of the entry out of the document, and returns it.
    pub fn remove(&mut self, key: &str) -> Option<Bson> {
        let position = self.position(key);
        if position.is_some() {
            self.keys.remove(position.unwrap());
        }
        self.document.remove(key)
    }
}

#[cfg(test)]
mod test {
    use super::OrderedDocument;
    use bson::Bson;

    #[test]
    fn ordered_insert() {
        let mut doc = OrderedDocument::new();
        doc.insert("first".to_owned(), Bson::I32(1));
        doc.insert("second".to_owned(), Bson::String("foo".to_owned()));
        doc.insert("alphanumeric".to_owned(), Bson::String("bar".to_owned()));

        let expected_keys = vec!(
            "first".to_owned(),
            "second".to_owned(),
            "alphanumeric".to_owned(),
        );

        let keys: Vec<_> = doc.iter().map(|(key, _)| key.to_owned()).collect();
        assert_eq!(expected_keys, keys);
    }

    #[test]
    fn remove() {
        let mut doc = OrderedDocument::new();
        doc.insert("first".to_owned(), Bson::I32(1));
        doc.insert("second".to_owned(), Bson::String("foo".to_owned()));
        doc.insert("alphanumeric".to_owned(), Bson::String("bar".to_owned()));

        assert!(doc.remove("second").is_some());
        assert!(doc.remove("none").is_none());

        let expected_keys = vec!(
            "first",
            "alphanumeric",
        );

        let keys: Vec<_> = doc.iter().map(|(key, _)| key.to_owned()).collect();
        assert_eq!(expected_keys, keys);
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Terminfo database interface.

use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io;
use std::io::BufReader;
use std::path::Path;

use Attr;
use color;
use Terminal;
use Result;
use self::searcher::get_dbpath_for_term;
use self::parser::compiled::{parse, msys_terminfo};
use self::parm::{expand, Variables, Param};
use self::Error::*;

/// A parsed terminfo database entry.
#[derive(Debug, Clone)]
pub struct TermInfo {
    /// Names for the terminal
    pub names: Vec<String>,
    /// Map of capability name to boolean value
    pub bools: HashMap<&'static str, bool>,
    /// Map of capability name to numeric value
    pub numbers: HashMap<&'static str, u16>,
    /// Map of capability name to raw (unexpanded) string
    pub strings: HashMap<&'static str, Vec<u8>>,
}

impl TermInfo {
    /// Create a `TermInfo` based on current environment.
    pub fn from_env() -> Result<TermInfo> {
        let term = match env::var("TERM") {
            Ok(name) => TermInfo::from_name(&name),
            Err(..) => return Err(::Error::TermUnset),
        };

        if term.is_err() && env::var("MSYSCON").ok().map_or(false, |s| "mintty.exe" == s) {
            // msys terminal
            Ok(msys_terminfo())
        } else {
            term
        }
    }

    /// Create a `TermInfo` for the named terminal.
    pub fn from_name(name: &str) -> Result<TermInfo> {
        get_dbpath_for_term(name)
            .ok_or_else(|| ::Error::TerminfoEntryNotFound)
            .and_then(|p| TermInfo::from_path(&p))
    }

    /// Parse the given `TermInfo`.
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<TermInfo> {
        Self::_from_path(path.as_ref())
    }
    // Keep the metadata small
    // (That is, this uses a &Path so that this function need not be instantiated
    // for every type
    // which implements AsRef<Path>. One day, if/when rustc is a bit smarter, it
    // might do this for
    // us. Alas. )
    fn _from_path(path: &Path) -> Result<TermInfo> {
        let file = try!(File::open(path).map_err(::Error::Io));
        let mut reader = BufReader::new(file);
        parse(&mut reader, false)
    }

    /// Retrieve a capability `cmd` and expand it with `params`, writing result to `out`.
    pub fn apply_cap(&self, cmd: &str, params: &[Param], out: &mut io::Write) -> Result<()> {
        match self.strings.get(cmd) {
            Some(cmd) => {
                match expand(cmd, params, &mut Variables::new()) {
                    Ok(s) => {
                        try!(out.write_all(&s));
                        Ok(())
                    }
                    Err(e) => Err(e.into()),
                }
            }
            None => Err(::Error::NotSupported),
        }
    }

    /// Write the reset string to `out`.
    pub fn reset(&self, out: &mut io::Write) -> Result<()> {
        // are there any terminals that have color/attrs and not sgr0?
        // Try falling back to sgr, then op
        let cmd = match [("sgr0", &[] as &[Param]), ("sgr", &[Param::Number(0)]), ("op", &[])]
                            .iter()
                            .filter_map(|&(cap, params)| {
                                self.strings.get(cap).map(|c| (c, params))
                            })
                            .next() {
            Some((op, params)) => {
                match expand(op, params, &mut Variables::new()) {
                    Ok(cmd) => cmd,
                    Err(e) => return Err(e.into()),
                }
            }
            None => return Err(::Error::NotSupported),
        };
        try!(out.write_all(&cmd));
        Ok(())
    }

}

#[derive(Debug, Eq, PartialEq)]
/// An error from parsing a terminfo entry
pub enum Error {
    /// The "magic" number at the start of the file was wrong.
    ///
    /// It should be `0x11A`
    BadMagic(u16),
    /// The names in the file were not valid UTF-8.
    ///
    /// In theory these should only be ASCII, but to work with the Rust `str` type, we treat them
    /// as UTF-8. This is valid, except when a terminfo file decides to be invalid. This hasn't
    /// been encountered in the wild.
    NotUtf8(::std::str::Utf8Error),
    /// The names section of the file was empty
    ShortNames,
    /// More boolean parameters are present in the file than this crate knows how to interpret.
    TooManyBools,
    /// More number parameters are present in the file than this crate knows how to interpret.
    TooManyNumbers,
    /// More string parameters are present in the file than this crate knows how to interpret.
    TooManyStrings,
    /// The length of some field was not >= -1.
    InvalidLength,
    /// The names table was missing a trailing null terminator.
    NamesMissingNull,
    /// The strings table was missing a trailing null terminator.
    StringsMissingNull,
}

impl ::std::fmt::Display for Error {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        use std::error::Error;
        match *self {
            NotUtf8(e) => write!(f, "{}", e),
            BadMagic(v) => write!(f, "bad magic number {:x} in terminfo header", v),
            _ => f.write_str(self.description()),
        }
    }
}

impl ::std::convert::From<::std::string::FromUtf8Error> for Error {
    fn from(v: ::std::string::FromUtf8Error) -> Self {
        NotUtf8(v.utf8_error())
    }
}

impl ::std::error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            BadMagic(..) => "incorrect magic number at start of file",
            ShortNames => "no names exposed, need at least one",
            TooManyBools => "more boolean properties than libterm knows about",
            TooManyNumbers => "more number properties than libterm knows about",
            TooManyStrings => "more string properties than libterm knows about",
            InvalidLength => "invalid length field value, must be >= -1",
            NotUtf8(ref e) => e.description(),
            NamesMissingNull => "names table missing NUL terminator",
            StringsMissingNull => "string table missing NUL terminator",
        }
    }

    fn cause(&self) -> Option<&::std::error::Error> {
        match *self {
            NotUtf8(ref e) => Some(e),
            _ => None,
        }
    }
}

pub mod searcher;

/// `TermInfo` format parsing.
pub mod parser {
    //! ncurses-compatible compiled terminfo format parsing (term(5))
    pub mod compiled;
    mod names;
}
pub mod parm;

fn cap_for_attr(attr: Attr) -> &'static str {
    match attr {
        Attr::Bold => "bold",
        Attr::Dim => "dim",
        Attr::Italic(true) => "sitm",
        Attr::Italic(false) => "ritm",
        Attr::Underline(true) => "smul",
        Attr::Underline(false) => "rmul",
        Attr::Blink => "blink",
        Attr::Standout(true) => "smso",
        Attr::Standout(false) => "rmso",
        Attr::Reverse => "rev",
        Attr::Secure => "invis",
        Attr::ForegroundColor(_) => "setaf",
        Attr::BackgroundColor(_) => "setab",
    }
}

/// A Terminal that knows how many colors it supports, with a reference to its
/// parsed Terminfo database record.
#[derive(Clone, Debug)]
pub struct TerminfoTerminal<T> {
    num_colors: u16,
    out: T,
    ti: TermInfo,
}

impl<T: Write + Send> Terminal for TerminfoTerminal<T> {
    type Output = T;
    fn fg(&mut self, color: color::Color) -> Result<()> {
        let color = self.dim_if_necessary(color);
        if self.num_colors > color {
            return self.ti.apply_cap("setaf", &[Param::Number(color as i32)], &mut self.out);
        }
        Err(::Error::ColorOutOfRange)
    }

    fn bg(&mut self, color: color::Color) -> Result<()> {
        let color = self.dim_if_necessary(color);
        if self.num_colors > color {
            return self.ti.apply_cap("setab", &[Param::Number(color as i32)], &mut self.out);
        }
        Err(::Error::ColorOutOfRange)
    }

    fn attr(&mut self, attr: Attr) -> Result<()> {
        match attr {
            Attr::ForegroundColor(c) => self.fg(c),
            Attr::BackgroundColor(c) => self.bg(c),
            _ => self.ti.apply_cap(cap_for_attr(attr), &[], &mut self.out),
        }
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        match attr {
            Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => self.num_colors > 0,
            _ => {
                let cap = cap_for_attr(attr);
                self.ti.strings.get(cap).is_some()
            }
        }
    }

    fn reset(&mut self) -> Result<()> {
        self.ti.reset(&mut self.out)
    }

    fn supports_reset(&self) -> bool {
        ["sgr0", "sgr", "op"].iter().any(|&cap| self.ti.strings.get(cap).is_some())
    }

    fn supports_color(&self) -> bool {
        self.num_colors > 0 && self.supports_reset()
    }

    fn cursor_up(&mut self) -> Result<()> {
        self.ti.apply_cap("cuu1", &[], &mut self.out)
    }

    fn delete_line(&mut self) -> Result<()> {
        self.ti.apply_cap("clr_eol", &[], &mut self.out)
    }

    fn carriage_return(&mut self) -> Result<()> {
        self.ti.apply_cap("cr", &[], &mut self.out)
    }

    fn get_ref(&self) -> &T {
        &self.out
    }

    fn get_mut(&mut self) -> &mut T {
        &mut self.out
    }

    fn into_inner(self) -> T
        where Self: Sized
    {
        self.out
    }
}

impl<T: Write + Send> TerminfoTerminal<T> {
    /// Create a new TerminfoTerminal with the given TermInfo and Write.
    pub fn new_with_terminfo(out: T, terminfo: TermInfo) -> TerminfoTerminal<T> {
        let nc = if terminfo.strings.contains_key("setaf") &&
                    terminfo.strings.contains_key("setab") {
            terminfo.numbers.get("colors").map_or(0, |&n| n)
        } else {
            0
        };

        TerminfoTerminal {
            out: out,
            ti: terminfo,
            num_colors: nc,
        }
    }

    /// Create a new TerminfoTerminal for the current environment with the given Write.
    ///
    /// Returns `None` when the terminfo cannot be found or parsed.
    pub fn new(out: T) -> Option<TerminfoTerminal<T>> {
        TermInfo::from_env().map(move |ti| TerminfoTerminal::new_with_terminfo(out, ti)).ok()
    }

    fn dim_if_necessary(&self, color: color::Color) -> color::Color {
        if color >= self.num_colors && color >= 8 && color < 16 {
            color - 8
        } else {
            color
        }
    }
}

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

    fn flush(&mut self) -> io::Result<()> {
        self.out.flush()
    }
}
<commit_msg>Use "el" instead of "crl_eol"<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Terminfo database interface.

use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io;
use std::io::BufReader;
use std::path::Path;

use Attr;
use color;
use Terminal;
use Result;
use self::searcher::get_dbpath_for_term;
use self::parser::compiled::{parse, msys_terminfo};
use self::parm::{expand, Variables, Param};
use self::Error::*;

/// A parsed terminfo database entry.
#[derive(Debug, Clone)]
pub struct TermInfo {
    /// Names for the terminal
    pub names: Vec<String>,
    /// Map of capability name to boolean value
    pub bools: HashMap<&'static str, bool>,
    /// Map of capability name to numeric value
    pub numbers: HashMap<&'static str, u16>,
    /// Map of capability name to raw (unexpanded) string
    pub strings: HashMap<&'static str, Vec<u8>>,
}

impl TermInfo {
    /// Create a `TermInfo` based on current environment.
    pub fn from_env() -> Result<TermInfo> {
        let term = match env::var("TERM") {
            Ok(name) => TermInfo::from_name(&name),
            Err(..) => return Err(::Error::TermUnset),
        };

        if term.is_err() && env::var("MSYSCON").ok().map_or(false, |s| "mintty.exe" == s) {
            // msys terminal
            Ok(msys_terminfo())
        } else {
            term
        }
    }

    /// Create a `TermInfo` for the named terminal.
    pub fn from_name(name: &str) -> Result<TermInfo> {
        get_dbpath_for_term(name)
            .ok_or_else(|| ::Error::TerminfoEntryNotFound)
            .and_then(|p| TermInfo::from_path(&p))
    }

    /// Parse the given `TermInfo`.
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<TermInfo> {
        Self::_from_path(path.as_ref())
    }
    // Keep the metadata small
    // (That is, this uses a &Path so that this function need not be instantiated
    // for every type
    // which implements AsRef<Path>. One day, if/when rustc is a bit smarter, it
    // might do this for
    // us. Alas. )
    fn _from_path(path: &Path) -> Result<TermInfo> {
        let file = try!(File::open(path).map_err(::Error::Io));
        let mut reader = BufReader::new(file);
        parse(&mut reader, false)
    }

    /// Retrieve a capability `cmd` and expand it with `params`, writing result to `out`.
    pub fn apply_cap(&self, cmd: &str, params: &[Param], out: &mut io::Write) -> Result<()> {
        match self.strings.get(cmd) {
            Some(cmd) => {
                match expand(cmd, params, &mut Variables::new()) {
                    Ok(s) => {
                        try!(out.write_all(&s));
                        Ok(())
                    }
                    Err(e) => Err(e.into()),
                }
            }
            None => Err(::Error::NotSupported),
        }
    }

    /// Write the reset string to `out`.
    pub fn reset(&self, out: &mut io::Write) -> Result<()> {
        // are there any terminals that have color/attrs and not sgr0?
        // Try falling back to sgr, then op
        let cmd = match [("sgr0", &[] as &[Param]), ("sgr", &[Param::Number(0)]), ("op", &[])]
                            .iter()
                            .filter_map(|&(cap, params)| {
                                self.strings.get(cap).map(|c| (c, params))
                            })
                            .next() {
            Some((op, params)) => {
                match expand(op, params, &mut Variables::new()) {
                    Ok(cmd) => cmd,
                    Err(e) => return Err(e.into()),
                }
            }
            None => return Err(::Error::NotSupported),
        };
        try!(out.write_all(&cmd));
        Ok(())
    }

}

#[derive(Debug, Eq, PartialEq)]
/// An error from parsing a terminfo entry
pub enum Error {
    /// The "magic" number at the start of the file was wrong.
    ///
    /// It should be `0x11A`
    BadMagic(u16),
    /// The names in the file were not valid UTF-8.
    ///
    /// In theory these should only be ASCII, but to work with the Rust `str` type, we treat them
    /// as UTF-8. This is valid, except when a terminfo file decides to be invalid. This hasn't
    /// been encountered in the wild.
    NotUtf8(::std::str::Utf8Error),
    /// The names section of the file was empty
    ShortNames,
    /// More boolean parameters are present in the file than this crate knows how to interpret.
    TooManyBools,
    /// More number parameters are present in the file than this crate knows how to interpret.
    TooManyNumbers,
    /// More string parameters are present in the file than this crate knows how to interpret.
    TooManyStrings,
    /// The length of some field was not >= -1.
    InvalidLength,
    /// The names table was missing a trailing null terminator.
    NamesMissingNull,
    /// The strings table was missing a trailing null terminator.
    StringsMissingNull,
}

impl ::std::fmt::Display for Error {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        use std::error::Error;
        match *self {
            NotUtf8(e) => write!(f, "{}", e),
            BadMagic(v) => write!(f, "bad magic number {:x} in terminfo header", v),
            _ => f.write_str(self.description()),
        }
    }
}

impl ::std::convert::From<::std::string::FromUtf8Error> for Error {
    fn from(v: ::std::string::FromUtf8Error) -> Self {
        NotUtf8(v.utf8_error())
    }
}

impl ::std::error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            BadMagic(..) => "incorrect magic number at start of file",
            ShortNames => "no names exposed, need at least one",
            TooManyBools => "more boolean properties than libterm knows about",
            TooManyNumbers => "more number properties than libterm knows about",
            TooManyStrings => "more string properties than libterm knows about",
            InvalidLength => "invalid length field value, must be >= -1",
            NotUtf8(ref e) => e.description(),
            NamesMissingNull => "names table missing NUL terminator",
            StringsMissingNull => "string table missing NUL terminator",
        }
    }

    fn cause(&self) -> Option<&::std::error::Error> {
        match *self {
            NotUtf8(ref e) => Some(e),
            _ => None,
        }
    }
}

pub mod searcher;

/// `TermInfo` format parsing.
pub mod parser {
    //! ncurses-compatible compiled terminfo format parsing (term(5))
    pub mod compiled;
    mod names;
}
pub mod parm;

fn cap_for_attr(attr: Attr) -> &'static str {
    match attr {
        Attr::Bold => "bold",
        Attr::Dim => "dim",
        Attr::Italic(true) => "sitm",
        Attr::Italic(false) => "ritm",
        Attr::Underline(true) => "smul",
        Attr::Underline(false) => "rmul",
        Attr::Blink => "blink",
        Attr::Standout(true) => "smso",
        Attr::Standout(false) => "rmso",
        Attr::Reverse => "rev",
        Attr::Secure => "invis",
        Attr::ForegroundColor(_) => "setaf",
        Attr::BackgroundColor(_) => "setab",
    }
}

/// A Terminal that knows how many colors it supports, with a reference to its
/// parsed Terminfo database record.
#[derive(Clone, Debug)]
pub struct TerminfoTerminal<T> {
    num_colors: u16,
    out: T,
    ti: TermInfo,
}

impl<T: Write + Send> Terminal for TerminfoTerminal<T> {
    type Output = T;
    fn fg(&mut self, color: color::Color) -> Result<()> {
        let color = self.dim_if_necessary(color);
        if self.num_colors > color {
            return self.ti.apply_cap("setaf", &[Param::Number(color as i32)], &mut self.out);
        }
        Err(::Error::ColorOutOfRange)
    }

    fn bg(&mut self, color: color::Color) -> Result<()> {
        let color = self.dim_if_necessary(color);
        if self.num_colors > color {
            return self.ti.apply_cap("setab", &[Param::Number(color as i32)], &mut self.out);
        }
        Err(::Error::ColorOutOfRange)
    }

    fn attr(&mut self, attr: Attr) -> Result<()> {
        match attr {
            Attr::ForegroundColor(c) => self.fg(c),
            Attr::BackgroundColor(c) => self.bg(c),
            _ => self.ti.apply_cap(cap_for_attr(attr), &[], &mut self.out),
        }
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        match attr {
            Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => self.num_colors > 0,
            _ => {
                let cap = cap_for_attr(attr);
                self.ti.strings.get(cap).is_some()
            }
        }
    }

    fn reset(&mut self) -> Result<()> {
        self.ti.reset(&mut self.out)
    }

    fn supports_reset(&self) -> bool {
        ["sgr0", "sgr", "op"].iter().any(|&cap| self.ti.strings.get(cap).is_some())
    }

    fn supports_color(&self) -> bool {
        self.num_colors > 0 && self.supports_reset()
    }

    fn cursor_up(&mut self) -> Result<()> {
        self.ti.apply_cap("cuu1", &[], &mut self.out)
    }

    fn delete_line(&mut self) -> Result<()> {
        self.ti.apply_cap("el", &[], &mut self.out)
    }

    fn carriage_return(&mut self) -> Result<()> {
        self.ti.apply_cap("cr", &[], &mut self.out)
    }

    fn get_ref(&self) -> &T {
        &self.out
    }

    fn get_mut(&mut self) -> &mut T {
        &mut self.out
    }

    fn into_inner(self) -> T
        where Self: Sized
    {
        self.out
    }
}

impl<T: Write + Send> TerminfoTerminal<T> {
    /// Create a new TerminfoTerminal with the given TermInfo and Write.
    pub fn new_with_terminfo(out: T, terminfo: TermInfo) -> TerminfoTerminal<T> {
        let nc = if terminfo.strings.contains_key("setaf") &&
                    terminfo.strings.contains_key("setab") {
            terminfo.numbers.get("colors").map_or(0, |&n| n)
        } else {
            0
        };

        TerminfoTerminal {
            out: out,
            ti: terminfo,
            num_colors: nc,
        }
    }

    /// Create a new TerminfoTerminal for the current environment with the given Write.
    ///
    /// Returns `None` when the terminfo cannot be found or parsed.
    pub fn new(out: T) -> Option<TerminfoTerminal<T>> {
        TermInfo::from_env().map(move |ti| TerminfoTerminal::new_with_terminfo(out, ti)).ok()
    }

    fn dim_if_necessary(&self, color: color::Color) -> color::Color {
        if color >= self.num_colors && color >= 8 && color < 16 {
            color - 8
        } else {
            color
        }
    }
}

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

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


<commit_before>use std::collections::hash_map::HashMap;
use std::collections::hash_set::HashSet;
use std::fmt::{Display, Formatter, Error};
use std::string::String;
use std::vec::Vec;

use alloc_box::AllocBox;
use super::binding::UniqueBinding;
use super::js_var::{JsVar, JsKey, JsType, JsPtrEnum};

macro_rules! ptr_type_mismatch {
    ($tag:expr, $ptr:expr) => {
        panic!("Pointer type mismatch: expected {:?} but received {:?}", $tag, $ptr);
    }
}

#[derive(Clone, Debug)]
pub struct JsObjStruct {
    pub proto: JsProto,
    pub name: String,
    pub dict: HashMap<JsKey, JsVar>,
}

impl JsObjStruct {
    #[allow(unused_variables)]
    pub fn new(proto: JsProto, name: &str, kv_tuples: Vec<(JsKey, JsVar, Option<JsPtrEnum>)>,
               allocator: &mut AllocBox) -> JsObjStruct {
        JsObjStruct {
            proto: None,
            name: String::from(name),
            dict: kv_tuples.into_iter().map(|(k, v, ptr)| {
                match v.t {
                    JsType::JsPtr(ref tag) => match ptr {
                        Some(ptr) => {
                            if tag.eq_ptr_type(&ptr) {
                                allocator.alloc(v.unique.clone(), ptr).expect("Unable to allocate!"); // TODO better error handling
                            } else {
                                ptr_type_mismatch!(tag, ptr);
                            }
                        },
                        None => ptr_type_mismatch!(tag, None::<JsPtrEnum>),
                    },
                    _ => match ptr {
                        Some(ptr) => ptr_type_mismatch!(None::<JsPtrEnum>, ptr),
                        None => {},
                    },
                }
                (k, v)
            }).collect()
        }
    }

    pub fn add_key(&mut self, k: JsKey, v: JsVar, ptr: Option<JsPtrEnum>, allocator: &mut AllocBox) {
        println!("{:#?}", self);
        // If the key already exists, potentially condemn its pointer, which is being overwritten.
        if let Some(var) = self.dict.get(&k) {
            println!("VAR");
            match var.t {
                JsType::JsPtr(_) => { println!("CONDEMN"); allocator.condemn(var.unique.clone()).expect("Unable to whiten!") },
                _ => {}
            }
        }
        // Then, allocate the new pointer if necessary...
        if let Some(ptr) = ptr {
            println!("POINTER");
            allocator.alloc(v.unique.clone(), ptr).expect("Unable to allocate!"); // TODO better error handling
        }
        // ...and insert the key & value into the dictionary blindly.
        self.dict.insert(k, v);
        println!("DONE");
        println!("{:#?}", self);
    }

    pub fn get_children(&self) -> HashSet<UniqueBinding> {
        let mut bindings = HashSet::new();
        for v in self.dict.values() {
            match v.t {
                JsType::JsPtr(_) => { bindings.insert(v.unique.clone()); },
                _ => (),
            }
        }
        bindings
    }
}

impl Display for JsObjStruct {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        try!(write!(fmt, "{{ "));

        for (i, (ref key, ref val)) in self.dict.iter().enumerate() {
            if i != 0 {
                try!(write!(fmt, ", "));
            }

            try!(write!(fmt, "{}: {}", key, val));
        }

        write!(fmt, " }}")
    }
}

pub type JsProto = Option<Box<JsObjStruct>>;

// TODO nice JS object creation macro
//macro_rules! js_obj {
//    ( $kt:ty : $ke:expr => $vt:ty : $ve:expr ),* {
//        {
//
//        }
//    };
//}
<commit_msg>Add remove_key method to objs<commit_after>use std::collections::hash_map::HashMap;
use std::collections::hash_set::HashSet;
use std::fmt::{Display, Formatter, Error};
use std::string::String;
use std::vec::Vec;

use alloc_box::AllocBox;
use super::binding::UniqueBinding;
use super::js_var::{JsVar, JsKey, JsType, JsPtrEnum};

macro_rules! ptr_type_mismatch {
    ($tag:expr, $ptr:expr) => {
        panic!("Pointer type mismatch: expected {:?} but received {:?}", $tag, $ptr);
    }
}

#[derive(Clone, Debug)]
pub struct JsObjStruct {
    pub proto: JsProto,
    pub name: String,
    pub dict: HashMap<JsKey, JsVar>,
}

impl JsObjStruct {
    #[allow(unused_variables)]
    pub fn new(proto: JsProto, name: &str, kv_tuples: Vec<(JsKey, JsVar, Option<JsPtrEnum>)>,
               allocator: &mut AllocBox) -> JsObjStruct {
        JsObjStruct {
            proto: None,
            name: String::from(name),
            dict: kv_tuples.into_iter().map(|(k, v, ptr)| {
                match v.t {
                    JsType::JsPtr(ref tag) => match ptr {
                        Some(ptr) => {
                            if tag.eq_ptr_type(&ptr) {
                                allocator.alloc(v.unique.clone(), ptr).expect("Unable to allocate!"); // TODO better error handling
                            } else {
                                ptr_type_mismatch!(tag, ptr);
                            }
                        },
                        None => ptr_type_mismatch!(tag, None::<JsPtrEnum>),
                    },
                    _ => match ptr {
                        Some(ptr) => ptr_type_mismatch!(None::<JsPtrEnum>, ptr),
                        None => {},
                    },
                }
                (k, v)
            }).collect()
        }
    }

    pub fn add_key(&mut self, k: JsKey, v: JsVar, ptr: Option<JsPtrEnum>, allocator: &mut AllocBox) {
        println!("{:#?}", self);
        // If the key already exists, potentially condemn its pointer, which is being overwritten.
        if let Some(var) = self.dict.get(&k) {
            println!("VAR");
            match var.t {
                JsType::JsPtr(_) => { println!("CONDEMN"); allocator.condemn(var.unique.clone()).expect("Unable to whiten!") },
                _ => {}
            }
        }
        // Then, allocate the new pointer if necessary...
        if let Some(ptr) = ptr {
            println!("POINTER");
            allocator.alloc(v.unique.clone(), ptr).expect("Unable to allocate!"); // TODO better error handling
        }
        // ...and insert the key & value into the dictionary blindly.
        self.dict.insert(k, v);
        println!("DONE");
        println!("{:#?}", self);
    }

    pub fn remove_key(&mut self, k: &JsKey, allocate: &mut AllocBox) -> Option<(JsVar, Option<JsPtrEnum>)>{
        if let Some(var) = self.dict.remove(k) {
            let ptr = allocator.borrow_mut().remove_binding(var.unique.clone());
            Some((var, ptr))
        } else {
            None
        }
    }

    pub fn get_children(&self) -> HashSet<UniqueBinding> {
        let mut bindings = HashSet::new();
        for v in self.dict.values() {
            match v.t {
                JsType::JsPtr(_) => { bindings.insert(v.unique.clone()); },
                _ => (),
            }
        }
        bindings
    }
}

impl Display for JsObjStruct {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        try!(write!(fmt, "{{ "));

        for (i, (ref key, ref val)) in self.dict.iter().enumerate() {
            if i != 0 {
                try!(write!(fmt, ", "));
            }

            try!(write!(fmt, "{}: {}", key, val));
        }

        write!(fmt, " }}")
    }
}

pub type JsProto = Option<Box<JsObjStruct>>;

// TODO nice JS object creation macro
//macro_rules! js_obj {
//    ( $kt:ty : $ke:expr => $vt:ty : $ve:expr ),* {
//        {
//
//        }
//    };
//}
<|endoftext|>


<commit_before>//! Draw polygon

use internal;
use triangulation;
use Graphics;
use Color;
use DrawState;
use math::Matrix2d;

/// A polygon
#[derive(Copy, Clone)]
pub struct Polygon {
    /// The color of the polygon
    pub color: internal::Color,
}

impl Polygon {
    /// Creates new polygon
    pub fn new(color: internal::Color) -> Polygon {
        Polygon {
            color: color,
        }
    }

    /// Draws polygon
    pub fn draw<G>(
        &self,
        polygon: internal::Polygon,
        draw_state: &DrawState,
        transform: Matrix2d,
        g: &mut G
    )
        where G: Graphics
    {
        g.tri_list(
            draw_state,
            &self.color,
            |f|
        triangulation::with_polygon_tri_list(
            transform,
            polygon,
            |vertices| f(vertices)
        ));
    }

    /// Draws tweened polygon with linear interpolation
    pub fn draw_tween_lerp<G>(
        &self,
        polygons: internal::Polygons,
        tween_factor: internal::Scalar,
        draw_state: &DrawState,
        transform: Matrix2d,
        g: &mut G
    )
        where G: Graphics
    {
        if self.color[3] == 0.0 { return; }
        g.tri_list(
            draw_state,
            &self.color,
            |f|
        triangulation::with_lerp_polygons_tri_list(
            transform,
            polygons,
            tween_factor,
            |vertices| f(vertices)
        ));
    }
}

/*
quack! {
    p: Polygon[]
    get:
        fn () -> Color [] { Color(p.color) }
    set:
        fn (val: Color) [] { p.color = val.0 }
    action:
}
*/

#[cfg(test)]
mod test {
    use super::Polygon;
    use Color;

    #[test]
    fn test_polygon() {
        let _polygon = Polygon::new([1.0; 4])
            .set(Color([0.0; 4]));
    }
}
<commit_msg>Added builder methods for `Polygon`<commit_after>//! Draw polygon

use internal;
use triangulation;
use Graphics;
use Color;
use DrawState;
use math::Matrix2d;

/// A polygon
#[derive(Copy, Clone)]
pub struct Polygon {
    /// The color of the polygon
    pub color: internal::Color,
}

impl Polygon {
    /// Creates new polygon
    pub fn new(color: internal::Color) -> Polygon {
        Polygon {
            color: color,
        }
    }

    /// Sets color.
    pub fn color(mut self, color: internal::Color) -> Self {
        self.color = color;
        self
    }

    /// Draws polygon
    pub fn draw<G>(
        &self,
        polygon: internal::Polygon,
        draw_state: &DrawState,
        transform: Matrix2d,
        g: &mut G
    )
        where G: Graphics
    {
        g.tri_list(
            draw_state,
            &self.color,
            |f|
        triangulation::with_polygon_tri_list(
            transform,
            polygon,
            |vertices| f(vertices)
        ));
    }

    /// Draws tweened polygon with linear interpolation
    pub fn draw_tween_lerp<G>(
        &self,
        polygons: internal::Polygons,
        tween_factor: internal::Scalar,
        draw_state: &DrawState,
        transform: Matrix2d,
        g: &mut G
    )
        where G: Graphics
    {
        if self.color[3] == 0.0 { return; }
        g.tri_list(
            draw_state,
            &self.color,
            |f|
        triangulation::with_lerp_polygons_tri_list(
            transform,
            polygons,
            tween_factor,
            |vertices| f(vertices)
        ));
    }
}

#[cfg(test)]
mod test {
    use super::Polygon;
    use Color;

    #[test]
    fn test_polygon() {
        let _polygon = Polygon::new([1.0; 4])
            .color([0.0; 4]);
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use super::super::interconnect::Interconnect;

const SCREEN_WIDTH: usize = 256;
const SCREEN_HEIGHT: usize = 240;
const CPU_CYCLES_PER_SCANLINE: u64 = 114;
const LAST_VISIBLE_SCANLINE: u8 = 239;
const VBLANK_SCANLINES: u64 = 20;

const VBLANK_FLAG: u8 = (1 << 7);

// Shamlessly copied from SprocketNES...too much typing
static PALETTE: [u8; 192] =
    [124, 124, 124, 0, 0, 252, 0, 0, 188, 68, 40, 188, 148, 0, 132, 168, 0, 32, 168, 16, 0, 136,
     20, 0, 80, 48, 0, 0, 120, 0, 0, 104, 0, 0, 88, 0, 0, 64, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188,
     188, 188, 0, 120, 248, 0, 88, 248, 104, 68, 252, 216, 0, 204, 228, 0, 88, 248, 56, 0, 228,
     92, 16, 172, 124, 0, 0, 184, 0, 0, 168, 0, 0, 168, 68, 0, 136, 136, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 248, 248, 248, 60, 188, 252, 104, 136, 252, 152, 120, 248, 248, 120, 248, 248, 88, 152,
     248, 120, 88, 252, 160, 68, 248, 184, 0, 184, 248, 24, 88, 216, 84, 88, 248, 152, 0, 232,
     216, 120, 120, 120, 0, 0, 0, 0, 0, 0, 252, 252, 252, 164, 228, 252, 184, 184, 248, 216, 184,
     248, 248, 184, 248, 248, 164, 192, 240, 208, 176, 252, 224, 168, 248, 216, 120, 216, 248,
     120, 184, 248, 184, 184, 248, 216, 0, 252, 252, 248, 216, 248, 0, 0, 0, 0, 0, 0];

#[derive(Debug, PartialEq)]
enum Scanline {
    PreRender,
    Visible(u8),
    PostRender,
    VBlank,
}

#[derive(Debug)]
struct RGB {
    red: u8,
    green: u8,
    blue: u8,
}

#[derive(Default)]
struct BgPixelBuffer {
    pattern_low: u8,
    pattern_high: u8,
    attribute: u8,
}

pub struct Ppu {
    ppuctrl: u8,
    ppumask: u8,
    ppustatus: u8,
    oamaddr: u8,
    oamdata: u8,
    ppuscroll: u8,
    ppuaddr: u8,
    ppudata: u8,
    oamdma: u8,

    // oam: Box<[u8; 0xff]>,
    cycles: u64,
    scanline: Scanline,
    pub frame: Box<[u8; SCREEN_WIDTH * SCREEN_HEIGHT * 3]>,

    x_scroll: u16,
    y_scroll: u16,
    nametable: u16,
}

impl Ppu {
    pub fn new() -> Ppu {
        Ppu {
            ppuctrl: 0,
            ppumask: 0,
            ppustatus: 0,
            oamaddr: 0,
            oamdata: 0,
            ppuscroll: 0,
            ppuaddr: 0,
            ppudata: 0,
            oamdma: 0,

            // oam: Box::new([0u8; 0xff]),
            cycles: 0,
            scanline: Scanline::PreRender,
            frame: Box::new([0u8; SCREEN_WIDTH * SCREEN_HEIGHT * 3]),

            x_scroll: 0,
            y_scroll: 0,
            nametable: 0x2000,
        }
    }

    pub fn read_reg(&self, addr: u16) -> u8 {
        if addr == 0x4014 {
            self.oamdma
        } else {
            match addr & 1 << 0 {
                0x0 => self.ppuctrl,
                0x1 => self.ppumask,
                0x2 => self.ppustatus,
                0x3 => self.oamaddr,
                0x4 => self.oamdata,
                0x5 => self.ppuscroll,
                0x6 => self.ppuaddr,
                0x7 => self.ppudata,
                _ => panic!("Attemped access of nonexistent PPU register: {:#x}", addr),
            }
        }
    }

    pub fn write_to_reg(&mut self, addr: u16, val: u8) {
        if addr == 0x4014 {
            self.oamdma = val;
        } else {
            match addr & 1 << 0 {
                0x0 => self.ppuctrl = val,
                0x1 => self.ppumask = val,
                0x2 => self.ppustatus = val,
                0x3 => self.oamaddr = val,
                0x4 => self.oamdata = val,
                0x5 => self.ppuscroll = val,
                0x6 => self.ppuaddr = val,
                0x7 => self.ppudata = val,
                _ => panic!("Attemped write to nonexistent PPU register: {:#x}", addr),
            }
        }
    }

    pub fn power_up(&mut self) {
        self.ppustatus = 0b10100000;
    }

    // Returns whether the NMI is currently allowed
    pub fn step(&mut self, interconnect: &mut Interconnect, current_cpu_cycle: &u64) -> bool {
        let mut vblank = false;
        while self.cycles < *current_cpu_cycle {
            match self.scanline {
                Scanline::PreRender => {
                    self.prerender();
                    vblank = false;
                }
                Scanline::Visible(line) => {
                    self.render_scanline(interconnect, line);
                    vblank = false;
                }
                Scanline::PostRender => {
                    self.postrender();
                    vblank = false;
                }
                Scanline::VBlank => {
                    println!("###################### V Blank ########################");
                    self.vblank(&mut vblank);
                    vblank = true;
                }
            }
            self.cycles += CPU_CYCLES_PER_SCANLINE; // It's easier to just deal in cpu cycles.
        }
        vblank
    }

    fn prerender(&mut self) {
        self.set_vblank(false);
        self.scanline = Scanline::Visible(0);
        self.cycles += CPU_CYCLES_PER_SCANLINE;
    }

    fn render_scanline(&mut self, interconnect: &mut Interconnect, line: u8) {
        // TODO: Refactor
        println!("################# Rendering scanline ##################: {:?}",
                 self.scanline);
        let mut bg_pixel_buffer = BgPixelBuffer::default();

        for x in 0..SCREEN_WIDTH {
            // I could probably just buffer the whole scanline, but I think this is more accurate
            if x % 8 == 0 {
                bg_pixel_buffer = self.refresh_buffer(interconnect);
            }
            let background_pixel = self.make_background_pixel(interconnect, &bg_pixel_buffer, x as u8);
            let y = self.y_scroll as usize;
            self.putpixel(x, y, background_pixel);
            self.x_scroll += 1;
        }

        self.cycles += CPU_CYCLES_PER_SCANLINE;
        self.y_scroll += 1;
        self.x_scroll = 0;
        if self.scanline == Scanline::Visible(LAST_VISIBLE_SCANLINE) {
            self.scanline = Scanline::PostRender;
        } else {
            self.scanline = Scanline::Visible(line + 1);
        }

    }

    fn postrender(&mut self) {
        self.scanline = Scanline::VBlank;
        self.cycles += CPU_CYCLES_PER_SCANLINE;
    }

    fn vblank(&mut self, vblank_nmi: &mut bool) {
        self.set_vblank(true);
        self.cycles += CPU_CYCLES_PER_SCANLINE * VBLANK_SCANLINES;
        self.scanline = Scanline::PreRender;
        self.x_scroll = 0;
        self.y_scroll = 0;
        *vblank_nmi = self.throw_nmi();
    }

    fn set_vblank(&mut self, status: bool) {
        match status {
            true => {
                self.ppustatus |= VBLANK_FLAG;
            }
            false => {
                self.ppustatus &= !VBLANK_FLAG;
            }
        }
    }

    fn throw_nmi(&self) -> bool {
        if (self.ppuctrl & (1 << 7)) != 0 {
            true
        } else {
            false
        }
    }

    fn refresh_buffer(&mut self, interconnect: &Interconnect) -> BgPixelBuffer {
        BgPixelBuffer {
            pattern_low: 0,
            pattern_high: 0,
            attribute: 0,
        }
    }

    fn make_background_pixel(&mut self, interconnect: &Interconnect, buffer: &BgPixelBuffer, x: u8) -> RGB {
        RGB {
            red: 0,
            green: 0,
            blue: 0,
        }
<<<<<<< HEAD
        println!("pattern_color: {:#b}", pattern_color);

        let tile_color = (attr_color << 2) | pattern_color;
        println!("Tile Color: {:#b}", tile_color);

        let palette_index = interconnect.ppu_read_byte(0x3f00 + (tile_color as u16)) & 0x3f;
        println!("palette index: {:#X}", palette_index);
        println!("RGB: {:?}", self.get_color(palette_index));
        self.get_color(palette_index)
    }

    fn get_color(&self, palette_index: u8) -> RGB {
        RGB {
            red: PALETTE[palette_index as usize * 3 + 2],
            green: PALETTE[palette_index as usize * 3 + 1],
            blue: PALETTE[palette_index as usize * 3 + 0],
        }
    }

    fn putpixel(&mut self, x: usize, y: usize, color: RGB) {
        self.frame[(y * SCREEN_WIDTH + x) * 3 + 0] = color.red;
        self.frame[(y * SCREEN_WIDTH + x) * 3 + 1] = color.green;
        self.frame[(y * SCREEN_WIDTH + x) * 3 + 2] = color.blue;
=======
>>>>>>> f96ef52be3e96c17ebe1d11fa8dbd00c221f2704
    }

    fn fetch_nametable_byte(&mut self, interconnect: &Interconnect) -> u8 {
    }

    fn fetch_attribute(&mut self, interconnect: &Interconnect) -> u8 {
    }
    fn fetch_pattern(&mut self, interconnect: &Interconnect, nametable_byte: u8) -> (u8, u8) {
    }
}

impl fmt::Debug for Ppu {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "PPU: ppuctrl: {:#x}, ppumask: {:#x}, ppustatus: {:#x}, oamaddr: {:#x}, oamdata: \
                {:#x}, ppuscroll: {:#x}, ppuaddr: {:#x},  ppudata: {:#x}, oamdma: {:#x}",
               self.ppuctrl,
               self.ppumask,
               self.ppustatus,
               self.oamaddr,
               self.oamdata,
               self.ppuscroll,
               self.ppuaddr,
               self.ppudata,
               self.oamdma)
    }
}
<commit_msg>Got compiling. On hiaitus until after classes<commit_after>use std::fmt;
use super::super::interconnect::Interconnect;

const SCREEN_WIDTH: usize = 256;
const SCREEN_HEIGHT: usize = 240;
const CPU_CYCLES_PER_SCANLINE: u64 = 114;
const LAST_VISIBLE_SCANLINE: u8 = 239;
const VBLANK_SCANLINES: u64 = 20;

const VBLANK_FLAG: u8 = (1 << 7);

// Shamlessly copied from SprocketNES...too much typing
static PALETTE: [u8; 192] =
    [124, 124, 124, 0, 0, 252, 0, 0, 188, 68, 40, 188, 148, 0, 132, 168, 0, 32, 168, 16, 0, 136,
     20, 0, 80, 48, 0, 0, 120, 0, 0, 104, 0, 0, 88, 0, 0, 64, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188,
     188, 188, 0, 120, 248, 0, 88, 248, 104, 68, 252, 216, 0, 204, 228, 0, 88, 248, 56, 0, 228,
     92, 16, 172, 124, 0, 0, 184, 0, 0, 168, 0, 0, 168, 68, 0, 136, 136, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 248, 248, 248, 60, 188, 252, 104, 136, 252, 152, 120, 248, 248, 120, 248, 248, 88, 152,
     248, 120, 88, 252, 160, 68, 248, 184, 0, 184, 248, 24, 88, 216, 84, 88, 248, 152, 0, 232,
     216, 120, 120, 120, 0, 0, 0, 0, 0, 0, 252, 252, 252, 164, 228, 252, 184, 184, 248, 216, 184,
     248, 248, 184, 248, 248, 164, 192, 240, 208, 176, 252, 224, 168, 248, 216, 120, 216, 248,
     120, 184, 248, 184, 184, 248, 216, 0, 252, 252, 248, 216, 248, 0, 0, 0, 0, 0, 0];

#[derive(Debug, PartialEq)]
enum Scanline {
    PreRender,
    Visible(u8),
    PostRender,
    VBlank,
}

#[derive(Debug)]
struct RGB {
    red: u8,
    green: u8,
    blue: u8,
}

#[derive(Default)]
struct BgPixelBuffer {
    pattern_low: u8,
    pattern_high: u8,
    attribute: u8,
}

pub struct Ppu {
    ppuctrl: u8,
    ppumask: u8,
    ppustatus: u8,
    oamaddr: u8,
    oamdata: u8,
    ppuscroll: u8,
    ppuaddr: u8,
    ppudata: u8,
    oamdma: u8,

    // oam: Box<[u8; 0xff]>,
    cycles: u64,
    scanline: Scanline,
    pub frame: Box<[u8; SCREEN_WIDTH * SCREEN_HEIGHT * 3]>,

    x_scroll: u16,
    y_scroll: u16,
    nametable: u16,
}

impl Ppu {
    pub fn new() -> Ppu {
        Ppu {
            ppuctrl: 0,
            ppumask: 0,
            ppustatus: 0,
            oamaddr: 0,
            oamdata: 0,
            ppuscroll: 0,
            ppuaddr: 0,
            ppudata: 0,
            oamdma: 0,

            // oam: Box::new([0u8; 0xff]),
            cycles: 0,
            scanline: Scanline::PreRender,
            frame: Box::new([0u8; SCREEN_WIDTH * SCREEN_HEIGHT * 3]),

            x_scroll: 0,
            y_scroll: 0,
            nametable: 0x2000,
        }
    }

    pub fn read_reg(&self, addr: u16) -> u8 {
        if addr == 0x4014 {
            self.oamdma
        } else {
            match addr & 1 << 0 {
                0x0 => self.ppuctrl,
                0x1 => self.ppumask,
                0x2 => self.ppustatus,
                0x3 => self.oamaddr,
                0x4 => self.oamdata,
                0x5 => self.ppuscroll,
                0x6 => self.ppuaddr,
                0x7 => self.ppudata,
                _ => panic!("Attemped access of nonexistent PPU register: {:#x}", addr),
            }
        }
    }

    pub fn write_to_reg(&mut self, addr: u16, val: u8) {
        if addr == 0x4014 {
            self.oamdma = val;
        } else {
            match addr & 1 << 0 {
                0x0 => self.ppuctrl = val,
                0x1 => self.ppumask = val,
                0x2 => self.ppustatus = val,
                0x3 => self.oamaddr = val,
                0x4 => self.oamdata = val,
                0x5 => self.ppuscroll = val,
                0x6 => self.ppuaddr = val,
                0x7 => self.ppudata = val,
                _ => panic!("Attemped write to nonexistent PPU register: {:#x}", addr),
            }
        }
    }

    pub fn power_up(&mut self) {
        self.ppustatus = 0b10100000;
    }

    // Returns whether the NMI is currently allowed
    pub fn step(&mut self, interconnect: &mut Interconnect, current_cpu_cycle: &u64) -> bool {
        let mut vblank = false;
        while self.cycles < *current_cpu_cycle {
            match self.scanline {
                Scanline::PreRender => {
                    self.prerender();
                    vblank = false;
                }
                Scanline::Visible(line) => {
                    self.render_scanline(interconnect, line);
                    vblank = false;
                }
                Scanline::PostRender => {
                    self.postrender();
                    vblank = false;
                }
                Scanline::VBlank => {
                    println!("###################### V Blank ########################");
                    self.vblank(&mut vblank);
                    vblank = true;
                }
            }
            self.cycles += CPU_CYCLES_PER_SCANLINE; // It's easier to just deal in cpu cycles.
        }
        vblank
    }

    fn prerender(&mut self) {
        self.set_vblank(false);
        self.scanline = Scanline::Visible(0);
        self.cycles += CPU_CYCLES_PER_SCANLINE;
    }

    fn render_scanline(&mut self, interconnect: &mut Interconnect, line: u8) {
        // TODO: Refactor
        println!("################# Rendering scanline ##################: {:?}",
                 self.scanline);
        let mut bg_pixel_buffer = BgPixelBuffer::default();

        for x in 0..SCREEN_WIDTH {
            // I could probably just buffer the whole scanline, but I think this is more accurate
            if x % 8 == 0 {
                bg_pixel_buffer = self.refresh_buffer(interconnect);
            }
            //let background_pixel = self.make_background_pixel(interconnect, &bg_pixel_buffer, x as u8);
            let y = self.y_scroll as usize;
            //self.putpixel(x, y, background_pixel);
            self.x_scroll += 1;
        }

        self.cycles += CPU_CYCLES_PER_SCANLINE;
        self.y_scroll += 1;
        self.x_scroll = 0;
        if self.scanline == Scanline::Visible(LAST_VISIBLE_SCANLINE) {
            self.scanline = Scanline::PostRender;
        } else {
            self.scanline = Scanline::Visible(line + 1);
        }

    }

    fn postrender(&mut self) {
        self.scanline = Scanline::VBlank;
        self.cycles += CPU_CYCLES_PER_SCANLINE;
    }

    fn vblank(&mut self, vblank_nmi: &mut bool) {
        self.set_vblank(true);
        self.cycles += CPU_CYCLES_PER_SCANLINE * VBLANK_SCANLINES;
        self.scanline = Scanline::PreRender;
        self.x_scroll = 0;
        self.y_scroll = 0;
        *vblank_nmi = self.throw_nmi();
    }

    fn set_vblank(&mut self, status: bool) {
        match status {
            true => {
                self.ppustatus |= VBLANK_FLAG;
            }
            false => {
                self.ppustatus &= !VBLANK_FLAG;
            }
        }
    }

    fn throw_nmi(&self) -> bool {
        if (self.ppuctrl & (1 << 7)) != 0 {
            true
        } else {
            false
        }
    }

    fn refresh_buffer(&mut self, interconnect: &Interconnect) -> BgPixelBuffer {
        BgPixelBuffer {
            pattern_low: 0,
            pattern_high: 0,
            attribute: 0,
        }
    }

    /*fn make_background_pixel(&mut self, interconnect: &Interconnect, buffer: &BgPixelBuffer, x: u8) -> RGB {
        RGB {
            red: 0,
            green: 0,
            blue: 0,
        };

        println!("pattern_color: {:#b}", pattern_color);

        let tile_color = (attr_color << 2) | pattern_color;
        println!("Tile Color: {:#b}", tile_color);

        let palette_index = interconnect.ppu_read_byte(0x3f00 + (tile_color as u16)) & 0x3f;
        println!("palette index: {:#X}", palette_index);
        println!("RGB: {:?}", self.get_color(palette_index));
        self.get_color(palette_index)
    }*/

    fn get_color(&self, palette_index: u8) -> RGB {
        RGB {
            red: PALETTE[palette_index as usize * 3 + 2],
            green: PALETTE[palette_index as usize * 3 + 1],
            blue: PALETTE[palette_index as usize * 3 + 0],
        }
    }

    fn putpixel(&mut self, x: usize, y: usize, color: RGB) {
        self.frame[(y * SCREEN_WIDTH + x) * 3 + 0] = color.red;
        self.frame[(y * SCREEN_WIDTH + x) * 3 + 1] = color.green;
        self.frame[(y * SCREEN_WIDTH + x) * 3 + 2] = color.blue;
    }

    fn fetch_nametable_byte(&mut self, interconnect: &Interconnect) -> u8 {
        0
    }

    fn fetch_attribute(&mut self, interconnect: &Interconnect) -> u8 {
        0
    }
    fn fetch_pattern(&mut self, interconnect: &Interconnect, nametable_byte: u8) -> (u8, u8) {
        (0,0)
    }
}

impl fmt::Debug for Ppu {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "PPU: ppuctrl: {:#x}, ppumask: {:#x}, ppustatus: {:#x}, oamaddr: {:#x}, oamdata: \
                {:#x}, ppuscroll: {:#x}, ppuaddr: {:#x},  ppudata: {:#x}, oamdma: {:#x}",
               self.ppuctrl,
               self.ppumask,
               self.ppustatus,
               self.oamaddr,
               self.oamdata,
               self.ppuscroll,
               self.ppuaddr,
               self.ppudata,
               self.oamdma)
    }
}
<|endoftext|>


<commit_before>use std::convert::AsRef;
use std::fs::{walk_dir, PathExt, File};
use std::path::{Path, PathBuf};
use std::collections::{BTreeMap, BTreeSet};
use std::fmt::Display;
use std::os::unix::ffi::OsStrExt;
use std::io::Write;

use cronparse::{CrontabFile, CrontabFileError, CrontabFileErrorKind, Limited};
use cronparse::crontab::{EnvVarEntry, CrontabEntry, ToCrontabEntry};
use cronparse::crontab::{SystemCrontabEntry, UserCrontabEntry};
use cronparse::schedule::{Schedule, Period, Calendar};
use cronparse::interval::Interval;

fn tohex(input: &[u8]) -> String {
    #[inline]
    fn hex(d: u8) -> char {
        match d {
            0...9 => (d + 0x30) as char,
            10...15 => (d + 0x57) as char,
            _ => unreachable!("unexpected value: {}", d)
        }
    }

    let mut buf = String::with_capacity(32);
    for b in input.into_iter() {
        buf.push(hex(b >> 4));
        buf.push(hex(b & 0xf));
    }
    buf
}

pub fn process_crontab_dir<T: ToCrontabEntry, D: AsRef<Path>>(srcdir: &str, dstdir: D) {
    let files = walk_dir(srcdir).and_then(|fs| fs.map(|r| r.map(|p| p.path()))
                                       .filter(|r| r.as_ref().map(|p| p.is_file()).unwrap_or(true))
                                       .collect::<Result<Vec<PathBuf>, _>>());
    match files {
        Err(err) => error!("error processing directory {}: {}", srcdir, err),
        Ok(files) => for file in files {
            process_crontab_file::<T, _, _>(file, dstdir.as_ref());
        }
    }
}

pub fn process_crontab_file<T: ToCrontabEntry, P: AsRef<Path>, D: AsRef<Path>>(path: P, dstdir: D) {
    CrontabFile::<T>::new(path.as_ref()).map(|crontab| {
        let mut env = BTreeMap::new();
        for entry in crontab {
            match entry {
                Ok(CrontabEntry::EnvVar(EnvVarEntry(name, value))) => { env.insert(name, value); },
                Ok(data) => generate_systemd_units(data, &env, path.as_ref(), dstdir.as_ref()),
                Err(err @ CrontabFileError { kind: CrontabFileErrorKind::Io(_), .. }) => warn!("error accessing file {}: {}", path.as_ref().display(), err),
                Err(err @ CrontabFileError { kind: CrontabFileErrorKind::Parse(_), .. }) => warn!("skipping file {} due to parsing error: {}", path.as_ref().display(), err),
            }
        }
    }).unwrap_or_else(|err| {
        error!("error parsing file {}: {}", path.as_ref().display(), err);
    });
}

macro_rules! try_ {
    ($exp:expr) => {
        match $exp {
            Ok(value) => value,
            Err(err) => { error!("{}", err); return; }
        }
    }
}

#[allow(non_snake_case)]
fn generate_systemd_units(entry: CrontabEntry, env: &BTreeMap<String, String>, path: &Path, dstdir: &Path) {
    use cronparse::crontab::CrontabEntry::*;

    info!("{} => {:?}, {:?}", path.display(), entry, env);

    let mut persistent = env.get("PERSISTENT").and_then(|v| match &**v {
        "yes" | "true" | "1" => Some(true),
        "auto" | "" => None,
        _ => Some(false)
    }).unwrap_or_else(|| match entry {
        Anacron(_) | User(UserCrontabEntry { sched: Schedule::Period(_), .. }) | System(SystemCrontabEntry { sched: Schedule::Period(_), .. }) => true,
        _ => false
    });

    let batch = env.get("BATCH").map(|v| match &**v {
        "yes" | "true" | "1" => true,
        _ => false
    }).unwrap_or(false);

    let random_delay = env.get("RANDOM_DELAY").and_then(|v| v.parse::<u64>().ok()).unwrap_or(1);
    let mut delay = env.get("DELAY").and_then(|v| v.parse::<u64>().ok()).unwrap_or(0);
    let hour = env.get("START_HOURS_RANGE").and_then(|v| v.splitn(1, '-').next().and_then(|v| v.parse::<u64>().ok())).unwrap_or(0);

    let schedule = entry.period().and_then(|period| match *period {
        Period::Reboot => {
            persistent = false;
            if delay == 0 {
                delay = 1;
            }
            None
        },
        Period::Minutely => {
            persistent = false;
            Some("@minutely".to_owned())
        },
        Period::Hourly => {
            if delay == 0 {
                Some("@hourly".to_owned())
            } else {
                Some(format!("*-*-* *:{}:0", delay))
            }
        },
        Period::Midnight => {
            if delay == 0 {
                Some("@daily".to_owned())
            } else {
                Some(format!("*-*-* 0:{}:0", delay))
            }
        },
        Period::Daily => {
            if delay == 0 && hour == 0 {
                Some("@daily".to_owned())
            } else {
                Some(format!("*-*-* {}:{}:0", hour, delay))
            }
        },
        Period::Weekly => {
            if delay == 0 && hour == 0 {
                Some("@weekly".to_owned())
            } else {
                Some(format!("Mon *-*-* {}:{}:0", hour, delay))
            }
        },
        Period::Monthly => {
            if delay == 0 && hour == 0 {
                Some("@monthly".to_owned())
            } else {
                Some(format!("*-*-1 {}:{}:0", hour, delay))
            }
        },
        Period::Quaterly => {
            if delay == 0 && hour == 0 {
                Some("@quaterly".to_owned())
            } else {
                Some(format!("*-1,4,7,10-1 {}:{}:0", hour, delay))
            }
        },
        Period::Biannually => {
            if delay == 0 && hour == 0 {
                Some("@semi-annually".to_owned())
            } else {
                Some(format!("*-1,7-1 {}:{}:0", hour, delay))
            }
        },
        Period::Yearly => {
            if delay == 0 && hour == 0 {
                Some("@yearly".to_owned())
            } else {
                Some(format!("*-1-1 {}:{}:0", hour, delay))
            }
        },
        Period::Days(days) => {
            // workaround for anacrontab
            if days > 31 {
                Some(format!("*-1/{}-1 {}:{}:0", days / 30, hour, delay))
            } else {
                Some(format!("*-*-1/{} {}:{}:0", days, hour, delay))
            }
        },
    }).or_else(|| entry.calendar().and_then(|cal| {
        let Calendar {
            ref dows,
            ref days,
            ref mons,
            ref hrs,
            ref mins
        } = *cal;

        Some(format!("{} *-{}-{} {}:{}:00",
                     linearize(&**dows),
                     linearize(&**mons),
                     linearize(&**days),
                     linearize(&**hrs),
                     linearize(&**mins)))
    }));

    if let Some(command) = entry.command() {
        let mut md5ctx = ::md5::Context::new();
        md5ctx.consume(path.as_os_str().as_bytes());
        if let Some(ref schedule) = schedule {
            md5ctx.consume(schedule.as_bytes());
        }
        md5ctx.consume(command.as_bytes());
        let md5hex = tohex(&md5ctx.compute());

        let service_unit_path = dstdir.join(format!("cronjob-{}.service", md5hex));
        let timer_unit_path = dstdir.join(format!("cronjob-{}.timer", md5hex));

        debug!("generating timer {:?} from {:?}", timer_unit_path, path);
        {
            let mut timer_unit_file = try_!(File::create(timer_unit_path));

            try_!(writeln!(timer_unit_file, r###"[Unit]
Description=[Timer] "{entry}"
Documentation=man:systemd-crontab-generator(8)
PartOf=cron.target
RefuseManualStart=true
RefuseManualStop=true
SourcePath={source_crontab_path}

[Timer]
Unit={service_unit_path}
Persistent={persistent}"###,
                entry = entry,
                source_crontab_path = path.display(),
                service_unit_path = service_unit_path.display(),
                persistent = persistent,
                ));

            if let Some(schedule) = schedule {
                try_!(writeln!(timer_unit_file, "OnCalendar={}", schedule));
            } else {
                try_!(writeln!(timer_unit_file, "OnBootSec={}m", delay));
            }

            if random_delay != 1 {
                try_!(writeln!(timer_unit_file, "AccuracySec={}m", random_delay));
            }
        }

        debug!("generating service {:?} from {:?}", service_unit_path, path);
        {
            let mut service_unit_file = try_!(File::create(service_unit_path));

            try_!(writeln!(service_unit_file, r###"[Unit]
Description=[Cron] "{entry}"
Documentation=man:systemd-crontab-generator(8)
RefuseManualStart=true
RefuseManualStop=true
SourcePath={source_crontab_path}

[Service]
Type=oneshot
IgnoreSIGPIPE=false
ExecStart={command}"###,
                entry = entry,
                source_crontab_path = path.display(),
                command = command,
                ));

            if let Some(user) = entry.user() {
                try_!(writeln!(service_unit_file, "User={}", user));
            }
            if let Some(group) = entry.group() {
                try_!(writeln!(service_unit_file, "Group={}", group));
            }
            if batch {
                try_!(writeln!(service_unit_file, "CPUSchedulingPolicy=idle"));
                try_!(writeln!(service_unit_file, "IOSchedulingClass=idle"));
            }

            if !env.is_empty() {
                try_!(write!(service_unit_file, "Environment="));
                for (name, value) in env.iter() {
                    try_!(write!(service_unit_file, r#""{}={}""#, name, value));
                }
                try_!(write!(service_unit_file, "\n"));
            }
        }
    }
}

fn linearize<T: Limited + Display>(input: &[Interval<T>]) -> String {
    if input.len() == 1 && input[0] == Interval::Full(1) {
        "*".to_owned()
    } else {
        let mut output = String::new();
        for part in input.iter().flat_map(|v| v.iter()).collect::<BTreeSet<_>>().iter() {
            output.push_str(&*part.to_string());
            output.push(',');
        }
        output.pop();
        output
    }
}
<commit_msg>symlink timer to cron target<commit_after>use std::convert::AsRef;
use std::fs::{walk_dir, PathExt, File, create_dir_all};
use std::path::{Path, PathBuf};
use std::collections::{BTreeMap, BTreeSet};
use std::fmt::Display;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::symlink;
use std::io::Write;

use cronparse::{CrontabFile, CrontabFileError, CrontabFileErrorKind, Limited};
use cronparse::crontab::{EnvVarEntry, CrontabEntry, ToCrontabEntry};
use cronparse::crontab::{SystemCrontabEntry, UserCrontabEntry};
use cronparse::schedule::{Schedule, Period, Calendar};
use cronparse::interval::Interval;

fn tohex(input: &[u8]) -> String {
    #[inline]
    fn hex(d: u8) -> char {
        match d {
            0...9 => (d + 0x30) as char,
            10...15 => (d + 0x57) as char,
            _ => unreachable!("unexpected value: {}", d)
        }
    }

    let mut buf = String::with_capacity(32);
    for b in input.into_iter() {
        buf.push(hex(b >> 4));
        buf.push(hex(b & 0xf));
    }
    buf
}

pub fn process_crontab_dir<T: ToCrontabEntry, D: AsRef<Path>>(srcdir: &str, dstdir: D) {
    let files = walk_dir(srcdir).and_then(|fs| fs.map(|r| r.map(|p| p.path()))
                                       .filter(|r| r.as_ref().map(|p| p.is_file()).unwrap_or(true))
                                       .collect::<Result<Vec<PathBuf>, _>>());
    match files {
        Err(err) => error!("error processing directory {}: {}", srcdir, err),
        Ok(files) => for file in files {
            process_crontab_file::<T, _, _>(file, dstdir.as_ref());
        }
    }
}

pub fn process_crontab_file<T: ToCrontabEntry, P: AsRef<Path>, D: AsRef<Path>>(path: P, dstdir: D) {
    CrontabFile::<T>::new(path.as_ref()).map(|crontab| {
        let mut env = BTreeMap::new();
        for entry in crontab {
            match entry {
                Ok(CrontabEntry::EnvVar(EnvVarEntry(name, value))) => { env.insert(name, value); },
                Ok(data) => generate_systemd_units(data, &env, path.as_ref(), dstdir.as_ref()),
                Err(err @ CrontabFileError { kind: CrontabFileErrorKind::Io(_), .. }) => warn!("error accessing file {}: {}", path.as_ref().display(), err),
                Err(err @ CrontabFileError { kind: CrontabFileErrorKind::Parse(_), .. }) => warn!("skipping file {} due to parsing error: {}", path.as_ref().display(), err),
            }
        }
    }).unwrap_or_else(|err| {
        error!("error parsing file {}: {}", path.as_ref().display(), err);
    });
}

macro_rules! try_ {
    ($exp:expr) => {
        match $exp {
            Ok(value) => value,
            Err(err) => { error!("{}", err); return; }
        }
    }
}

#[allow(non_snake_case)]
fn generate_systemd_units(entry: CrontabEntry, env: &BTreeMap<String, String>, path: &Path, dstdir: &Path) {
    use cronparse::crontab::CrontabEntry::*;

    info!("generating units for {}: {:?}, {:?}", path.display(), entry, env);

    let mut persistent = env.get("PERSISTENT").and_then(|v| match &**v {
        "yes" | "true" | "1" => Some(true),
        "auto" | "" => None,
        _ => Some(false)
    }).unwrap_or_else(|| match entry {
        Anacron(_) | User(UserCrontabEntry { sched: Schedule::Period(_), .. }) | System(SystemCrontabEntry { sched: Schedule::Period(_), .. }) => true,
        _ => false
    });

    let batch = env.get("BATCH").map(|v| match &**v {
        "yes" | "true" | "1" => true,
        _ => false
    }).unwrap_or(false);

    let random_delay = env.get("RANDOM_DELAY").and_then(|v| v.parse::<u64>().ok()).unwrap_or(1);
    let mut delay = env.get("DELAY").and_then(|v| v.parse::<u64>().ok()).unwrap_or(0);
    let hour = env.get("START_HOURS_RANGE").and_then(|v| v.splitn(1, '-').next().and_then(|v| v.parse::<u64>().ok())).unwrap_or(0);

    let schedule = entry.period().and_then(|period| match *period {
        Period::Reboot => {
            persistent = false;
            if delay == 0 {
                delay = 1;
            }
            None
        },
        Period::Minutely => {
            persistent = false;
            Some("@minutely".to_owned())
        },
        Period::Hourly => {
            if delay == 0 {
                Some("@hourly".to_owned())
            } else {
                Some(format!("*-*-* *:{}:0", delay))
            }
        },
        Period::Midnight => {
            if delay == 0 {
                Some("@daily".to_owned())
            } else {
                Some(format!("*-*-* 0:{}:0", delay))
            }
        },
        Period::Daily => {
            if delay == 0 && hour == 0 {
                Some("@daily".to_owned())
            } else {
                Some(format!("*-*-* {}:{}:0", hour, delay))
            }
        },
        Period::Weekly => {
            if delay == 0 && hour == 0 {
                Some("@weekly".to_owned())
            } else {
                Some(format!("Mon *-*-* {}:{}:0", hour, delay))
            }
        },
        Period::Monthly => {
            if delay == 0 && hour == 0 {
                Some("@monthly".to_owned())
            } else {
                Some(format!("*-*-1 {}:{}:0", hour, delay))
            }
        },
        Period::Quaterly => {
            if delay == 0 && hour == 0 {
                Some("@quaterly".to_owned())
            } else {
                Some(format!("*-1,4,7,10-1 {}:{}:0", hour, delay))
            }
        },
        Period::Biannually => {
            if delay == 0 && hour == 0 {
                Some("@semi-annually".to_owned())
            } else {
                Some(format!("*-1,7-1 {}:{}:0", hour, delay))
            }
        },
        Period::Yearly => {
            if delay == 0 && hour == 0 {
                Some("@yearly".to_owned())
            } else {
                Some(format!("*-1-1 {}:{}:0", hour, delay))
            }
        },
        Period::Days(days) => {
            // workaround for anacrontab
            if days > 31 {
                Some(format!("*-1/{}-1 {}:{}:0", days / 30, hour, delay))
            } else {
                Some(format!("*-*-1/{} {}:{}:0", days, hour, delay))
            }
        },
    }).or_else(|| entry.calendar().and_then(|cal| {
        let Calendar {
            ref dows,
            ref days,
            ref mons,
            ref hrs,
            ref mins
        } = *cal;

        Some(format!("{} *-{}-{} {}:{}:00",
                     linearize(&**dows),
                     linearize(&**mons),
                     linearize(&**days),
                     linearize(&**hrs),
                     linearize(&**mins)))
    }));

    if let Some(command) = entry.command() {
        let mut md5ctx = ::md5::Context::new();
        md5ctx.consume(path.as_os_str().as_bytes());
        if let Some(ref schedule) = schedule {
            md5ctx.consume(schedule.as_bytes());
        }
        md5ctx.consume(command.as_bytes());
        let md5hex = tohex(&md5ctx.compute());

        let service_unit_name = format!("cronjob-{}.service", md5hex);
        let timer_unit_name = format!("cronjob-{}.timer", md5hex);

        let service_unit_path = dstdir.join(service_unit_name);
        let timer_unit_path = dstdir.join(&timer_unit_name);
        let cron_target_wants_path = dstdir.join("cron.target.wants");
        try_!(create_dir_all(&cron_target_wants_path));

        debug!("generating timer {:?} from {:?}", timer_unit_path, path);
        {
            let mut timer_unit_file = try_!(File::create(&timer_unit_path));

            try_!(writeln!(timer_unit_file, r###"[Unit]
Description=[Timer] "{entry}"
Documentation=man:systemd-crontab-generator(8)
PartOf=cron.target
RefuseManualStart=true
RefuseManualStop=true
SourcePath={source_crontab_path}

[Timer]
Unit={service_unit_path}
Persistent={persistent}"###,
                entry = entry,
                source_crontab_path = path.display(),
                service_unit_path = service_unit_path.display(),
                persistent = persistent,
                ));

            if let Some(schedule) = schedule {
                try_!(writeln!(timer_unit_file, "OnCalendar={}", schedule));
            } else {
                try_!(writeln!(timer_unit_file, "OnBootSec={}m", delay));
            }

            if random_delay != 1 {
                try_!(writeln!(timer_unit_file, "AccuracySec={}m", random_delay));
            }
        }
        try_!(symlink(timer_unit_path, cron_target_wants_path.join(timer_unit_name)));

        debug!("generating service {:?} from {:?}", service_unit_path, path);
        {
            let mut service_unit_file = try_!(File::create(service_unit_path));

            try_!(writeln!(service_unit_file, r###"[Unit]
Description=[Cron] "{entry}"
Documentation=man:systemd-crontab-generator(8)
RefuseManualStart=true
RefuseManualStop=true
SourcePath={source_crontab_path}

[Service]
Type=oneshot
IgnoreSIGPIPE=false
ExecStart={command}"###,
                entry = entry,
                source_crontab_path = path.display(),
                command = command,
                ));

            if let Some(user) = entry.user() {
                try_!(writeln!(service_unit_file, "User={}", user));
            }
            if let Some(group) = entry.group() {
                try_!(writeln!(service_unit_file, "Group={}", group));
            }
            if batch {
                try_!(writeln!(service_unit_file, "CPUSchedulingPolicy=idle"));
                try_!(writeln!(service_unit_file, "IOSchedulingClass=idle"));
            }

            if !env.is_empty() {
                try_!(write!(service_unit_file, "Environment="));
                for (name, value) in env.iter() {
                    try_!(write!(service_unit_file, r#""{}={}""#, name, value));
                }
                try_!(write!(service_unit_file, "\n"));
            }
        }
    }
}

fn linearize<T: Limited + Display>(input: &[Interval<T>]) -> String {
    if input.len() == 1 && input[0] == Interval::Full(1) {
        "*".to_owned()
    } else {
        let mut output = String::new();
        for part in input.iter().flat_map(|v| v.iter()).collect::<BTreeSet<_>>().iter() {
            output.push_str(&*part.to_string());
            output.push(',');
        }
        output.pop();
        output
    }
}
<|endoftext|>


<commit_before>use command::Command;
use vm::VM;

pub struct Program {
    instructions : Vec<Command>,
    instruction_pointer: Option<usize>,
    is_seeking: bool,
    current_depth: u64,
    goal_depth: Option<u64>,
}

impl Program {
    pub fn new () -> Program {
        Program {
            instructions: Vec::new(),
            instruction_pointer: None,
            is_seeking: false,
            current_depth: 0,
            goal_depth: None,
        }
    }

    pub fn append(&mut self, instructions: &[Command]) {
        self.instructions.extend(instructions.iter().cloned());
        if self.instruction_pointer.is_none() { self.instruction_pointer = Some(0); }
    }

    pub fn execute(&mut self, vm: &mut VM) {
        match self.instruction_pointer {
            None => {},
            Some(mut index) => {
                while index < self.instructions.len() {
                    let command = self.instructions[index];
                    if command == Command::JumpForward {
                        index = self.handle_jump_forward(vm, index);
                    } else if command == Command::JumpBackward {
                        self.current_depth = self.current_depth - 1
                    } else {
                        vm.apply(command);
                        index = index + 1;
                    }
                }
                self.instruction_pointer = Some(index);
            }
        }
    }

    fn seek_forward(&mut self, starting_index: usize) -> (usize, bool) {
        let goal_depth = self.goal_depth.unwrap();
        for index in starting_index..self.instructions.len() {
            match self.instructions[index] {
                Command::JumpForward => self.current_depth = self.current_depth + 1,
                Command::JumpBackward => self.current_depth = self.current_depth - 1,
                _ => {},
            }
            if self.current_depth == goal_depth { return (index + 1, false) }
        }
        (self.instructions.len(), true)
    }

    fn handle_jump_forward(&mut self, vm: &VM, index: usize) -> usize {
        if vm.cells[vm.data_pointer] == 0 {
            self.goal_depth = Some(self.current_depth);
            let (index, still_seeking) = self.seek_forward(index);
            self.is_seeking = still_seeking;
            return index;
        } else {
            self.current_depth = self.current_depth + 1;
            return index;
        }
    }
}
<commit_msg>Change function name to be more clear<commit_after>use command::Command;
use vm::VM;

pub struct Program {
    instructions : Vec<Command>,
    instruction_pointer: Option<usize>,
    is_seeking: bool,
    current_depth: u64,
    goal_depth: Option<u64>,
}

impl Program {
    pub fn new () -> Program {
        Program {
            instructions: Vec::new(),
            instruction_pointer: None,
            is_seeking: false,
            current_depth: 0,
            goal_depth: None,
        }
    }

    pub fn append(&mut self, instructions: &[Command]) {
        self.instructions.extend(instructions.iter().cloned());
        if self.instruction_pointer.is_none() { self.instruction_pointer = Some(0); }
    }

    pub fn execute(&mut self, vm: &mut VM) {
        match self.instruction_pointer {
            None => {},
            Some(mut index) => {
                while index < self.instructions.len() {
                    let command = self.instructions[index];
                    if command == Command::JumpForward {
                        index = self.start_jump_forward(vm, index);
                    } else if command == Command::JumpBackward {
                        self.current_depth = self.current_depth - 1
                    } else {
                        vm.apply(command);
                        index = index + 1;
                    }
                }
                self.instruction_pointer = Some(index);
            }
        }
    }

    fn seek_forward(&mut self, starting_index: usize) -> (usize, bool) {
        let goal_depth = self.goal_depth.unwrap();
        for index in starting_index..self.instructions.len() {
            match self.instructions[index] {
                Command::JumpForward => self.current_depth = self.current_depth + 1,
                Command::JumpBackward => self.current_depth = self.current_depth - 1,
                _ => {},
            }
            if self.current_depth == goal_depth { return (index + 1, false) }
        }
        (self.instructions.len(), true)
    }

    fn start_jump_forward(&mut self, vm: &VM, index: usize) -> usize {
        if vm.cells[vm.data_pointer] == 0 {
            self.goal_depth = Some(self.current_depth);
            let (index, still_seeking) = self.seek_forward(index);
            self.is_seeking = still_seeking;
            return index;
        } else {
            self.current_depth = self.current_depth + 1;
            return index;
        }
    }
}
<|endoftext|>