<commit_before>pub mod options;
pub mod results;

use bson;
use bson::Bson;

use client::db::Database;
use client::common::{ReadPreference, WriteConcern};
use client::coll::options::*;
use client::coll::results::*;

use client::wire_protocol::flags::OpQueryFlags;
use client::wire_protocol::operations::Message;

/// Interfaces with a MongoDB collection.
pub struct Collection<'a> {
    db: &'a Database<'a>,
    pub namespace: String,
    read_preference: ReadPreference,
    write_concern: WriteConcern,
}

impl<'a> Collection<'a> {
    /// Creates a collection representation with optional read and write controls.
    ///
    /// If `create` is specified, the collection will be explicitly created in the database.
    pub fn new(db: &'a Database<'a>, name: &str, create: bool,
               read_preference: Option<ReadPreference>, write_concern: Option<WriteConcern>) -> Collection<'a> {

        let rp = match read_preference {
            Some(rp) => rp,
            None => db.read_preference.to_owned(),
        };

        let wc = match write_concern {
            Some(wc) => wc,
            None => db.write_concern.to_owned(),
        };

        Collection {
            db: db,
            namespace: format!("{}.{}", db.name, name),
            read_preference: rp,
            write_concern: wc,
        }
    }

    /// Returns a unique operational request id.
    pub fn get_req_id(&self) -> i32 {
        self.db.client.get_req_id()
    }

    /// Extracts the collection name from the namespace.
    pub fn name(&self) -> String {
        match self.namespace.find(".") {
            Some(idx) => self.namespace[idx+1..].to_owned(),
            None => {
                // '.' is inserted in Collection::new, so this should only panic due to user error.
                let msg = format!("Invalid namespace specified: '{}'.", self.namespace);
                panic!(msg);
            }
        }
    }

    /// Permanently deletes the collection from the database.
    pub fn drop(&'a self) -> Result<(), String> {
        self.db.drop_collection(&self.name()[..])
    }

    // Read Spec
    /// Runs an aggregation framework pipeline.
    pub fn aggregate(pipeline: &[bson::Document], options: AggregateOptions) -> Result<Vec<bson::Document>, String> {
        unimplemented!()
    }

    /// Gets the number of documents matching the filter.
    pub fn count(filter: bson::Document, options: CountOptions) -> Result<i64, String> {
        unimplemented!()
    }

    /// Finds the distinct values for a specified field across a single collection.
    pub fn distinct(field_name: &str, filter: bson::Document, options: DistinctOptions) -> Result<Vec<Bson>, String> {
        unimplemented!()
    }

    /// Returns a list of documents within the collection that match the filter.
    pub fn find(&self, filter: Option<bson::Document>, options: Option<FindOptions>)
                -> Result<Vec<bson::Document>, String> {

        let doc = match filter {
            Some(bson) => bson,
            None => bson::Document::new(),
        };

        let options = match options {
            Some(opts) => opts,
            None => FindOptions::new(),
        };

        let flags = OpQueryFlags::with_find_options(&options);
        let req = try!(Message::with_query(self.get_req_id(), flags, self.namespace.to_owned(),
                                           options.skip as i32, options.limit, doc, options.projection));

        let socket = match self.db.client.socket.lock() {
            Ok(val) => val,
            _ => return Err("Client socket lock poisoned.".to_owned()),
        };

        try!(req.write(&mut *socket.borrow_mut()));
        let message = try!(Message::read(&mut *socket.borrow_mut()));

        match message {
            Message::OpReply { header: _, flags: _, cursor_id: _,
                               starting_from: _, number_returned: _,
                               documents, } => Ok(documents),
            _ => Err("Invalid response received from server".to_owned())
        }
    }

    /// Returns the first document within the collection that matches the filter, or None.
    pub fn find_one(&self, filter: Option<bson::Document>, options: Option<FindOptions>)
                    -> Result<Option<bson::Document>, String> {

        let options = match options {
            Some(opts) => opts,
            None => FindOptions::new(),
        };

        let res = try!(self.find(filter, Some(options.with_limit(1))));
        match res.len() {
            0 => Ok(None),
            1 => Ok(Some(res[0].to_owned())),
            n => Err(format!("Expected a single document, found {}.", n)),
        }
    }

    // Find and Modify Spec
    /// Finds a single document and deletes it, returning the original.
    pub fn find_one_and_delete(&self, filter: bson::Document,
                               options: Option<FindOneAndDeleteOptions>)  -> Option<bson::Document> {
        unimplemented!()
    }

    /// Finds a single document and replaces it, returning either the original
    ///  or replaced document.
    pub fn find_one_and_replace(&self, filter: bson::Document, replacement: bson::Document,
                                options: Option<FindOneAndReplaceOptions>)  -> Option<bson::Document> {
        unimplemented!()
    }

    /// Finds a single document and updates it, returning either the original
    ///  or updated document.
    pub fn find_one_and_update(&self, filter: bson::Document, update: bson::Document,
                               options: Option<FindOneAndUpdateOptions>)  -> Option<bson::Document> {
        unimplemented!()
    }

    // Write Spec
    /// Sends a batch of writes to the server at the same time.
    pub fn bulk_write(requests: &[WriteModel], ordered: bool) -> BulkWriteResult {
        unimplemented!()
    }

    // Internal insertion helper function.
    fn insert(&self, docs: Vec<bson::Document>, ordered: bool,
              write_concern: Option<WriteConcern>) -> Result<bson::Document, String> {

        let wc = match write_concern {
            Some(wc) => wc,
            None => WriteConcern::new(),
        };

        let converted_docs = docs.iter().map(|doc| Bson::Document(doc.to_owned())).collect();

        let mut cmd = bson::Document::new();
        cmd.insert("insert".to_owned(), Bson::String(self.name()));
        cmd.insert("documents".to_owned(), Bson::Array(converted_docs));
        cmd.insert("ordered".to_owned(), Bson::Boolean(ordered));
        cmd.insert("writeConcern".to_owned(), Bson::Document(wc.to_bson()));

        let res = try!(self.db.command(cmd));
        match res {
            Some(doc) => Ok(doc),
            None => Err("Insertion reply not received from server.".to_owned()),
        }
    }

    /// Inserts the provided document. If the document is missing an identifier,
    ///  the driver should generate one.
    pub fn insert_one(&self, doc: bson::Document, write_concern: Option<WriteConcern>) -> Result<InsertOneResult, String> {
        let res = try!(self.insert(vec!(doc), true, write_concern));
        Ok(InsertOneResult::new(res))
    }

    /// Inserts the provided documents. If any documents are missing an identifier,
    ///  the driver should generate them.
    pub fn insert_many(&self, docs: Vec<bson::Document>, ordered: bool,
                       write_concern: Option<WriteConcern>) -> Result<InsertManyResult, String> {
        let res = try!(self.insert(docs, ordered, write_concern));
        Ok(InsertManyResult::new(res))
    }

    // Internal deletion helper function.
    fn delete(&self, filter: bson::Document, limit: i64, write_concern: Option<WriteConcern>) -> Result<DeleteResult, String> {
        let wc = match write_concern {
            Some(wc) => wc,
            None => WriteConcern::new(),
        };

        let mut deletes = bson::Document::new();
        deletes.insert("q".to_owned(), Bson::Document(filter));
        deletes.insert("limit".to_owned(), Bson::I64(limit));

        let mut cmd = bson::Document::new();
        cmd.insert("delete".to_owned(), Bson::String(self.name()));
        cmd.insert("deletes".to_owned(), Bson::Array(vec!(Bson::Document(deletes))));
        cmd.insert("writeConcern".to_owned(), Bson::Document(wc.to_bson()));

        let res = try!(self.db.command(cmd));
        match res {
            Some(doc) => Ok(DeleteResult::new(doc)),
            None => Err("Delete reply not received from server.".to_owned()),
        }
    }

    /// Deletes a single document.
    pub fn delete_one(&self, filter: bson::Document, write_concern: Option<WriteConcern>) -> Result<DeleteResult, String> {
        self.delete(filter, 1, write_concern)
    }

    /// Deletes multiple documents.
    pub fn delete_many(&self, filter: bson::Document, write_concern: Option<WriteConcern>) -> Result<DeleteResult, String> {
        self.delete(filter, 0, write_concern)
    }

    // Internal update helper function.
    fn update(&self, filter: bson::Document, update: bson::Document, upsert: bool, multi: bool,
              write_concern: Option<WriteConcern>) -> Result<UpdateResult, String> {

        let wc = match write_concern {
            Some(wc) => wc,
            None => WriteConcern::new(),
        };

        let mut updates = bson::Document::new();
        updates.insert("q".to_owned(), Bson::Document(filter));
        updates.insert("u".to_owned(), Bson::Document(update));
        updates.insert("upsert".to_owned(), Bson::Boolean(upsert));
        updates.insert("multi".to_owned(), Bson::Boolean(multi));

        let mut cmd = bson::Document::new();
        cmd.insert("update".to_owned(), Bson::String(self.name()));
        cmd.insert("updates".to_owned(), Bson::Array(vec!(Bson::Document(updates))));
        cmd.insert("writeConcern".to_owned(), Bson::Document(wc.to_bson()));

        let res = try!(self.db.command(cmd));
        match res {
            Some(doc) => Ok(UpdateResult::new(doc)),
            None => Err("delete_many reply not received from server.".to_owned()),
        }
    }

    /// Replaces a single document.
    pub fn replace_one(&self, filter: bson::Document, replacement: bson::Document, upsert: bool,
                       write_concern: Option<WriteConcern>) -> Result<UpdateResult, String> {

        for key in replacement.keys() {
            if key.starts_with("$") {
                return Err("Replacement cannot include $ operators.".to_owned());
            }
        }

        self.update(filter, replacement, upsert, false, write_concern)
    }

    /// Updates a single document.
    pub fn update_one(&self, filter: bson::Document, update: bson::Document, upsert: bool,
                      write_concern: Option<WriteConcern>) -> Result<UpdateResult, String> {

        for key in update.keys() {
            if !key.starts_with("$") {
                return Err("Update only works with $ operators.".to_owned());
            }
        }

        self.update(filter, update, upsert, false, write_concern)
    }

    /// Updates multiple documents.
    pub fn update_many(&self, filter: bson::Document, update: bson::Document, upsert: bool,
                       write_concern: Option<WriteConcern>) -> Result<UpdateResult, String> {

        for key in update.keys() {
            if !key.starts_with("$") {
                return Err("Update only works with $ operators.".to_owned());
            }
        }

        self.update(filter, update, upsert, true, write_concern)
    }
}
<commit_msg>Fix non-ascii substrings + coll style<commit_after>pub mod options;
pub mod results;

use bson;
use bson::Bson;

use client::db::Database;
use client::common::{ReadPreference, WriteConcern};
use client::coll::options::*;
use client::coll::results::*;

use client::wire_protocol::flags::OpQueryFlags;
use client::wire_protocol::operations::Message;

/// Interfaces with a MongoDB collection.
pub struct Collection<'a> {
    db: &'a Database<'a>,
    pub namespace: String,
    read_preference: ReadPreference,
    write_concern: WriteConcern,
}

impl<'a> Collection<'a> {
    /// Creates a collection representation with optional read and write controls.
    ///
    /// If `create` is specified, the collection will be explicitly created in the database.
    pub fn new(db: &'a Database<'a>, name: &str, create: bool,
               read_preference: Option<ReadPreference>, write_concern: Option<WriteConcern>) -> Collection<'a> {

        let rp = read_preference.unwrap_or(db.read_preference.to_owned());
        let wc = write_concern.unwrap_or(db.write_concern.to_owned());

        Collection {
            db: db,
            namespace: format!("{}.{}", db.name, name),
            read_preference: rp,
            write_concern: wc,
        }
    }

    /// Returns a unique operational request id.
    pub fn get_req_id(&self) -> i32 {
        self.db.client.get_req_id()
    }

    /// Extracts the collection name from the namespace.
    pub fn name(&self) -> String {
        match self.namespace.find(".") {
            Some(idx) => self.namespace[self.namespace.char_indices()
                                        .nth(idx+1).unwrap().0..].to_owned(),
            None => {
                // '.' is inserted in Collection::new, so this should only panic due to user error.
                let msg = format!("Invalid namespace specified: '{}'.", self.namespace);
                panic!(msg);
            }
        }
    }

    /// Permanently deletes the collection from the database.
    pub fn drop(&'a self) -> Result<(), String> {
        self.db.drop_collection(&self.name()[..])
    }

    /// Runs an aggregation framework pipeline.
    pub fn aggregate(pipeline: &[bson::Document], options: AggregateOptions) -> Result<Vec<bson::Document>, String> {
        unimplemented!()
    }

    /// Gets the number of documents matching the filter.
    pub fn count(filter: bson::Document, options: CountOptions) -> Result<i64, String> {
        unimplemented!()
    }

    /// Finds the distinct values for a specified field across a single collection.
    pub fn distinct(field_name: &str, filter: bson::Document, options: DistinctOptions) -> Result<Vec<Bson>, String> {
        unimplemented!()
    }

    /// Returns a list of documents within the collection that match the filter.
    pub fn find(&self, filter: Option<bson::Document>, options: Option<FindOptions>)
                -> Result<Vec<bson::Document>, String> {

        let doc = filter.unwrap_or(bson::Document::new());
        let options = options.unwrap_or(FindOptions::new());
        let flags = OpQueryFlags::with_find_options(&options);

        let req = try!(Message::with_query(self.get_req_id(), flags, self.namespace.to_owned(),
                                           options.skip as i32, options.limit, doc, options.projection));

        let socket = match self.db.client.socket.lock() {
            Ok(val) => val,
            _ => return Err("Client socket lock poisoned.".to_owned()),
        };

        try!(req.write(&mut *socket.borrow_mut()));
        let message = try!(Message::read(&mut *socket.borrow_mut()));

        match message {
            Message::OpReply { header: _, flags: _, cursor_id: _,
                               starting_from: _, number_returned: _,
                               documents, } => Ok(documents),
            _ => Err("Invalid response received from server".to_owned())
        }
    }

    /// Returns the first document within the collection that matches the filter, or None.
    pub fn find_one(&self, filter: Option<bson::Document>, options: Option<FindOptions>)
                    -> Result<Option<bson::Document>, String> {

        let options = options.unwrap_or(FindOptions::new());
        let res = try!(self.find(filter, Some(options.with_limit(1))));
        match res.len() {
            0 => Ok(None),
            1 => Ok(Some(res[0].to_owned())),
            n => Err(format!("Expected a single document, found {}.", n)),
        }
    }

    /// Finds a single document and deletes it, returning the original.
    pub fn find_one_and_delete(&self, filter: bson::Document,
                               options: Option<FindOneAndDeleteOptions>)  -> Option<bson::Document> {
        unimplemented!()
    }

    /// Finds a single document and replaces it, returning either the original
    /// or replaced document.
    pub fn find_one_and_replace(&self, filter: bson::Document, replacement: bson::Document,
                                options: Option<FindOneAndReplaceOptions>)  -> Option<bson::Document> {
        unimplemented!()
    }

    /// Finds a single document and updates it, returning either the original
    /// or updated document.
    pub fn find_one_and_update(&self, filter: bson::Document, update: bson::Document,
                               options: Option<FindOneAndUpdateOptions>)  -> Option<bson::Document> {
        unimplemented!()
    }

    /// Sends a batch of writes to the server at the same time.
    pub fn bulk_write(requests: &[WriteModel], ordered: bool) -> BulkWriteResult {
        unimplemented!()
    }

    // Internal insertion helper function.
    fn insert(&self, docs: Vec<bson::Document>, ordered: bool,
              write_concern: Option<WriteConcern>) -> Result<bson::Document, String> {

        let wc =  write_concern.unwrap_or(WriteConcern::new());
        let converted_docs = docs.iter().map(|doc| Bson::Document(doc.to_owned())).collect();

        let mut cmd = bson::Document::new();
        cmd.insert("insert".to_owned(), Bson::String(self.name()));
        cmd.insert("documents".to_owned(), Bson::Array(converted_docs));
        cmd.insert("ordered".to_owned(), Bson::Boolean(ordered));
        cmd.insert("writeConcern".to_owned(), Bson::Document(wc.to_bson()));

        let res = try!(self.db.command(cmd));
        match res {
            Some(doc) => Ok(doc),
            None => Err("Insertion reply not received from server.".to_owned()),
        }
    }

    /// Inserts the provided document. If the document is missing an identifier,
    /// the driver should generate one.
    pub fn insert_one(&self, doc: bson::Document, write_concern: Option<WriteConcern>) -> Result<InsertOneResult, String> {
        let res = try!(self.insert(vec!(doc), true, write_concern));
        Ok(InsertOneResult::new(res))
    }

    /// Inserts the provided documents. If any documents are missing an identifier,
    /// the driver should generate them.
    pub fn insert_many(&self, docs: Vec<bson::Document>, ordered: bool,
                       write_concern: Option<WriteConcern>) -> Result<InsertManyResult, String> {
        let res = try!(self.insert(docs, ordered, write_concern));
        Ok(InsertManyResult::new(res))
    }

    // Internal deletion helper function.
    fn delete(&self, filter: bson::Document, limit: i64, write_concern: Option<WriteConcern>) -> Result<DeleteResult, String> {
        let wc = write_concern.unwrap_or(WriteConcern::new());

        let mut deletes = bson::Document::new();
        deletes.insert("q".to_owned(), Bson::Document(filter));
        deletes.insert("limit".to_owned(), Bson::I64(limit));

        let mut cmd = bson::Document::new();
        cmd.insert("delete".to_owned(), Bson::String(self.name()));
        cmd.insert("deletes".to_owned(), Bson::Array(vec!(Bson::Document(deletes))));
        cmd.insert("writeConcern".to_owned(), Bson::Document(wc.to_bson()));

        let res = try!(self.db.command(cmd));
        match res {
            Some(doc) => Ok(DeleteResult::new(doc)),
            None => Err("Delete reply not received from server.".to_owned()),
        }
    }

    /// Deletes a single document.
    pub fn delete_one(&self, filter: bson::Document, write_concern: Option<WriteConcern>) -> Result<DeleteResult, String> {
        self.delete(filter, 1, write_concern)
    }

    /// Deletes multiple documents.
    pub fn delete_many(&self, filter: bson::Document, write_concern: Option<WriteConcern>) -> Result<DeleteResult, String> {
        self.delete(filter, 0, write_concern)
    }

    // Internal update helper function.
    fn update(&self, filter: bson::Document, update: bson::Document, upsert: bool, multi: bool,
              write_concern: Option<WriteConcern>) -> Result<UpdateResult, String> {

        let wc = write_concern.unwrap_or(WriteConcern::new());

        let mut updates = bson::Document::new();
        updates.insert("q".to_owned(), Bson::Document(filter));
        updates.insert("u".to_owned(), Bson::Document(update));
        updates.insert("upsert".to_owned(), Bson::Boolean(upsert));
        updates.insert("multi".to_owned(), Bson::Boolean(multi));

        let mut cmd = bson::Document::new();
        cmd.insert("update".to_owned(), Bson::String(self.name()));
        cmd.insert("updates".to_owned(), Bson::Array(vec!(Bson::Document(updates))));
        cmd.insert("writeConcern".to_owned(), Bson::Document(wc.to_bson()));

        let res = try!(self.db.command(cmd));
        match res {
            Some(doc) => Ok(UpdateResult::new(doc)),
            None => Err("delete_many reply not received from server.".to_owned()),
        }
    }

    /// Replaces a single document.
    pub fn replace_one(&self, filter: bson::Document, replacement: bson::Document, upsert: bool,
                       write_concern: Option<WriteConcern>) -> Result<UpdateResult, String> {

        for key in replacement.keys() {
            if key.starts_with("$") {
                return Err("Replacement cannot include $ operators.".to_owned());
            }
        }

        self.update(filter, replacement, upsert, false, write_concern)
    }

    /// Updates a single document.
    pub fn update_one(&self, filter: bson::Document, update: bson::Document, upsert: bool,
                      write_concern: Option<WriteConcern>) -> Result<UpdateResult, String> {

        for key in update.keys() {
            if !key.starts_with("$") {
                return Err("Update only works with $ operators.".to_owned());
            }
        }

        self.update(filter, update, upsert, false, write_concern)
    }

    /// Updates multiple documents.
    pub fn update_many(&self, filter: bson::Document, update: bson::Document, upsert: bool,
                       write_concern: Option<WriteConcern>) -> Result<UpdateResult, String> {

        for key in update.keys() {
            if !key.starts_with("$") {
                return Err("Update only works with $ operators.".to_owned());
            }
        }

        self.update(filter, update, upsert, true, write_concern)
    }
}
<|endoftext|>


<commit_before>use crate::{Command, Merge};
use std::fmt;
use std::iter::{FromIterator, IntoIterator};
use std::vec::IntoIter;

/// Macro for merging commands.
///
/// # Examples
/// ```
/// # use undo::{merge, Command, Record};
/// #[derive(Debug)]
/// struct Add(char);
///
/// impl Command<String> for Add {
///     fn apply(&mut self, s: &mut String) -> undo::Result {
///         s.push(self.0);
///         Ok(())
///     }
///
///     fn undo(&mut self, s: &mut String) -> undo::Result {
///         self.0 = s.pop().ok_or("`s` is empty")?;
///         Ok(())
///     }
/// }
///
/// fn main() -> undo::Result {
///     let mut record = Record::default();
///     record.apply(merge![Add('a'), Add('b'), Add('c')])?;
///     assert_eq!(record.as_receiver(), "abc");
///     record.undo().unwrap()?;
///     assert_eq!(record.as_receiver(), "");
///     record.redo().unwrap()?;
///     assert_eq!(record.as_receiver(), "abc");
///     Ok(())
/// }
/// ```
#[macro_export]
macro_rules! merge {
    ($cmd1:expr, $cmd2:expr, $($commands:expr),*) => {{
        let mut merged = $crate::Merged::new($cmd1, $cmd2);
        $(merged.push($commands);)*
        merged
    }};
}

/// The result of merging commands.
///
/// The [`merge!`](macro.merge.html) macro can be used for convenience when merging commands.
pub struct Merged<R> {
    commands: Vec<Box<dyn Command<R>>>,
    #[cfg(feature = "display")]
    summary: Option<String>,
}

impl<R> Merged<R> {
    /// Merges `cmd1` and `cmd2` into a single command.
    #[inline]
    pub fn new(cmd1: impl Command<R> + 'static, cmd2: impl Command<R> + 'static) -> Merged<R> {
        Merged {
            commands: vec![Box::new(cmd1), Box::new(cmd2)],
            #[cfg(feature = "display")]
            summary: None,
        }
    }

    /// Merges `self` with `command`.
    #[inline]
    pub fn push(&mut self, command: impl Command<R> + 'static) {
        self.commands.push(Box::new(command));
    }

    /// Merges `self` with `command` and returns the merged command.
    #[inline]
    pub fn join(mut self, command: impl Command<R> + 'static) -> Merged<R> {
        self.push(command);
        self
    }

    /// Sets a summary for the two merged commands. This overrides the default display text.
    #[inline]
    #[cfg(feature = "display")]
    pub fn set_summary(&mut self, summary: impl Into<String>) {
        self.summary = Some(summary.into());
    }
}

impl<R> Command<R> for Merged<R> {
    #[inline]
    fn apply(&mut self, receiver: &mut R) -> crate::Result {
        for command in &mut self.commands {
            command.apply(receiver)?;
        }
        Ok(())
    }

    #[inline]
    fn undo(&mut self, receiver: &mut R) -> crate::Result {
        for command in self.commands.iter_mut().rev() {
            command.undo(receiver)?;
        }
        Ok(())
    }

    #[inline]
    fn redo(&mut self, receiver: &mut R) -> crate::Result {
        for command in &mut self.commands {
            command.redo(receiver)?;
        }
        Ok(())
    }

    #[inline]
    fn merge(&self) -> Merge {
        self.commands.first().map_or(Merge::Always, |c| c.merge())
    }
}

impl<R> Default for Merged<R> {
    #[inline]
    fn default() -> Self {
        Merged {
            commands: Vec::default(),
            #[cfg(feature = "display")]
            summary: None,
        }
    }
}

impl<R, C: Command<R> + 'static> FromIterator<C> for Merged<R> {
    #[inline]
    fn from_iter<T: IntoIterator<Item = C>>(commands: T) -> Self {
        Merged {
            commands: commands.into_iter().map(|c| Box::new(c) as _).collect(),
            #[cfg(feature = "display")]
            summary: None,
        }
    }
}

impl<R> IntoIterator for Merged<R> {
    type Item = Box<dyn Command<R>>;
    type IntoIter = IntoIter<Self::Item>;

    #[inline]
    fn into_iter(self) -> <Self as IntoIterator>::IntoIter {
        self.commands.into_iter()
    }
}

impl<R, C: Command<R> + 'static> Extend<C> for Merged<R> {
    #[inline]
    fn extend<T: IntoIterator<Item = C>>(&mut self, iter: T) {
        self.commands
            .extend(iter.into_iter().map(|c| Box::new(c) as _));
    }
}

impl<R> fmt::Debug for Merged<R> {
    #[inline]
    #[cfg(not(feature = "display"))]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Merged")
            .field("commands", &self.commands)
            .finish()
    }

    #[inline]
    #[cfg(feature = "display")]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Merged")
            .field("commands", &self.commands)
            .field("summary", &self.summary)
            .finish()
    }
}

#[cfg(feature = "display")]
impl<R> fmt::Display for Merged<R> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match &self.summary {
            Some(summary) => f.write_str(summary),
            None => {
                if let Some((first, commands)) = self.commands.split_first() {
                    (first as &dyn fmt::Display).fmt(f)?;
                    for command in commands {
                        write!(f, "\n\n{}", command)?;
                    }
                }
                Ok(())
            }
        }
    }
}
<commit_msg>Allow merge macro to take less than two commands<commit_after>use crate::{Command, Merge};
use std::fmt;
use std::iter::{FromIterator, IntoIterator};
use std::vec::IntoIter;

/// Macro for merging commands.
///
/// # Examples
/// ```
/// # use undo::{merge, Command, Record};
/// #[derive(Debug)]
/// struct Add(char);
///
/// impl Command<String> for Add {
///     fn apply(&mut self, s: &mut String) -> undo::Result {
///         s.push(self.0);
///         Ok(())
///     }
///
///     fn undo(&mut self, s: &mut String) -> undo::Result {
///         self.0 = s.pop().ok_or("`s` is empty")?;
///         Ok(())
///     }
/// }
///
/// fn main() -> undo::Result {
///     let mut record = Record::default();
///     record.apply(merge![Add('a'), Add('b'), Add('c')])?;
///     assert_eq!(record.as_receiver(), "abc");
///     record.undo().unwrap()?;
///     assert_eq!(record.as_receiver(), "");
///     record.redo().unwrap()?;
///     assert_eq!(record.as_receiver(), "abc");
///     Ok(())
/// }
/// ```
#[macro_export]
macro_rules! merge {
    ($($commands:expr),*) => {{
        let mut merged = $crate::Merged::default();
        $(merged.push($commands);)*
        merged
    }};
}

/// The result of merging commands.
///
/// The [`merge!`](macro.merge.html) macro can be used for convenience when merging commands.
pub struct Merged<R> {
    commands: Vec<Box<dyn Command<R>>>,
    #[cfg(feature = "display")]
    summary: Option<String>,
}

impl<R> Merged<R> {
    /// Merges `cmd1` and `cmd2` into a single command.
    #[inline]
    pub fn new(cmd1: impl Command<R> + 'static, cmd2: impl Command<R> + 'static) -> Merged<R> {
        Merged {
            commands: vec![Box::new(cmd1), Box::new(cmd2)],
            #[cfg(feature = "display")]
            summary: None,
        }
    }

    /// Merges `self` with `command`.
    #[inline]
    pub fn push(&mut self, command: impl Command<R> + 'static) {
        self.commands.push(Box::new(command));
    }

    /// Merges `self` with `command` and returns the merged command.
    #[inline]
    pub fn join(mut self, command: impl Command<R> + 'static) -> Merged<R> {
        self.push(command);
        self
    }

    /// Sets a summary for the two merged commands. This overrides the default display text.
    #[inline]
    #[cfg(feature = "display")]
    pub fn set_summary(&mut self, summary: impl Into<String>) {
        self.summary = Some(summary.into());
    }
}

impl<R> Command<R> for Merged<R> {
    #[inline]
    fn apply(&mut self, receiver: &mut R) -> crate::Result {
        for command in &mut self.commands {
            command.apply(receiver)?;
        }
        Ok(())
    }

    #[inline]
    fn undo(&mut self, receiver: &mut R) -> crate::Result {
        for command in self.commands.iter_mut().rev() {
            command.undo(receiver)?;
        }
        Ok(())
    }

    #[inline]
    fn redo(&mut self, receiver: &mut R) -> crate::Result {
        for command in &mut self.commands {
            command.redo(receiver)?;
        }
        Ok(())
    }

    #[inline]
    fn merge(&self) -> Merge {
        self.commands.first().map_or(Merge::Always, |c| c.merge())
    }
}

impl<R> Default for Merged<R> {
    #[inline]
    fn default() -> Self {
        Merged {
            commands: Vec::default(),
            #[cfg(feature = "display")]
            summary: None,
        }
    }
}

impl<R, C: Command<R> + 'static> FromIterator<C> for Merged<R> {
    #[inline]
    fn from_iter<T: IntoIterator<Item = C>>(commands: T) -> Self {
        Merged {
            commands: commands.into_iter().map(|c| Box::new(c) as _).collect(),
            #[cfg(feature = "display")]
            summary: None,
        }
    }
}

impl<R> IntoIterator for Merged<R> {
    type Item = Box<dyn Command<R>>;
    type IntoIter = IntoIter<Self::Item>;

    #[inline]
    fn into_iter(self) -> <Self as IntoIterator>::IntoIter {
        self.commands.into_iter()
    }
}

impl<R, C: Command<R> + 'static> Extend<C> for Merged<R> {
    #[inline]
    fn extend<T: IntoIterator<Item = C>>(&mut self, iter: T) {
        self.commands
            .extend(iter.into_iter().map(|c| Box::new(c) as _));
    }
}

impl<R> fmt::Debug for Merged<R> {
    #[inline]
    #[cfg(not(feature = "display"))]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Merged")
            .field("commands", &self.commands)
            .finish()
    }

    #[inline]
    #[cfg(feature = "display")]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Merged")
            .field("commands", &self.commands)
            .field("summary", &self.summary)
            .finish()
    }
}

#[cfg(feature = "display")]
impl<R> fmt::Display for Merged<R> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match &self.summary {
            Some(summary) => f.write_str(summary),
            None => {
                if let Some((first, commands)) = self.commands.split_first() {
                    (first as &dyn fmt::Display).fmt(f)?;
                    for command in commands {
                        write!(f, "\n\n{}", command)?;
                    }
                }
                Ok(())
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::cast::transmute;
use std::libc::{c_int};
use std::uint;

use audio::{AudioFormat, Channels, Mono, Stereo};
use video::ll::SDL_RWFromFile; // XXX refactoring
use get_error;

// Setup linking for all targets.
#[cfg(target_os="macos")]
mod mac {
    #[cfg(mac_framework)]
    #[link_args="-framework SDL_mixer"]
    extern {}

    #[cfg(mac_dylib)]
    #[link_args="-lSDL_mixer"]
    extern {}
}

#[cfg(not(target_os="macos"))]
mod others {
    #[link_args="-lSDL_mixer"]
    extern {}
}

pub mod ll {
    use video::ll::SDL_RWops; // XXX refactoring

    use std::libc::c_int;

    pub struct Mix_Chunk {
        allocated: c_int,
        abuf: *u8,
        alen: u32,
        volume: u8,
    }

    externfn!(fn Mix_OpenAudio(frequency: c_int, format: u16, channels: c_int, chunksize: c_int)
                    -> c_int)
    externfn!(fn Mix_QuerySpec(frequency: *mut c_int, format: *mut u16, channels: *mut c_int)
                    -> c_int)
    externfn!(fn Mix_LoadWAV_RW(src: *SDL_RWops, freesrc: c_int) -> *Mix_Chunk)
    externfn!(fn Mix_FreeChunk(chunk: *Mix_Chunk))
    externfn!(fn Mix_AllocateChannels(numchans: c_int) -> c_int)
    externfn!(fn Mix_Playing(channel: c_int) -> c_int)
    externfn!(fn Mix_PlayChannelTimed(channel: c_int, chunk: *Mix_Chunk, loops: c_int, ticks: c_int)
                    -> c_int)
    externfn!(fn Mix_GetChunk(channel: c_int) -> *Mix_Chunk)
    externfn!(fn Mix_CloseAudio())
    externfn!(fn Mix_HaltChannel(channel: c_int) -> c_int)
}

pub struct Chunk {
    priv data: ChunkData
}

enum ChunkData {
    Borrowed(*ll::Mix_Chunk),
    Allocated(*ll::Mix_Chunk),
    OwnedBuffer(ChunkAndBuffer)
}

struct ChunkAndBuffer {
    buffer: ~[u8],
    ll_chunk: ll::Mix_Chunk
}

unsafe fn check_if_not_playing(ll_chunk_addr: *ll::Mix_Chunk) {
    // Verify that the chunk is not currently playing.
    //
    // TODO: I can't prove to myself that this is not racy, although I believe it is not
    // as long as all SDL calls are happening from the same thread. Somebody with better
    // knowledge of how SDL_mixer works internally should double check this, though.

    let mut frequency = 0;
    let mut format = 0;
    let mut channels = 0;
    if ll::Mix_QuerySpec(&mut frequency, &mut format, &mut channels) == 0 {
        channels = 0;
    }

    for ch in range(0, (channels as uint)) {
        if ll::Mix_GetChunk(ch as i32) == ll_chunk_addr {
            fail!(~"attempt to free a channel that's playing!")
        }
    }
}

impl Drop for Chunk {
    fn drop(&self) {
        unsafe {
            match self.data {
                Borrowed(_) => (),
                Allocated(ll_chunk) => {
                    check_if_not_playing(ll_chunk);
                    ll::Mix_FreeChunk(ll_chunk);
                },
                OwnedBuffer(ref chunk) => {
                    check_if_not_playing(&chunk.ll_chunk);
                }
            }
        }
    }
}

impl Chunk {
    pub fn new(buffer: ~[u8], volume: u8) -> ~Chunk {
        unsafe {
            let buffer_addr: *u8 = transmute(&buffer[0]);
            let buffer_len = buffer.len() as u32;
            ~Chunk {
                data: OwnedBuffer(
                    ChunkAndBuffer {
                        buffer: buffer,
                        ll_chunk: ll::Mix_Chunk {
                            allocated: 0,
                            abuf: buffer_addr,
                            alen: buffer_len,
                            volume: volume
                        }
                    }
                )
            }
        }
    }

    pub fn from_wav(path: &Path) -> Result<~Chunk, ~str> {
        let raw = unsafe {
            ll::Mix_LoadWAV_RW(SDL_RWFromFile(path.to_c_str().unwrap(), "rb".to_c_str().unwrap()), 1)
        };

        if raw.is_null() { Err(get_error()) }
        else { Ok(~Chunk { data: Allocated(raw) }) }
    }

    pub fn to_ll_chunk(&self) -> *ll::Mix_Chunk {
        match self.data {
            Borrowed(ll_chunk) => ll_chunk,
            Allocated(ll_chunk) => ll_chunk,
            OwnedBuffer(ref chunk) => {
                let ll_chunk: *ll::Mix_Chunk = &chunk.ll_chunk; ll_chunk
            }
        }
    }

    pub fn volume(&self) -> u8 {
        let ll_chunk: *ll::Mix_Chunk = self.to_ll_chunk();
        unsafe { (*ll_chunk).volume }
    }

    pub fn play_timed(&self, channel: Option<c_int>, loops: c_int, ticks: c_int) -> c_int {
        unsafe {
            let ll_channel = match channel {
                None => -1,
                Some(channel) => channel,
            };
            ll::Mix_PlayChannelTimed(ll_channel, self.to_ll_chunk(), loops, ticks)
        }
    }

    pub fn play(&self, channel: Option<c_int>, loops: c_int) -> c_int {
        self.play_timed(channel, loops, -1)
    }
}

pub fn open(frequency: c_int, format: AudioFormat, channels: Channels, chunksize: c_int)
         -> Result<(),()> {
    unsafe {
        if ll::Mix_OpenAudio(frequency, format.to_ll_format(), channels.count(), chunksize) == 0 {
            Ok(())
        } else {
            Err(())
        }
    }
}

pub fn close() {
    unsafe {
        ll::Mix_CloseAudio()
    }
}

struct Query {
    frequency: c_int,
    format: AudioFormat,
    channels: Channels,
}

pub fn query() -> Option<Query> {
    unsafe {
        let mut frequency = 0;
        let mut ll_format = 0;
        let mut ll_channels = 0;
        if ll::Mix_QuerySpec(&mut frequency, &mut ll_format, &mut ll_channels) == 0 {
            return None;
        }
        Some(Query {
            frequency: frequency,
            format: AudioFormat::from_ll_format(ll_format),
            channels: if ll_channels == 1 { Mono } else { Stereo }
        })
    }
}

pub fn allocate_channels(numchans: c_int) -> c_int {
    unsafe {
        ll::Mix_AllocateChannels(numchans)
    }
}

pub fn playing(channel: Option<c_int>) -> bool {
    unsafe {
        match channel {
            Some(channel) => ll::Mix_Playing(channel) as bool,
            None => ll::Mix_Playing(-1) as bool
        }
    }
}

pub fn halt_channel(channel: c_int) -> c_int {
    unsafe {
        ll::Mix_HaltChannel(channel)
    }
}

<commit_msg>Use `== 0` instead of `as bool`<commit_after>use std::cast::transmute;
use std::libc::{c_int};
use std::uint;

use audio::{AudioFormat, Channels, Mono, Stereo};
use video::ll::SDL_RWFromFile; // XXX refactoring
use get_error;

// Setup linking for all targets.
#[cfg(target_os="macos")]
mod mac {
    #[cfg(mac_framework)]
    #[link_args="-framework SDL_mixer"]
    extern {}

    #[cfg(mac_dylib)]
    #[link_args="-lSDL_mixer"]
    extern {}
}

#[cfg(not(target_os="macos"))]
mod others {
    #[link_args="-lSDL_mixer"]
    extern {}
}

pub mod ll {
    use video::ll::SDL_RWops; // XXX refactoring

    use std::libc::c_int;

    pub struct Mix_Chunk {
        allocated: c_int,
        abuf: *u8,
        alen: u32,
        volume: u8,
    }

    externfn!(fn Mix_OpenAudio(frequency: c_int, format: u16, channels: c_int, chunksize: c_int)
                    -> c_int)
    externfn!(fn Mix_QuerySpec(frequency: *mut c_int, format: *mut u16, channels: *mut c_int)
                    -> c_int)
    externfn!(fn Mix_LoadWAV_RW(src: *SDL_RWops, freesrc: c_int) -> *Mix_Chunk)
    externfn!(fn Mix_FreeChunk(chunk: *Mix_Chunk))
    externfn!(fn Mix_AllocateChannels(numchans: c_int) -> c_int)
    externfn!(fn Mix_Playing(channel: c_int) -> c_int)
    externfn!(fn Mix_PlayChannelTimed(channel: c_int, chunk: *Mix_Chunk, loops: c_int, ticks: c_int)
                    -> c_int)
    externfn!(fn Mix_GetChunk(channel: c_int) -> *Mix_Chunk)
    externfn!(fn Mix_CloseAudio())
    externfn!(fn Mix_HaltChannel(channel: c_int) -> c_int)
}

pub struct Chunk {
    priv data: ChunkData
}

enum ChunkData {
    Borrowed(*ll::Mix_Chunk),
    Allocated(*ll::Mix_Chunk),
    OwnedBuffer(ChunkAndBuffer)
}

struct ChunkAndBuffer {
    buffer: ~[u8],
    ll_chunk: ll::Mix_Chunk
}

unsafe fn check_if_not_playing(ll_chunk_addr: *ll::Mix_Chunk) {
    // Verify that the chunk is not currently playing.
    //
    // TODO: I can't prove to myself that this is not racy, although I believe it is not
    // as long as all SDL calls are happening from the same thread. Somebody with better
    // knowledge of how SDL_mixer works internally should double check this, though.

    let mut frequency = 0;
    let mut format = 0;
    let mut channels = 0;
    if ll::Mix_QuerySpec(&mut frequency, &mut format, &mut channels) == 0 {
        channels = 0;
    }

    for ch in range(0, (channels as uint)) {
        if ll::Mix_GetChunk(ch as i32) == ll_chunk_addr {
            fail!(~"attempt to free a channel that's playing!")
        }
    }
}

impl Drop for Chunk {
    fn drop(&self) {
        unsafe {
            match self.data {
                Borrowed(_) => (),
                Allocated(ll_chunk) => {
                    check_if_not_playing(ll_chunk);
                    ll::Mix_FreeChunk(ll_chunk);
                },
                OwnedBuffer(ref chunk) => {
                    check_if_not_playing(&chunk.ll_chunk);
                }
            }
        }
    }
}

impl Chunk {
    pub fn new(buffer: ~[u8], volume: u8) -> ~Chunk {
        unsafe {
            let buffer_addr: *u8 = transmute(&buffer[0]);
            let buffer_len = buffer.len() as u32;
            ~Chunk {
                data: OwnedBuffer(
                    ChunkAndBuffer {
                        buffer: buffer,
                        ll_chunk: ll::Mix_Chunk {
                            allocated: 0,
                            abuf: buffer_addr,
                            alen: buffer_len,
                            volume: volume
                        }
                    }
                )
            }
        }
    }

    pub fn from_wav(path: &Path) -> Result<~Chunk, ~str> {
        let raw = unsafe {
            ll::Mix_LoadWAV_RW(SDL_RWFromFile(path.to_c_str().unwrap(), "rb".to_c_str().unwrap()), 1)
        };

        if raw.is_null() { Err(get_error()) }
        else { Ok(~Chunk { data: Allocated(raw) }) }
    }

    pub fn to_ll_chunk(&self) -> *ll::Mix_Chunk {
        match self.data {
            Borrowed(ll_chunk) => ll_chunk,
            Allocated(ll_chunk) => ll_chunk,
            OwnedBuffer(ref chunk) => {
                let ll_chunk: *ll::Mix_Chunk = &chunk.ll_chunk; ll_chunk
            }
        }
    }

    pub fn volume(&self) -> u8 {
        let ll_chunk: *ll::Mix_Chunk = self.to_ll_chunk();
        unsafe { (*ll_chunk).volume }
    }

    pub fn play_timed(&self, channel: Option<c_int>, loops: c_int, ticks: c_int) -> c_int {
        unsafe {
            let ll_channel = match channel {
                None => -1,
                Some(channel) => channel,
            };
            ll::Mix_PlayChannelTimed(ll_channel, self.to_ll_chunk(), loops, ticks)
        }
    }

    pub fn play(&self, channel: Option<c_int>, loops: c_int) -> c_int {
        self.play_timed(channel, loops, -1)
    }
}

pub fn open(frequency: c_int, format: AudioFormat, channels: Channels, chunksize: c_int)
         -> Result<(),()> {
    unsafe {
        if ll::Mix_OpenAudio(frequency, format.to_ll_format(), channels.count(), chunksize) == 0 {
            Ok(())
        } else {
            Err(())
        }
    }
}

pub fn close() {
    unsafe {
        ll::Mix_CloseAudio()
    }
}

struct Query {
    frequency: c_int,
    format: AudioFormat,
    channels: Channels,
}

pub fn query() -> Option<Query> {
    unsafe {
        let mut frequency = 0;
        let mut ll_format = 0;
        let mut ll_channels = 0;
        if ll::Mix_QuerySpec(&mut frequency, &mut ll_format, &mut ll_channels) == 0 {
            return None;
        }
        Some(Query {
            frequency: frequency,
            format: AudioFormat::from_ll_format(ll_format),
            channels: if ll_channels == 1 { Mono } else { Stereo }
        })
    }
}

pub fn allocate_channels(numchans: c_int) -> c_int {
    unsafe {
        ll::Mix_AllocateChannels(numchans)
    }
}

pub fn playing(channel: Option<c_int>) -> bool {
    unsafe {
        match channel {
            Some(channel) => ll::Mix_Playing(channel) == 0,
            None => ll::Mix_Playing(-1) == 0
        }
    }
}

pub fn halt_channel(channel: c_int) -> c_int {
    unsafe {
        ll::Mix_HaltChannel(channel)
    }
}

<|endoftext|>


<commit_before>// TODO: Make this non public
pub mod file_naming;

use std::collections::HashMap;
use self::file_naming::{FileName, FileType, FileNameParser};

pub struct BackupSet {
    pub file_type : FileType,
    pub time : String,
    pub start_time : String,
    pub end_time : String,
    pub compressed : bool,
    pub encrypted : bool,
    pub partial : bool,
    pub manifest_path : String,
    volumes_paths : HashMap<i32, String>,
    info_set : bool,    // true if informations are set
}

impl BackupSet {
    pub fn new() -> Self {
        BackupSet{
            file_type : FileType::Full,
            time : String::new(),
            start_time : String::new(),
            end_time : String::new(),
            compressed : false,
            encrypted : false,
            partial : false,
            manifest_path : String::new(),
            volumes_paths : HashMap::new(),
            info_set : false
        }
    }

    /// Add a filename to given set. Return true if it fits.
    ///
    /// The filename will match the given set if it has the right
    /// times and is of the right type. The information will be set
    /// from the first filename given.
    pub fn add_filename(&mut self, fname : &str, pr : &FileName) -> bool {
        if !self.info_set {
            self.set_info(pr);
        }
        else {
            // check if the file is from the same backup set
            if self.file_type != pr.file_type ||
                self.time != pr.time ||
                self.start_time != pr.start_time ||
                self.end_time != pr.end_time {
                    return false;
            }
            else {
                // fix encrypted flag
                if self.encrypted != pr.encrypted &&
                    self.partial && pr.encrypted {
                        self.encrypted = pr.encrypted;
                    }
            }
        }
        // set manifest or volume number
        if pr.manifest {
            self.manifest_path = fname.to_owned();
        }
        else {
            self.volumes_paths.insert(pr.volume_number, fname.to_owned());
        }
        true
    }

    pub fn is_complete(&self) -> bool {
        !self.manifest_path.is_empty()
    }

    pub fn get_time(&self) -> &str {
        if self.time.is_empty() {
            self.end_time.as_ref()
        }
        else {
            self.time.as_ref()
        }
    }

    fn set_info(&mut self, fname : &FileName) {
        self.file_type = fname.file_type;
        self.time = fname.time.clone();
        self.start_time = fname.start_time.clone();
        self.end_time = fname.end_time.clone();
        self.compressed = fname.compressed;
        self.encrypted = fname.encrypted;
        self.partial = fname.partial;
        self.info_set = true;
    }
}

pub struct BackupChain {
    fullset : BackupSet,
    incset_list : Vec<BackupSet>,
    start_time : String,
    end_time : String
}

impl BackupChain {
    /// Create a new BackupChain starting from a full backup set.
    pub fn new(fullset : BackupSet) -> Self {
        assert_eq!(fullset.file_type, FileType::Full);
        let time = fullset.time.clone();

        BackupChain{
            fullset : fullset,
            incset_list : Vec::new(),
            start_time : time.clone(),
            end_time : time
        }
    }

    /// Adds the given incremental backkup element to the backup chain if possible,
    /// returns it back otherwise.
    pub fn add_inc(&mut self, incset : BackupSet) -> Option<BackupSet> {
        if self.end_time == incset.start_time {
            self.end_time = incset.time.clone();
            self.incset_list.push(incset);
            None
        }
        else {
            // replace the last element if the end time comes before
            let replace_last = self.incset_list.last().map_or(false,
                |last| incset.start_time == last.start_time && incset.end_time > last.end_time);
            if replace_last {
                self.end_time = incset.time.clone();
                self.incset_list.pop();
                self.incset_list.push(incset);
                None
            }
            else {
                // ignore the given incremental backup set
                Some(incset)
            }
        }
    }
}

pub type FileNameList = Vec<String>;
type BackupSetList = Vec<BackupSet>;
type BackupChains = Vec<BackupChain>;

pub struct CollectionsStatus {
    backup_chains : BackupChains,
}

impl CollectionsStatus {
    pub fn new() -> Self {
        CollectionsStatus {
            backup_chains : Vec::new()
        }
    }

    pub fn compute_backup_chains(&mut self, filename_list : &FileNameList) {
        let mut sets = self.compute_backup_sets(filename_list);
        self.sort_backup_sets(&mut sets);
        let mut chains = self.add_to_chains(sets);
        self.get_sorted_chains(&mut chains);
        self.backup_chains = chains;
    }

    fn compute_backup_sets(&self, filename_list : &FileNameList) -> BackupSetList {
        let mut sets = BackupSetList::new();
        let parser = FileNameParser::new();
        for filename in filename_list.iter() {
            if let Some(filename_info) = parser.parse(filename.as_ref()) {
                let mut inserted = false;
                for set in sets.iter_mut() {
                    if set.add_filename(filename.as_ref(), &filename_info) {
                        inserted = true;
                        break;
                    }
                }
                if !inserted {
                    let mut new_set = BackupSet::new();
                    new_set.add_filename(filename.as_ref(), &filename_info);
                    sets.push(new_set);
                }
            }
        }
        sets
    }

    fn sort_backup_sets(&self, set_list : &mut BackupSetList) {
        set_list.sort_by(|a, b| a.get_time().cmp(b.get_time()));
    }

    fn add_to_chains(&self, set_list : BackupSetList) -> BackupChains {
        let mut chains = BackupChains::new();
        for set in set_list.into_iter() {
            match set.file_type {
                FileType::Full => {
                    let new_chain = BackupChain::new(set);
                    chains.push(new_chain);
                }
                FileType::Inc => {
                    let mut rejected_set = Some(set);
                    for chain in chains.iter_mut() {
                        rejected_set = chain.add_inc(rejected_set.unwrap());
                        if rejected_set.is_none() {
                            break;
                        }
                    }
                    if let Some(_) = rejected_set {
                        // TODO: add to orphaned sets
                    }
                }
                _ => { continue; }
            }
        }
        chains
    }

    fn get_sorted_chains(&self, chains : &mut BackupChains) {
        chains.sort_by(|a, b| a.end_time.cmp(&b.end_time));
    }
}

#[cfg(test)]
mod test {
    use super::BackupSet;
    use super::file_naming::{FileType, FileNameParser};

    #[test]
    fn parse_and_add() {
        let full1_name = "duplicity-full.20150617T182545Z.vol1.difftar.gz";
        let manifest1_name = "duplicity-full.20150617T182545Z.manifest";
        let inc1_name = "duplicity-inc.20150617T182629Z.to.20150617T182650Z.vol1.difftar.gz";

        let parser = FileNameParser::new();
        let full1 = parser.parse(full1_name).unwrap();
        let manifest1 = parser.parse(manifest1_name).unwrap();
        let inc1 = parser.parse(inc1_name).unwrap();

        let mut set = BackupSet::new();
        // add to set
        assert!(set.add_filename(full1_name, &full1));
        assert!(set.add_filename(manifest1_name, &manifest1));
        assert!(!set.add_filename(inc1_name, &inc1));
        // test results
        assert_eq!(set.file_type, FileType::Full);
        assert_eq!(set.time, "20150617t182545z");
        assert_eq!(set.start_time, "");
        assert_eq!(set.end_time, "");
        assert!(set.compressed);
        assert!(!set.encrypted);
        assert!(!set.partial);
        assert_eq!(set.manifest_path, manifest1_name);
    }
}
<commit_msg>Improved style of CollectionStatus<commit_after>// TODO: Make this non public
pub mod file_naming;

use std::collections::HashMap;
use self::file_naming::{FileName, FileType, FileNameParser};

pub struct BackupSet {
    pub file_type : FileType,
    pub time : String,
    pub start_time : String,
    pub end_time : String,
    pub compressed : bool,
    pub encrypted : bool,
    pub partial : bool,
    pub manifest_path : String,
    volumes_paths : HashMap<i32, String>,
    info_set : bool,    // true if informations are set
}

impl BackupSet {
    pub fn new() -> Self {
        BackupSet{
            file_type : FileType::Full,
            time : String::new(),
            start_time : String::new(),
            end_time : String::new(),
            compressed : false,
            encrypted : false,
            partial : false,
            manifest_path : String::new(),
            volumes_paths : HashMap::new(),
            info_set : false
        }
    }

    /// Add a filename to given set. Return true if it fits.
    ///
    /// The filename will match the given set if it has the right
    /// times and is of the right type. The information will be set
    /// from the first filename given.
    pub fn add_filename(&mut self, fname : &str, pr : &FileName) -> bool {
        if !self.info_set {
            self.set_info(pr);
        }
        else {
            // check if the file is from the same backup set
            if self.file_type != pr.file_type ||
                self.time != pr.time ||
                self.start_time != pr.start_time ||
                self.end_time != pr.end_time {
                    return false;
            }
            else {
                // fix encrypted flag
                if self.encrypted != pr.encrypted &&
                    self.partial && pr.encrypted {
                        self.encrypted = pr.encrypted;
                    }
            }
        }
        // set manifest or volume number
        if pr.manifest {
            self.manifest_path = fname.to_owned();
        }
        else {
            self.volumes_paths.insert(pr.volume_number, fname.to_owned());
        }
        true
    }

    pub fn is_complete(&self) -> bool {
        !self.manifest_path.is_empty()
    }

    pub fn get_time(&self) -> &str {
        if self.time.is_empty() {
            self.end_time.as_ref()
        }
        else {
            self.time.as_ref()
        }
    }

    fn set_info(&mut self, fname : &FileName) {
        self.file_type = fname.file_type;
        self.time = fname.time.clone();
        self.start_time = fname.start_time.clone();
        self.end_time = fname.end_time.clone();
        self.compressed = fname.compressed;
        self.encrypted = fname.encrypted;
        self.partial = fname.partial;
        self.info_set = true;
    }
}

pub struct BackupChain {
    fullset : BackupSet,
    incset_list : Vec<BackupSet>,
    start_time : String,
    end_time : String
}

impl BackupChain {
    /// Create a new BackupChain starting from a full backup set.
    pub fn new(fullset : BackupSet) -> Self {
        assert_eq!(fullset.file_type, FileType::Full);
        let time = fullset.time.clone();

        BackupChain{
            fullset : fullset,
            incset_list : Vec::new(),
            start_time : time.clone(),
            end_time : time
        }
    }

    /// Adds the given incremental backkup element to the backup chain if possible,
    /// returns it back otherwise.
    pub fn add_inc(&mut self, incset : BackupSet) -> Option<BackupSet> {
        if self.end_time == incset.start_time {
            self.end_time = incset.time.clone();
            self.incset_list.push(incset);
            None
        }
        else {
            // replace the last element if the end time comes before
            let replace_last = self.incset_list.last().map_or(false,
                |last| incset.start_time == last.start_time && incset.end_time > last.end_time);
            if replace_last {
                self.end_time = incset.time.clone();
                self.incset_list.pop();
                self.incset_list.push(incset);
                None
            }
            else {
                // ignore the given incremental backup set
                Some(incset)
            }
        }
    }
}

pub type FileNameList = Vec<String>;
pub type BackupChains = Vec<BackupChain>;
type BackupSetList = Vec<BackupSet>;

pub struct CollectionsStatus {
    pub backup_chains : BackupChains,
}

impl CollectionsStatus {
    pub fn new() -> Self {
        CollectionsStatus {
            backup_chains : Vec::new()
        }
    }

    pub fn from_filename_list(filename_list : &FileNameList) -> Self {
        let mut result = Self::new();
        result.compute_backup_chains(filename_list);
        result
    }

    fn compute_backup_chains(&mut self, filename_list : &FileNameList) {
        let mut sets = self.compute_backup_sets(filename_list);
        self.sort_backup_sets(&mut sets);
        self.add_to_chains(sets);
        self.sort_chains();
    }

    fn compute_backup_sets(&self, filename_list : &FileNameList) -> BackupSetList {
        let mut sets = BackupSetList::new();
        let parser = FileNameParser::new();
        for filename in filename_list.iter() {
            if let Some(filename_info) = parser.parse(filename.as_ref()) {
                let mut inserted = false;
                for set in sets.iter_mut() {
                    if set.add_filename(filename.as_ref(), &filename_info) {
                        inserted = true;
                        break;
                    }
                }
                if !inserted {
                    let mut new_set = BackupSet::new();
                    new_set.add_filename(filename.as_ref(), &filename_info);
                    sets.push(new_set);
                }
            }
        }
        sets
    }

    fn sort_backup_sets(&self, set_list : &mut BackupSetList) {
        set_list.sort_by(|a, b| a.get_time().cmp(b.get_time()));
    }

    fn add_to_chains(&mut self, set_list : BackupSetList) {
        for set in set_list.into_iter() {
            match set.file_type {
                FileType::Full => {
                    let new_chain = BackupChain::new(set);
                    self.backup_chains.push(new_chain);
                }
                FileType::Inc => {
                    let mut rejected_set = Some(set);
                    for chain in self.backup_chains.iter_mut() {
                        rejected_set = chain.add_inc(rejected_set.unwrap());
                        if rejected_set.is_none() {
                            break;
                        }
                    }
                    if let Some(_) = rejected_set {
                        // TODO: add to orphaned sets
                    }
                }
                _ => { continue; }
            }
        }
    }

    fn sort_chains(&mut self) {
        self.backup_chains.sort_by(|a, b| a.end_time.cmp(&b.end_time));
    }
}

#[cfg(test)]
mod test {
    use super::BackupSet;
    use super::file_naming::{FileType, FileNameParser};

    #[test]
    fn parse_and_add() {
        let full1_name = "duplicity-full.20150617T182545Z.vol1.difftar.gz";
        let manifest1_name = "duplicity-full.20150617T182545Z.manifest";
        let inc1_name = "duplicity-inc.20150617T182629Z.to.20150617T182650Z.vol1.difftar.gz";

        let parser = FileNameParser::new();
        let full1 = parser.parse(full1_name).unwrap();
        let manifest1 = parser.parse(manifest1_name).unwrap();
        let inc1 = parser.parse(inc1_name).unwrap();

        let mut set = BackupSet::new();
        // add to set
        assert!(set.add_filename(full1_name, &full1));
        assert!(set.add_filename(manifest1_name, &manifest1));
        assert!(!set.add_filename(inc1_name, &inc1));
        // test results
        assert_eq!(set.file_type, FileType::Full);
        assert_eq!(set.time, "20150617t182545z");
        assert_eq!(set.start_time, "");
        assert_eq!(set.end_time, "");
        assert!(set.compressed);
        assert!(!set.encrypted);
        assert!(!set.partial);
        assert_eq!(set.manifest_path, manifest1_name);
    }
}
<|endoftext|>


<commit_before>//! Read and write NDArrays as Numpy arrays

use ndarray::prelude::*;
use ndarray as nd;
use std::fs::{OpenOption, File};
use std::path::Path;
use std::ptr;
use std::str;
use std::io::{Read, Write};
use regex::bytes::Regex;
use byteorder::{LittleEndian, BigEndian, ReadBytesExt, WriteBytesExt};
use errors::*;
use memmap::{Mmap, Protection};

/// Write an array as a numpy array, fast but only works for native byte order
pub fn write_matrix<S, P>(path: P, arr: &ArrayBase<S, Ix2>) -> Result<()>
    where S: nd::Data<Elem=f64>, P: AsRef<Path> {
    let header_nospace = format!("{{'descr': '<f8', 'fortran_order': False, 'shape': ({},{})}}",
        arr.shape()[0], arr.shape()[1]);
    let virtual_len =
        // Calculating how many bytes we have in the header, so we can get alignment
        header_nospace.len()
        + 6 // The magic string
        + 2 // The version number
        + 2 // An unsigned 2-byte integer for header length
        + 1 ; // Because there will be a \n added
    let padding_needed = (((virtual_len + 15) / 16) * 16) - virtual_len; // to get to the next 16

    // Numpy version 1.0
    let mut writer = File::create(path)?;
    writer.write_all(b"\x93NUMPY\x01\x00")?;
    writer.write_u16::<LittleEndian>((header_nospace.len()
        + padding_needed
        + 1) // newline
        // Magic string, version number and 2 bytes for the header length number not included.
        as u16
        )?;
    write!(writer, "{}{}\n", header_nospace, " ".repeat(padding_needed))?;
    unsafe {
        // O(N) array copy without memory overhead.
        assert!(arr.is_standard_layout(), "To write an array it needs to be in C order (for now).");
        // Treat the incoming data as floating point
        writer.write_all(::std::slice::from_raw_parts(arr.as_ptr() as *const u8, arr.len()*8))?;
    };
    Ok(())
}

/// Read a Numpy matrix into memory. Be careful if it's large. You could run out of memory.
///
/// You need to know the number of dimensions at compile time so for convenience, we assume you
/// need a matrix. Also, this method is fast but only works for native byte order.
pub fn read_matrix<P: AsRef<Path>>(path: P) -> Result<Array2<f64>> {
    let header_match = Regex::new(r"NUMPY\x01\x00(?s:..)\{'descr': ?'<f8', ?'fortran_order': ?False, ?'shape': ?\((\d+), ?(\d+)\)\} *\n").unwrap();
    let mut reader = File::open(path.as_ref())?;
    let mut content = vec![];
    reader.read_to_end(&mut content)?;
    // The skip and the nested context here is so that we can regex parse (with a borrow)
    // and then reuse the buffer as the array. This way there are never two copies in memory.
    let res: Result<(usize, usize, usize)> = {
        let captures = header_match.captures(&content)
            .ok_or(helpful_complaint(path.as_ref(), &content))?;
        Ok((// where the full match ends
            captures.get(0).unwrap().end(),
            // The shape of the array as described in the metadata
            str::from_utf8(&captures[1]).unwrap().parse().unwrap(),
            str::from_utf8(&captures[2]).unwrap().parse().unwrap()
        ))
    };
    let (skip, h, w) = res?;
    let content_f64 = unsafe {
        // O(N) array copy without memory overhead.
        let length = content.len() - skip;
        assert_eq!(length, h*w*8,
            "The numpy file's array is the wrong length for a {}x{} array. \
            It should be {} elements, ({} bytes), but it is actually {} bytes.",
            h, w, h*w, h*w*8, length
        );
        ptr::copy((&content[skip..]).as_ptr(),
                  (&mut content[..length]).as_mut_ptr(),
                  length);
        content.set_len(length);
        // Treat the incoming data as floating point
        let mut out : Vec<f64> = ::std::mem::transmute(content);
        out.set_len(length/8);
        out
    };
    Ok(ArrayBase::from_shape_vec([h, w], content_f64)?)
}

pub struct MatFile(usize, usize, Mmap);

/// Load a Numpy matrix as an mmap. This only consumes address space. (Part 1)
///
/// This is a two-step process because the Mmap needs to outlive the matrix.
pub fn open_matrix_mmap<P: AsRef<Path>>(path: P) -> Result<MatFile> {
    let header_match = Regex::new(r"NUMPY\x01\x00(?s:..)\{'descr': ?'<f8', ?'fortran_order': ?False, ?'shape': ?\((\d+), ?(\d+)\)\} *\n").unwrap();
    let mut reader = OpenOption::new()
        .read(true)
        .write(true)
        .create(true)
        .open(path.as_ref())?;
    let mut content = [0u8; 128];
    let bytes_read = reader.read(&mut content)?;
    assert!(bytes_read > 0, format!("The numpy file {} seems to be empty.", path.as_ref().display()));
    // The skip and the nested context here is so that we can regex parse (with a borrow)
    // and then reuse the buffer as the array. This way there are never two copies in memory.
    let captures = header_match.captures(&content)
        .ok_or(helpful_complaint(path.as_ref(), &content))?;
    // where the full match ends
    let skip = captures.get(0).unwrap().end();
    // The shape of the array as described in the metadata
    let h = str::from_utf8(&captures[1]).unwrap().parse().unwrap();
    let w = str::from_utf8(&captures[2]).unwrap().parse().unwrap();

    Ok(MatFile(h, w, Mmap::open_with_offset(&reader, Protection::ReadWrite, skip, h*w*8)?))
}

/// Load a Numpy matrix as an mmap (Part 2)
///
/// You need to know the number of dimensions at compile time so for convenience, we assume you
/// need a matrix. Also, this method is even faster than read_matrix but only works for native byte
/// order.
///
/// This is different than you might expect. The file you read from must outlive the array,
/// because the array is based on an mmap of that file (so that it doesn't read it into memory).
pub fn read_matrix_mmap<'t>(mmap: &'t MatFile) -> Result<ArrayViewMut2<'t, f64>> {
    unsafe {
        let new_slice= ::std::slice::from_raw_parts_mut(mmap.2.ptr() as *mut f64, mmap.2.len()/8);
        Ok(ArrayViewMut2::from_shape([mmap.0, mmap.1], new_slice)?)
    }
}

/// Tell the user more info about the file
///
/// It seems verbose but you can see this error often so it save you time.
fn helpful_complaint(p: &Path, header: &[u8]) -> Error {
    let cap = ::std::cmp::min(header.len(), 100);
    let complaint = format!(
        "Expected {} to be an uncompressed numpy (.npy) file, but couldn't \
        parse the header. The first hundred bytes look like:

        {}

        As bytes, the header is as follows:

        {:?}

        It should look something like this example, where . are non-printable characters: \
        NUMPY..{{'descr': '<f8', 'fortran_order': False, 'shape': (34, 27)}}\
        Note: Cabarrus only supports 2D big-endian 64-bit float matrices in C order (for \
        simplicity). You may need to change the dtype accordingly.",
        p.display(),
        String::from_utf8_lossy(&header[..cap]),
        &header[..cap]);
    Error::Other(complaint)
}<commit_msg>Mismatched File and Mmap permissions - typo<commit_after>//! Read and write NDArrays as Numpy arrays

use ndarray::prelude::*;
use ndarray as nd;
use std::fs::{OpenOptions, File};
use std::path::Path;
use std::ptr;
use std::str;
use std::io::{Read, Write};
use regex::bytes::Regex;
use byteorder::{LittleEndian, BigEndian, ReadBytesExt, WriteBytesExt};
use errors::*;
use memmap::{Mmap, Protection};

/// Write an array as a numpy array, fast but only works for native byte order
pub fn write_matrix<S, P>(path: P, arr: &ArrayBase<S, Ix2>) -> Result<()>
    where S: nd::Data<Elem=f64>, P: AsRef<Path> {
    let header_nospace = format!("{{'descr': '<f8', 'fortran_order': False, 'shape': ({},{})}}",
        arr.shape()[0], arr.shape()[1]);
    let virtual_len =
        // Calculating how many bytes we have in the header, so we can get alignment
        header_nospace.len()
        + 6 // The magic string
        + 2 // The version number
        + 2 // An unsigned 2-byte integer for header length
        + 1 ; // Because there will be a \n added
    let padding_needed = (((virtual_len + 15) / 16) * 16) - virtual_len; // to get to the next 16

    // Numpy version 1.0
    let mut writer = File::create(path)?;
    writer.write_all(b"\x93NUMPY\x01\x00")?;
    writer.write_u16::<LittleEndian>((header_nospace.len()
        + padding_needed
        + 1) // newline
        // Magic string, version number and 2 bytes for the header length number not included.
        as u16
        )?;
    write!(writer, "{}{}\n", header_nospace, " ".repeat(padding_needed))?;
    unsafe {
        // O(N) array copy without memory overhead.
        assert!(arr.is_standard_layout(), "To write an array it needs to be in C order (for now).");
        // Treat the incoming data as floating point
        writer.write_all(::std::slice::from_raw_parts(arr.as_ptr() as *const u8, arr.len()*8))?;
    };
    Ok(())
}

/// Read a Numpy matrix into memory. Be careful if it's large. You could run out of memory.
///
/// You need to know the number of dimensions at compile time so for convenience, we assume you
/// need a matrix. Also, this method is fast but only works for native byte order.
pub fn read_matrix<P: AsRef<Path>>(path: P) -> Result<Array2<f64>> {
    let header_match = Regex::new(r"NUMPY\x01\x00(?s:..)\{'descr': ?'<f8', ?'fortran_order': ?False, ?'shape': ?\((\d+), ?(\d+)\)\} *\n").unwrap();
    let mut reader = File::open(path.as_ref())?;
    let mut content = vec![];
    reader.read_to_end(&mut content)?;
    // The skip and the nested context here is so that we can regex parse (with a borrow)
    // and then reuse the buffer as the array. This way there are never two copies in memory.
    let res: Result<(usize, usize, usize)> = {
        let captures = header_match.captures(&content)
            .ok_or(helpful_complaint(path.as_ref(), &content))?;
        Ok((// where the full match ends
            captures.get(0).unwrap().end(),
            // The shape of the array as described in the metadata
            str::from_utf8(&captures[1]).unwrap().parse().unwrap(),
            str::from_utf8(&captures[2]).unwrap().parse().unwrap()
        ))
    };
    let (skip, h, w) = res?;
    let content_f64 = unsafe {
        // O(N) array copy without memory overhead.
        let length = content.len() - skip;
        assert_eq!(length, h*w*8,
            "The numpy file's array is the wrong length for a {}x{} array. \
            It should be {} elements, ({} bytes), but it is actually {} bytes.",
            h, w, h*w, h*w*8, length
        );
        ptr::copy((&content[skip..]).as_ptr(),
                  (&mut content[..length]).as_mut_ptr(),
                  length);
        content.set_len(length);
        // Treat the incoming data as floating point
        let mut out : Vec<f64> = ::std::mem::transmute(content);
        out.set_len(length/8);
        out
    };
    Ok(ArrayBase::from_shape_vec([h, w], content_f64)?)
}

pub struct MatFile(usize, usize, Mmap);

/// Load a Numpy matrix as an mmap. This only consumes address space. (Part 1)
///
/// This is a two-step process because the Mmap needs to outlive the matrix.
pub fn open_matrix_mmap<P: AsRef<Path>>(path: P) -> Result<MatFile> {
    let header_match = Regex::new(r"NUMPY\x01\x00(?s:..)\{'descr': ?'<f8', ?'fortran_order': ?False, ?'shape': ?\((\d+), ?(\d+)\)\} *\n").unwrap();
    let mut reader = OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(path.as_ref())?;
    let mut content = [0u8; 128];
    let bytes_read = reader.read(&mut content)?;
    assert!(bytes_read > 0, format!("The numpy file {} seems to be empty.", path.as_ref().display()));
    // The skip and the nested context here is so that we can regex parse (with a borrow)
    // and then reuse the buffer as the array. This way there are never two copies in memory.
    let captures = header_match.captures(&content)
        .ok_or(helpful_complaint(path.as_ref(), &content))?;
    // where the full match ends
    let skip = captures.get(0).unwrap().end();
    // The shape of the array as described in the metadata
    let h = str::from_utf8(&captures[1]).unwrap().parse().unwrap();
    let w = str::from_utf8(&captures[2]).unwrap().parse().unwrap();

    Ok(MatFile(h, w, Mmap::open_with_offset(&reader, Protection::ReadWrite, skip, h*w*8)?))
}

/// Load a Numpy matrix as an mmap (Part 2)
///
/// You need to know the number of dimensions at compile time so for convenience, we assume you
/// need a matrix. Also, this method is even faster than read_matrix but only works for native byte
/// order.
///
/// This is different than you might expect. The file you read from must outlive the array,
/// because the array is based on an mmap of that file (so that it doesn't read it into memory).
pub fn read_matrix_mmap<'t>(mmap: &'t MatFile) -> Result<ArrayViewMut2<'t, f64>> {
    unsafe {
        let new_slice= ::std::slice::from_raw_parts_mut(mmap.2.ptr() as *mut f64, mmap.2.len()/8);
        Ok(ArrayViewMut2::from_shape([mmap.0, mmap.1], new_slice)?)
    }
}

/// Tell the user more info about the file
///
/// It seems verbose but you can see this error often so it save you time.
fn helpful_complaint(p: &Path, header: &[u8]) -> Error {
    let cap = ::std::cmp::min(header.len(), 100);
    let complaint = format!(
        "Expected {} to be an uncompressed numpy (.npy) file, but couldn't \
        parse the header. The first hundred bytes look like:

        {}

        As bytes, the header is as follows:

        {:?}

        It should look something like this example, where . are non-printable characters: \
        NUMPY..{{'descr': '<f8', 'fortran_order': False, 'shape': (34, 27)}}\
        Note: Cabarrus only supports 2D big-endian 64-bit float matrices in C order (for \
        simplicity). You may need to change the dtype accordingly.",
        p.display(),
        String::from_utf8_lossy(&header[..cap]),
        &header[..cap]);
    Error::Other(complaint)
}<|endoftext|>


<commit_before>use std;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::io;

use tempdir::TempDir;

use pipeline::{CompileOptions, OutputType};
use ir;
use codegen;

fn get_writer(output_path: &Option<PathBuf>) -> io::Result<Box<std::io::Write>> {
    if let Some(ref output_path) = *output_path {
        Ok(Box::new(std::fs::File::create(output_path)?))
    } else {
        Ok(Box::new(std::io::stdout()))
    }
}

pub fn main_outer(tu: ir::TranslationUnit,
                  input_path: &str,
                  options: &CompileOptions)
                  -> io::Result<()> {
    match options.output_type {
        OutputType::Check => Ok(()),
        OutputType::LLVM => {
            let mut writer = get_writer(&options.output_path)?;
            codegen::llvm_gen::gen_translation_unit(&mut writer, tu)
        }
        OutputType::Run => {
            let tmp_dir = TempDir::new("elang-compiler")?;
            let exec_path = output_exec(tu, input_path, tmp_dir.path(), options)?;
            if !run_command(exec_path, &[])?.success() {
                panic!("exec fail");
            }
            Ok(())
        }
        OutputType::Exec => {
            let tmp_dir = TempDir::new("elang-compiler")?;
            let exec_path = output_exec(tu, input_path, tmp_dir.path(), options)?;
            let default_path = PathBuf::from("a.out");
            std::fs::copy(exec_path, options.output_path.as_ref().unwrap_or(&default_path))?;
            Ok(())
        }
    }
}

fn executable_name(path: &str) -> String {
    let file_name = Path::new(path).file_name().unwrap().to_str().unwrap();

    let mut name_parts: Vec<_> = file_name.split('.').collect();
    let parts_len = name_parts.len();
    if parts_len > 1 {
        name_parts.pop();
    }

    name_parts.join(".")
}

fn output_exec(tu: ir::TranslationUnit,
               input_path: &str,
               tmp_dir_path: &Path,
               options: &CompileOptions)
               -> io::Result<PathBuf> {
    fn path_to_str(path: &PathBuf) -> &str {
        path.to_str().unwrap()
    }

    let exec_name = executable_name(input_path);

    let llvm_path = tmp_dir_path.join(format!("{}.ll", exec_name));
    let obj_path = tmp_dir_path.join(format!("{}.o", exec_name));
    let exec_path = tmp_dir_path.join(exec_name);

    let mut llvm_file = File::create(&llvm_path)?;
    codegen::llvm_gen::gen_translation_unit(&mut llvm_file, tu).expect("error llvm gen");

    if options.opt &&
       !run_command("opt",
                    &["-O3", "-S", path_to_str(&llvm_path), "-o", path_to_str(&llvm_path)])
                ?
                .success() {
        panic!("opt fail");
    }

    if !run_command("llc",
                    &["-filetype=obj", path_to_str(&llvm_path), "-o", path_to_str(&obj_path)])
                ?
                .success() {
        panic!("llc fail");
    }

    if !run_command("clang",
                    &[path_to_str(&obj_path), "-o", path_to_str(&exec_path)])
                ?
                .success() {
        panic!("clang fail");
    }

    Ok(exec_path)
}

fn run_command<S: AsRef<std::ffi::OsStr>>(ex: S,
                                          args: &[&str])
                                          -> io::Result<std::process::ExitStatus> {
    std::process::Command::new(ex).args(args).status()
}
<commit_msg>Added opt in llvm output<commit_after>use std;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::io;

use tempdir::TempDir;

use pipeline::{CompileOptions, OutputType};
use ir;
use codegen;

fn get_writer(output_path: &Option<PathBuf>) -> io::Result<Box<std::io::Write>> {
    if let Some(ref output_path) = *output_path {
        Ok(Box::new(std::fs::File::create(output_path)?))
    } else {
        Ok(Box::new(std::io::stdout()))
    }
}

fn redirect_stream<R, W>(reader: &mut R, writer: &mut W) -> io::Result<()>
    where R: io::Read,
          W: io::Write
{
    let mut buffer = vec![0; 64 * 1024];

    loop {
        let len_read = try!(reader.read(&mut buffer));

        if len_read == 0 {
            return Ok(());
        }

        try!(writer.write_all(&buffer[..len_read]));
    }
}

pub fn main_outer(tu: ir::TranslationUnit,
                  input_path: &str,
                  options: &CompileOptions)
                  -> io::Result<()> {
    match options.output_type {
        OutputType::Check => Ok(()),
        OutputType::LLVM => {
            let tmp_dir = TempDir::new("elang-compiler")?;
            let llvm_path = output_llvm(tu, input_path, tmp_dir.path(), options)?;

            let mut reader = std::fs::File::open(llvm_path)?;
            let mut writer = get_writer(&options.output_path)?;
            redirect_stream(&mut reader, &mut writer)
        }
        OutputType::Run => {
            let tmp_dir = TempDir::new("elang-compiler")?;
            let exec_path = output_exec(tu, input_path, tmp_dir.path(), options)?;
            if !run_command(exec_path, &[])?.success() {
                panic!("exec fail");
            }
            Ok(())
        }
        OutputType::Exec => {
            let tmp_dir = TempDir::new("elang-compiler")?;
            let exec_path = output_exec(tu, input_path, tmp_dir.path(), options)?;
            let default_path = PathBuf::from("a.out");
            std::fs::copy(exec_path,
                          options.output_path.as_ref().unwrap_or(&default_path))?;
            Ok(())
        }
    }
}

fn executable_name(path: &str) -> String {
    let file_name = Path::new(path).file_name().unwrap().to_str().unwrap();

    let mut name_parts: Vec<_> = file_name.split('.').collect();
    let parts_len = name_parts.len();
    if parts_len > 1 {
        name_parts.pop();
    }

    name_parts.join(".")
}

fn path_to_str(path: &PathBuf) -> &str {
    path.to_str().unwrap()
}

fn output_llvm(tu: ir::TranslationUnit,
               input_path: &str,
               tmp_dir_path: &Path,
               options: &CompileOptions)
               -> io::Result<PathBuf> {
    let exec_name = executable_name(input_path);

    let llvm_path = tmp_dir_path.join(format!("{}.ll", exec_name));

    let mut llvm_file = File::create(&llvm_path)?;
    codegen::llvm_gen::gen_translation_unit(&mut llvm_file, tu).expect("error llvm gen");

    if options.opt &&
       !run_command("opt",
                    &["-O3", "-S", path_to_str(&llvm_path), "-o", path_to_str(&llvm_path)])
                ?
                .success() {
        panic!("opt fail");
    }

    Ok(llvm_path)
}

fn output_exec(tu: ir::TranslationUnit,
               input_path: &str,
               tmp_dir_path: &Path,
               options: &CompileOptions)
               -> io::Result<PathBuf> {
    let exec_name = executable_name(input_path);
    let llvm_path = output_llvm(tu, input_path, tmp_dir_path, options)?;
    let obj_path = tmp_dir_path.join(format!("{}.o", exec_name));
    let exec_path = tmp_dir_path.join(exec_name);

    if !run_command("llc",
                    &["-filetype=obj", path_to_str(&llvm_path), "-o", path_to_str(&obj_path)])
                ?
                .success() {
        panic!("llc fail");
    }

    if !run_command("clang",
                    &[path_to_str(&obj_path), "-o", path_to_str(&exec_path)])
                ?
                .success() {
        panic!("clang fail");
    }

    Ok(exec_path)
}

fn run_command<S: AsRef<std::ffi::OsStr>>(ex: S,
                                          args: &[&str])
                                          -> io::Result<std::process::ExitStatus> {
    std::process::Command::new(ex).args(args).status()
}
<|endoftext|>