<commit_before>use schema::Schema;
use schema::Document;
use indexer::SegmentSerializer;
use core::Index;
use core::SerializableSegment;
use core::Segment;
use std::thread::JoinHandle;
use indexer::SegmentWriter;
use std::clone::Clone;
use std::io;
use std::path::Path;
use std::thread;
use std::collections::HashSet;
use indexer::merger::IndexMerger;
use core::SegmentId;
use datastruct::stacker::Heap;
use std::mem::swap;
use chan;

use Result;
use Error;

// Size of the margin for the heap. A segment is closed when the remaining memory
// in the heap goes below MARGIN_IN_BYTES.
pub const MARGIN_IN_BYTES: u32 = 10_000_000u32;

// We impose the memory per thread to be at least 30 MB.
pub const HEAP_SIZE_LIMIT: u32 = MARGIN_IN_BYTES * 3u32;

// Add document will block if the number of docs waiting in the queue to be indexed reaches PIPELINE_MAX_SIZE_IN_DOCS
const PIPELINE_MAX_SIZE_IN_DOCS: usize = 10_000;

pub const LOCKFILE_NAME: &'static str = ".tantivy-indexer.lock";

type DocumentSender = chan::Sender<Document>;
type DocumentReceiver = chan::Receiver<Document>;

type NewSegmentSender = chan::Sender<Result<(SegmentId, usize)>>;
type NewSegmentReceiver = chan::Receiver<Result<(SegmentId, usize)>>;

/// `IndexWriter` is the user entry-point to add document to an index.
///
/// It manages a small number of indexing thread, as well as a shared
/// indexing queue.
/// Each indexing thread builds its own independant `Segment`, via
/// a `SegmentWriter` object.
pub struct IndexWriter {
    index: Index,
    heap_size_in_bytes_per_thread: usize,
    workers_join_handle: Vec<JoinHandle<()>>,
    segment_ready_sender: NewSegmentSender,
    segment_ready_receiver: NewSegmentReceiver,
    document_receiver: DocumentReceiver,
    document_sender: DocumentSender,
    num_threads: usize,
    docstamp: u64,
}

fn index_documents(heap: &mut Heap,
                   segment: Segment,
                   schema: &Schema,
                   document_iterator: &mut Iterator<Item = Document>)
                   -> Result<usize> {
    heap.clear();
    let mut segment_writer = try!(SegmentWriter::for_segment(heap, segment, &schema));
    for doc in document_iterator {
        try!(segment_writer.add_document(&doc, &schema));
        if segment_writer.is_buffer_full() {
            info!("Buffer limit reached, flushing segment with maxdoc={}.",
                  segment_writer.max_doc());
            break;
        }
    }
    let num_docs = segment_writer.max_doc() as usize;
    try!(segment_writer.finalize());
    Ok(num_docs)
}

impl Drop for IndexWriter {
    fn drop(&mut self) {
        let lockfile_path = Path::new(LOCKFILE_NAME);
        match self.index.directory_mut().delete(lockfile_path) {
            Ok(_) => (),
            Err(_) => (),
        }
    }
}
impl IndexWriter {
    /// Spawns a new worker thread for indexing.
    /// The thread consumes documents from the pipeline.
    ///
    fn add_indexing_worker(&mut self) -> Result<()> {
        let index = self.index.clone();
        let schema = self.index.schema();
        let segment_ready_sender_clone = self.segment_ready_sender.clone();
        let document_receiver_clone = self.document_receiver.clone();

        let mut heap = Heap::with_capacity(self.heap_size_in_bytes_per_thread);
        let join_handle: JoinHandle<()> = thread::spawn(move || {
            loop {
                let segment = index.new_segment();
                let segment_id = segment.id();
                let mut document_iterator = document_receiver_clone.clone()
                    .into_iter()
                    .peekable();
                // the peeking here is to avoid
                // creating a new segment's files
                // if no document are available.
                if document_iterator.peek().is_some() {
                    let index_result =
                        index_documents(&mut heap, segment, &schema, &mut document_iterator)
                            .map(|num_docs| (segment_id, num_docs));
                    segment_ready_sender_clone.send(index_result);
                } else {
                    return;
                }
            }
        });
        self.workers_join_handle.push(join_handle);
        Ok(())
    }

    /// Open a new index writer
    ///
    /// num_threads tells the number of indexing worker that
    /// should work at the same time.
    pub fn open(index: &Index,
                num_threads: usize,
                heap_size_in_bytes_per_thread: usize)
                -> Result<IndexWriter> {
        if heap_size_in_bytes_per_thread <= HEAP_SIZE_LIMIT as usize {
            panic!(format!("The heap size per thread needs to be at least {}.",
                           HEAP_SIZE_LIMIT));
        }

        let mut cloned_index = index.clone();
        let lockfile_path = Path::new(LOCKFILE_NAME);

        try!(cloned_index.directory_mut().open_write(lockfile_path));
        let (document_sender, document_receiver): (DocumentSender, DocumentReceiver) =
            chan::sync(PIPELINE_MAX_SIZE_IN_DOCS);
        let (segment_ready_sender, segment_ready_receiver): (NewSegmentSender, NewSegmentReceiver) = chan::async();
        let mut index_writer = IndexWriter {
            heap_size_in_bytes_per_thread: heap_size_in_bytes_per_thread,
            index: cloned_index,
            segment_ready_receiver: segment_ready_receiver,
            segment_ready_sender: segment_ready_sender,
            document_receiver: document_receiver,
            document_sender: document_sender,
            workers_join_handle: Vec::new(),
            num_threads: num_threads,
            docstamp: try!(index.docstamp()),
        };
        try!(index_writer.start_workers());
        Ok(index_writer)
    }

    fn start_workers(&mut self) -> Result<()> {
        for _ in 0..self.num_threads {
            try!(self.add_indexing_worker());
        }
        Ok(())
    }

    /// Merges a given list of segments
    pub fn merge(&mut self, segments: &[Segment]) -> Result<()> {
        let schema = self.index.schema();
        let merger = try!(IndexMerger::open(schema, segments));
        let mut merged_segment = self.index.new_segment();
        let segment_serializer = try!(SegmentSerializer::for_segment(&mut merged_segment));
        try!(merger.write(segment_serializer));
        let merged_segment_ids: HashSet<SegmentId> =
            segments.iter().map(|segment| segment.id()).collect();
        try!(self.index.publish_merge_segment(merged_segment_ids, merged_segment.id()));
        Ok(())
    }

    /// Closes the current document channel send.
    /// and replace all the channels by new ones.
    ///
    /// The current workers will keep on indexing
    /// the pending document and stop
    /// when no documents are remaining.
    ///
    /// Returns the former segment_ready channel.
    fn recreate_channels(&mut self)
                         -> (DocumentReceiver, chan::Receiver<Result<(SegmentId, usize)>>) {
        let (mut document_sender, mut document_receiver): (DocumentSender, DocumentReceiver) =
            chan::sync(PIPELINE_MAX_SIZE_IN_DOCS);
        let (mut segment_ready_sender, mut segment_ready_receiver): (NewSegmentSender,
                                                                     NewSegmentReceiver) =
            chan::async();
        swap(&mut self.document_sender, &mut document_sender);
        swap(&mut self.document_receiver, &mut document_receiver);
        swap(&mut self.segment_ready_sender, &mut segment_ready_sender);
        swap(&mut self.segment_ready_receiver,
             &mut segment_ready_receiver);
        (document_receiver, segment_ready_receiver)
    }

    /// Rollback to the last commit
    ///
    /// This cancels all of the update that
    /// happened before after the last commit.
    /// After calling rollback, the index is in the same
    /// state as it was after the last commit.
    ///
    /// The docstamp at the last commit is returned.
    pub fn rollback(&mut self) -> Result<u64> {

        // we cannot drop segment ready receiver yet
        // as it would block the workers.
        let (document_receiver, mut _segment_ready_receiver) = self.recreate_channels();

        // consumes the document receiver pipeline
        // worker don't need to index the pending documents.
        for _ in document_receiver {}

        let mut former_workers_join_handle = Vec::new();
        swap(&mut former_workers_join_handle,
             &mut self.workers_join_handle);

        // wait for all the worker to finish their work
        // (it should be fast since we consumed all pending documents)
        for worker_handle in former_workers_join_handle {
            try!(worker_handle.join()
                .map_err(|e| Error::ErrorInThread(format!("{:?}", e))));
            // add a new worker for the next generation.
            try!(self.add_indexing_worker());
        }

        // reset the docstamp to what it was before
        self.docstamp = try!(self.index.docstamp());
        Ok(self.docstamp)
    }

    /// Commits all of the pending changes
    ///
    /// A call to commit blocks.
    /// After it returns, all of the document that
    /// were added since the last commit are published
    /// and persisted.
    ///
    /// In case of a crash or an hardware failure (as
    /// long as the hard disk is spared), it will be possible
    /// to resume indexing from this point.
    ///
    /// Commit returns the `docstamp` of the last document
    /// that made it in the commit.
    ///
    pub fn commit(&mut self) -> Result<u64> {

        let (document_receiver, segment_ready_receiver) = self.recreate_channels();
        drop(document_receiver);

        // Docstamp of the last document in this commit.
        let commit_docstamp = self.docstamp;

        let mut former_workers_join_handle = Vec::new();
        swap(&mut former_workers_join_handle,
             &mut self.workers_join_handle);

        for worker_handle in former_workers_join_handle {
            try!(worker_handle.join()
                .map_err(|e| Error::ErrorInThread(format!("{:?}", e))));
            // add a new worker for the next generation.
            try!(self.add_indexing_worker());
        }

        let segment_ids_and_size: Vec<(SegmentId, usize)> = try!(segment_ready_receiver.into_iter()
            .collect());

        let segment_ids: Vec<SegmentId> = segment_ids_and_size.iter()
            .map(|&(segment_id, _num_docs)| segment_id)
            .collect();

        try!(self.index.publish_segments(&segment_ids, commit_docstamp));

        Ok(commit_docstamp)
    }

    /// Adds a document.
    ///
    /// If the indexing pipeline is full, this call may block.
    ///
    /// The docstamp is an increasing `u64` that can
    /// be used by the client to align commits with its own
    /// document queue.
    ///
    /// Currently it represents the number of documents that
    /// have been added since the creation of the index.
    pub fn add_document(&mut self, doc: Document) -> io::Result<u64> {
        self.document_sender.send(doc);
        self.docstamp += 1;
        Ok(self.docstamp)
    }
}

#[cfg(test)]
mod tests {

    use schema::{self, Document};
    use Index;
    use Term;
    use Error;

    #[test]
    fn test_lockfile_stops_duplicates() {

        let schema_builder = schema::SchemaBuilder::default();
        let index = Index::create_in_ram(schema_builder.build());
        let index_writer = index.writer(40_000_000).unwrap();
        match index.writer(40_000_000) {
            Err(Error::FileAlreadyExists(_)) => {}
            _ => panic!("Expected FileAlreadyExists error"),
        }
    }

    #[test]
    fn test_lockfile_released_on_drop() {
        let schema_builder = schema::SchemaBuilder::default();
        let index = Index::create_in_ram(schema_builder.build());
        {
            let index_writer = index.writer(40_000_000).unwrap();
        }

        let index_writer_two = index.writer(40_000_000).unwrap();
    }
    #[test]
    fn test_commit_and_rollback() {
        let mut schema_builder = schema::SchemaBuilder::default();
        let text_field = schema_builder.add_text_field("text", schema::TEXT);
        let index = Index::create_in_ram(schema_builder.build());

        let num_docs_containing = |s: &str| {
            let searcher = index.searcher();
            let term_a = Term::from_field_text(text_field, s);
            searcher.doc_freq(&term_a)
        };

        {
            // writing the segment
            let mut index_writer = index.writer_with_num_threads(3, 40_000_000).unwrap();
            {
                let mut doc = Document::default();
                doc.add_text(text_field, "a");
                index_writer.add_document(doc).unwrap();
            }
            assert_eq!(index_writer.rollback().unwrap(), 0u64);
            assert_eq!(num_docs_containing("a"), 0);

            {
                let mut doc = Document::default();
                doc.add_text(text_field, "b");
                index_writer.add_document(doc).unwrap();
            }
            {
                let mut doc = Document::default();
                doc.add_text(text_field, "c");
                index_writer.add_document(doc).unwrap();
            }
            assert_eq!(index_writer.commit().unwrap(), 2u64);

            assert_eq!(num_docs_containing("a"), 0);
            assert_eq!(num_docs_containing("b"), 1);
            assert_eq!(num_docs_containing("c"), 1);
        }
        index.searcher();
    }

}
<commit_msg>📎 small change for clippy<commit_after>use schema::Schema;
use schema::Document;
use indexer::SegmentSerializer;
use core::Index;
use core::SerializableSegment;
use core::Segment;
use std::thread::JoinHandle;
use indexer::SegmentWriter;
use std::clone::Clone;
use std::io;
use std::path::Path;
use std::thread;
use std::collections::HashSet;
use indexer::merger::IndexMerger;
use core::SegmentId;
use datastruct::stacker::Heap;
use std::mem::swap;
use chan;

use Result;
use Error;

// Size of the margin for the heap. A segment is closed when the remaining memory
// in the heap goes below MARGIN_IN_BYTES.
pub const MARGIN_IN_BYTES: u32 = 10_000_000u32;

// We impose the memory per thread to be at least 30 MB.
pub const HEAP_SIZE_LIMIT: u32 = MARGIN_IN_BYTES * 3u32;

// Add document will block if the number of docs waiting in the queue to be indexed reaches PIPELINE_MAX_SIZE_IN_DOCS
const PIPELINE_MAX_SIZE_IN_DOCS: usize = 10_000;

pub const LOCKFILE_NAME: &'static str = ".tantivy-indexer.lock";

type DocumentSender = chan::Sender<Document>;
type DocumentReceiver = chan::Receiver<Document>;

type NewSegmentSender = chan::Sender<Result<(SegmentId, usize)>>;
type NewSegmentReceiver = chan::Receiver<Result<(SegmentId, usize)>>;

/// `IndexWriter` is the user entry-point to add document to an index.
///
/// It manages a small number of indexing thread, as well as a shared
/// indexing queue.
/// Each indexing thread builds its own independant `Segment`, via
/// a `SegmentWriter` object.
pub struct IndexWriter {
    index: Index,
    heap_size_in_bytes_per_thread: usize,
    workers_join_handle: Vec<JoinHandle<()>>,
    segment_ready_sender: NewSegmentSender,
    segment_ready_receiver: NewSegmentReceiver,
    document_receiver: DocumentReceiver,
    document_sender: DocumentSender,
    num_threads: usize,
    docstamp: u64,
}

fn index_documents(heap: &mut Heap,
                   segment: Segment,
                   schema: &Schema,
                   document_iterator: &mut Iterator<Item = Document>)
                   -> Result<usize> {
    heap.clear();
    let mut segment_writer = try!(SegmentWriter::for_segment(heap, segment, &schema));
    for doc in document_iterator {
        try!(segment_writer.add_document(&doc, &schema));
        if segment_writer.is_buffer_full() {
            info!("Buffer limit reached, flushing segment with maxdoc={}.",
                  segment_writer.max_doc());
            break;
        }
    }
    let num_docs = segment_writer.max_doc() as usize;
    try!(segment_writer.finalize());
    Ok(num_docs)
}

impl Drop for IndexWriter {
    fn drop(&mut self) {
        let lockfile_path = Path::new(LOCKFILE_NAME);
        match self.index.directory_mut().delete(lockfile_path) {
            Ok(_) | Err(_) => ()
        }
    }
}
impl IndexWriter {
    /// Spawns a new worker thread for indexing.
    /// The thread consumes documents from the pipeline.
    ///
    fn add_indexing_worker(&mut self) -> Result<()> {
        let index = self.index.clone();
        let schema = self.index.schema();
        let segment_ready_sender_clone = self.segment_ready_sender.clone();
        let document_receiver_clone = self.document_receiver.clone();

        let mut heap = Heap::with_capacity(self.heap_size_in_bytes_per_thread);
        let join_handle: JoinHandle<()> = thread::spawn(move || {
            loop {
                let segment = index.new_segment();
                let segment_id = segment.id();
                let mut document_iterator = document_receiver_clone.clone()
                    .into_iter()
                    .peekable();
                // the peeking here is to avoid
                // creating a new segment's files
                // if no document are available.
                if document_iterator.peek().is_some() {
                    let index_result =
                        index_documents(&mut heap, segment, &schema, &mut document_iterator)
                            .map(|num_docs| (segment_id, num_docs));
                    segment_ready_sender_clone.send(index_result);
                } else {
                    return;
                }
            }
        });
        self.workers_join_handle.push(join_handle);
        Ok(())
    }

    /// Open a new index writer
    ///
    /// num_threads tells the number of indexing worker that
    /// should work at the same time.
    pub fn open(index: &Index,
                num_threads: usize,
                heap_size_in_bytes_per_thread: usize)
                -> Result<IndexWriter> {
        if heap_size_in_bytes_per_thread <= HEAP_SIZE_LIMIT as usize {
            panic!(format!("The heap size per thread needs to be at least {}.",
                           HEAP_SIZE_LIMIT));
        }

        let mut cloned_index = index.clone();
        let lockfile_path = Path::new(LOCKFILE_NAME);

        try!(cloned_index.directory_mut().open_write(lockfile_path));
        let (document_sender, document_receiver): (DocumentSender, DocumentReceiver) =
            chan::sync(PIPELINE_MAX_SIZE_IN_DOCS);
        let (segment_ready_sender, segment_ready_receiver): (NewSegmentSender, NewSegmentReceiver) = chan::async();
        let mut index_writer = IndexWriter {
            heap_size_in_bytes_per_thread: heap_size_in_bytes_per_thread,
            index: cloned_index,
            segment_ready_receiver: segment_ready_receiver,
            segment_ready_sender: segment_ready_sender,
            document_receiver: document_receiver,
            document_sender: document_sender,
            workers_join_handle: Vec::new(),
            num_threads: num_threads,
            docstamp: try!(index.docstamp()),
        };
        try!(index_writer.start_workers());
        Ok(index_writer)
    }

    fn start_workers(&mut self) -> Result<()> {
        for _ in 0..self.num_threads {
            try!(self.add_indexing_worker());
        }
        Ok(())
    }

    /// Merges a given list of segments
    pub fn merge(&mut self, segments: &[Segment]) -> Result<()> {
        let schema = self.index.schema();
        let merger = try!(IndexMerger::open(schema, segments));
        let mut merged_segment = self.index.new_segment();
        let segment_serializer = try!(SegmentSerializer::for_segment(&mut merged_segment));
        try!(merger.write(segment_serializer));
        let merged_segment_ids: HashSet<SegmentId> =
            segments.iter().map(|segment| segment.id()).collect();
        try!(self.index.publish_merge_segment(merged_segment_ids, merged_segment.id()));
        Ok(())
    }

    /// Closes the current document channel send.
    /// and replace all the channels by new ones.
    ///
    /// The current workers will keep on indexing
    /// the pending document and stop
    /// when no documents are remaining.
    ///
    /// Returns the former segment_ready channel.
    fn recreate_channels(&mut self)
                         -> (DocumentReceiver, chan::Receiver<Result<(SegmentId, usize)>>) {
        let (mut document_sender, mut document_receiver): (DocumentSender, DocumentReceiver) =
            chan::sync(PIPELINE_MAX_SIZE_IN_DOCS);
        let (mut segment_ready_sender, mut segment_ready_receiver): (NewSegmentSender,
                                                                     NewSegmentReceiver) =
            chan::async();
        swap(&mut self.document_sender, &mut document_sender);
        swap(&mut self.document_receiver, &mut document_receiver);
        swap(&mut self.segment_ready_sender, &mut segment_ready_sender);
        swap(&mut self.segment_ready_receiver,
             &mut segment_ready_receiver);
        (document_receiver, segment_ready_receiver)
    }

    /// Rollback to the last commit
    ///
    /// This cancels all of the update that
    /// happened before after the last commit.
    /// After calling rollback, the index is in the same
    /// state as it was after the last commit.
    ///
    /// The docstamp at the last commit is returned.
    pub fn rollback(&mut self) -> Result<u64> {

        // we cannot drop segment ready receiver yet
        // as it would block the workers.
        let (document_receiver, mut _segment_ready_receiver) = self.recreate_channels();

        // consumes the document receiver pipeline
        // worker don't need to index the pending documents.
        for _ in document_receiver {}

        let mut former_workers_join_handle = Vec::new();
        swap(&mut former_workers_join_handle,
             &mut self.workers_join_handle);

        // wait for all the worker to finish their work
        // (it should be fast since we consumed all pending documents)
        for worker_handle in former_workers_join_handle {
            try!(worker_handle.join()
                .map_err(|e| Error::ErrorInThread(format!("{:?}", e))));
            // add a new worker for the next generation.
            try!(self.add_indexing_worker());
        }

        // reset the docstamp to what it was before
        self.docstamp = try!(self.index.docstamp());
        Ok(self.docstamp)
    }

    /// Commits all of the pending changes
    ///
    /// A call to commit blocks.
    /// After it returns, all of the document that
    /// were added since the last commit are published
    /// and persisted.
    ///
    /// In case of a crash or an hardware failure (as
    /// long as the hard disk is spared), it will be possible
    /// to resume indexing from this point.
    ///
    /// Commit returns the `docstamp` of the last document
    /// that made it in the commit.
    ///
    pub fn commit(&mut self) -> Result<u64> {

        let (document_receiver, segment_ready_receiver) = self.recreate_channels();
        drop(document_receiver);

        // Docstamp of the last document in this commit.
        let commit_docstamp = self.docstamp;

        let mut former_workers_join_handle = Vec::new();
        swap(&mut former_workers_join_handle,
             &mut self.workers_join_handle);

        for worker_handle in former_workers_join_handle {
            try!(worker_handle.join()
                .map_err(|e| Error::ErrorInThread(format!("{:?}", e))));
            // add a new worker for the next generation.
            try!(self.add_indexing_worker());
        }

        let segment_ids_and_size: Vec<(SegmentId, usize)> = try!(segment_ready_receiver.into_iter()
            .collect());

        let segment_ids: Vec<SegmentId> = segment_ids_and_size.iter()
            .map(|&(segment_id, _num_docs)| segment_id)
            .collect();

        try!(self.index.publish_segments(&segment_ids, commit_docstamp));

        Ok(commit_docstamp)
    }

    /// Adds a document.
    ///
    /// If the indexing pipeline is full, this call may block.
    ///
    /// The docstamp is an increasing `u64` that can
    /// be used by the client to align commits with its own
    /// document queue.
    ///
    /// Currently it represents the number of documents that
    /// have been added since the creation of the index.
    pub fn add_document(&mut self, doc: Document) -> io::Result<u64> {
        self.document_sender.send(doc);
        self.docstamp += 1;
        Ok(self.docstamp)
    }
}

#[cfg(test)]
mod tests {

    use schema::{self, Document};
    use Index;
    use Term;
    use Error;

    #[test]
    fn test_lockfile_stops_duplicates() {

        let schema_builder = schema::SchemaBuilder::default();
        let index = Index::create_in_ram(schema_builder.build());
        let index_writer = index.writer(40_000_000).unwrap();
        match index.writer(40_000_000) {
            Err(Error::FileAlreadyExists(_)) => {}
            _ => panic!("Expected FileAlreadyExists error"),
        }
    }

    #[test]
    fn test_lockfile_released_on_drop() {
        let schema_builder = schema::SchemaBuilder::default();
        let index = Index::create_in_ram(schema_builder.build());
        {
            let index_writer = index.writer(40_000_000).unwrap();
        }

        let index_writer_two = index.writer(40_000_000).unwrap();
    }
    #[test]
    fn test_commit_and_rollback() {
        let mut schema_builder = schema::SchemaBuilder::default();
        let text_field = schema_builder.add_text_field("text", schema::TEXT);
        let index = Index::create_in_ram(schema_builder.build());

        let num_docs_containing = |s: &str| {
            let searcher = index.searcher();
            let term_a = Term::from_field_text(text_field, s);
            searcher.doc_freq(&term_a)
        };

        {
            // writing the segment
            let mut index_writer = index.writer_with_num_threads(3, 40_000_000).unwrap();
            {
                let mut doc = Document::default();
                doc.add_text(text_field, "a");
                index_writer.add_document(doc).unwrap();
            }
            assert_eq!(index_writer.rollback().unwrap(), 0u64);
            assert_eq!(num_docs_containing("a"), 0);

            {
                let mut doc = Document::default();
                doc.add_text(text_field, "b");
                index_writer.add_document(doc).unwrap();
            }
            {
                let mut doc = Document::default();
                doc.add_text(text_field, "c");
                index_writer.add_document(doc).unwrap();
            }
            assert_eq!(index_writer.commit().unwrap(), 2u64);

            assert_eq!(num_docs_containing("a"), 0);
            assert_eq!(num_docs_containing("b"), 1);
            assert_eq!(num_docs_containing("c"), 1);
        }
        index.searcher();
    }

}
<|endoftext|>


<commit_before>mod progress;

use self::progress::Progress;
use anyhow::Result;
use flate2::read::GzDecoder;
use std::fs;
use std::path::Path;
use tar::Archive;
use walkdir::DirEntry;

const REVISION: &str = "50171c310cd15e1b2d3723766ce64e2e4d6696fc";

#[rustfmt::skip]
static EXCLUDE: &[&str] = &[
    // TODO: anonymous structs/unions
    // type A = struct { field: u8 };
    // https://github.com/dtolnay/syn/issues/1049
    "src/test/pretty/anonymous-types.rs",

    // TODO: impl ~const T {}
    // https://github.com/dtolnay/syn/issues/1051
    "src/test/ui/rfc-2632-const-trait-impl/syntax.rs",

    // TODO: ~const in where-clause
    // https://github.com/dtolnay/syn/issues/1051
    "src/test/ui/rfc-2632-const-trait-impl/trait-where-clause-run.rs",
    "src/test/ui/rfc-2632-const-trait-impl/trait-where-clause-self-referential.rs",

    // TODO: doc comment on match arm
    "src/tools/rustfmt/tests/source/match.rs",
    "src/tools/rustfmt/tests/target/match.rs",

    // TODO: inner attribute inside block
    "src/tools/rustfmt/tests/target/attrib-block-expr.rs",

    // Rustc loses some attributes
    // https://github.com/rust-lang/rust/issues/84879
    "src/test/ui/proc-macro/issue-81555.rs",

    // Compile-fail expr parameter in const generic position: f::<1 + 2>()
    "src/test/ui/const-generics/early/closing-args-token.rs",
    "src/test/ui/const-generics/early/const-expression-parameter.rs",

    // Deprecated anonymous parameter syntax in traits
    "src/test/ui/issues/issue-13105.rs",
    "src/test/ui/issues/issue-13775.rs",
    "src/test/ui/issues/issue-34074.rs",
    "src/test/ui/proc-macro/trait-fn-args-2015.rs",
    "src/tools/rustfmt/tests/source/trait.rs",
    "src/tools/rustfmt/tests/target/trait.rs",

    // Excessive nesting
    "src/test/ui/issues/issue-74564-if-expr-stack-overflow.rs",

    // Testing rustfmt on invalid syntax
    "src/tools/rustfmt/tests/coverage/target/comments.rs",
    "src/tools/rustfmt/tests/parser/issue-4126/invalid.rs",
    "src/tools/rustfmt/tests/parser/issue_4418.rs",
    "src/tools/rustfmt/tests/parser/unclosed-delims/issue_4466.rs",
    "src/tools/rustfmt/tests/source/configs/disable_all_formatting/true.rs",
    "src/tools/rustfmt/tests/source/configs/spaces_around_ranges/false.rs",
    "src/tools/rustfmt/tests/source/configs/spaces_around_ranges/true.rs",
    "src/tools/rustfmt/tests/source/type.rs",
    "src/tools/rustfmt/tests/target/configs/spaces_around_ranges/false.rs",
    "src/tools/rustfmt/tests/target/configs/spaces_around_ranges/true.rs",
    "src/tools/rustfmt/tests/target/type.rs",

    // Not actually test cases
    "src/test/rustdoc-ui/test-compile-fail2.rs",
    "src/test/rustdoc-ui/test-compile-fail3.rs",
    "src/test/ui/include-single-expr-helper.rs",
    "src/test/ui/include-single-expr-helper-1.rs",
    "src/test/ui/json-bom-plus-crlf-multifile-aux.rs",
    "src/test/ui/lint/expansion-time-include.rs",
    "src/test/ui/macros/auxiliary/macro-comma-support.rs",
    "src/test/ui/macros/auxiliary/macro-include-items-expr.rs",
    "src/test/ui/parser/auxiliary/issue-21146-inc.rs",
];

pub fn base_dir_filter(entry: &DirEntry) -> bool {
    let path = entry.path();
    if path.is_dir() {
        return true; // otherwise walkdir does not visit the files
    }
    if path.extension().map(|e| e != "rs").unwrap_or(true) {
        return false;
    }

    let mut path_string = path.to_string_lossy();
    if cfg!(windows) {
        path_string = path_string.replace('\\', "/").into();
    }
    let path = if let Some(path) = path_string.strip_prefix("tests/rust/") {
        path
    } else {
        panic!("unexpected path in Rust dist: {}", path_string);
    };

    if path.starts_with("src/test/compile-fail") || path.starts_with("src/test/rustfix") {
        return false;
    }

    if path.starts_with("src/test/ui") {
        let stderr_path = entry.path().with_extension("stderr");
        if stderr_path.exists() {
            // Expected to fail in some way
            return false;
        }
    }

    !EXCLUDE.contains(&path)
}

#[allow(dead_code)]
pub fn edition(path: &Path) -> &'static str {
    if path.ends_with("dyn-2015-no-warnings-without-lints.rs") {
        "2015"
    } else {
        "2018"
    }
}

pub fn clone_rust() {
    let needs_clone = match fs::read_to_string("tests/rust/COMMIT") {
        Err(_) => true,
        Ok(contents) => contents.trim() != REVISION,
    };
    if needs_clone {
        download_and_unpack().unwrap();
    }
    let mut missing = String::new();
    let test_src = Path::new("tests/rust");
    for exclude in EXCLUDE {
        if !test_src.join(exclude).exists() {
            missing += "\ntests/rust/";
            missing += exclude;
        }
    }
    if !missing.is_empty() {
        panic!("excluded test file does not exist:{}\n", missing);
    }
}

fn download_and_unpack() -> Result<()> {
    let url = format!(
        "https://github.com/rust-lang/rust/archive/{}.tar.gz",
        REVISION
    );
    let response = reqwest::blocking::get(&url)?.error_for_status()?;
    let progress = Progress::new(response);
    let decoder = GzDecoder::new(progress);
    let mut archive = Archive::new(decoder);
    let prefix = format!("rust-{}", REVISION);

    let tests_rust = Path::new("tests/rust");
    if tests_rust.exists() {
        fs::remove_dir_all(tests_rust)?;
    }

    for entry in archive.entries()? {
        let mut entry = entry?;
        let path = entry.path()?;
        if path == Path::new("pax_global_header") {
            continue;
        }
        let relative = path.strip_prefix(&prefix)?;
        let out = tests_rust.join(relative);
        entry.unpack(&out)?;
    }

    fs::write("tests/rust/COMMIT", REVISION)?;
    Ok(())
}
<commit_msg>Remove a test exclude that got fixed upstream in rustc<commit_after>mod progress;

use self::progress::Progress;
use anyhow::Result;
use flate2::read::GzDecoder;
use std::fs;
use std::path::Path;
use tar::Archive;
use walkdir::DirEntry;

const REVISION: &str = "50171c310cd15e1b2d3723766ce64e2e4d6696fc";

#[rustfmt::skip]
static EXCLUDE: &[&str] = &[
    // TODO: anonymous structs/unions
    // type A = struct { field: u8 };
    // https://github.com/dtolnay/syn/issues/1049
    "src/test/pretty/anonymous-types.rs",

    // TODO: impl ~const T {}
    // https://github.com/dtolnay/syn/issues/1051
    "src/test/ui/rfc-2632-const-trait-impl/syntax.rs",

    // TODO: ~const in where-clause
    // https://github.com/dtolnay/syn/issues/1051
    "src/test/ui/rfc-2632-const-trait-impl/trait-where-clause-run.rs",
    "src/test/ui/rfc-2632-const-trait-impl/trait-where-clause-self-referential.rs",

    // TODO: doc comment on match arm
    "src/tools/rustfmt/tests/source/match.rs",
    "src/tools/rustfmt/tests/target/match.rs",

    // TODO: inner attribute inside block
    "src/tools/rustfmt/tests/target/attrib-block-expr.rs",

    // Compile-fail expr parameter in const generic position: f::<1 + 2>()
    "src/test/ui/const-generics/early/closing-args-token.rs",
    "src/test/ui/const-generics/early/const-expression-parameter.rs",

    // Deprecated anonymous parameter syntax in traits
    "src/test/ui/issues/issue-13105.rs",
    "src/test/ui/issues/issue-13775.rs",
    "src/test/ui/issues/issue-34074.rs",
    "src/test/ui/proc-macro/trait-fn-args-2015.rs",
    "src/tools/rustfmt/tests/source/trait.rs",
    "src/tools/rustfmt/tests/target/trait.rs",

    // Excessive nesting
    "src/test/ui/issues/issue-74564-if-expr-stack-overflow.rs",

    // Testing rustfmt on invalid syntax
    "src/tools/rustfmt/tests/coverage/target/comments.rs",
    "src/tools/rustfmt/tests/parser/issue-4126/invalid.rs",
    "src/tools/rustfmt/tests/parser/issue_4418.rs",
    "src/tools/rustfmt/tests/parser/unclosed-delims/issue_4466.rs",
    "src/tools/rustfmt/tests/source/configs/disable_all_formatting/true.rs",
    "src/tools/rustfmt/tests/source/configs/spaces_around_ranges/false.rs",
    "src/tools/rustfmt/tests/source/configs/spaces_around_ranges/true.rs",
    "src/tools/rustfmt/tests/source/type.rs",
    "src/tools/rustfmt/tests/target/configs/spaces_around_ranges/false.rs",
    "src/tools/rustfmt/tests/target/configs/spaces_around_ranges/true.rs",
    "src/tools/rustfmt/tests/target/type.rs",

    // Not actually test cases
    "src/test/rustdoc-ui/test-compile-fail2.rs",
    "src/test/rustdoc-ui/test-compile-fail3.rs",
    "src/test/ui/include-single-expr-helper.rs",
    "src/test/ui/include-single-expr-helper-1.rs",
    "src/test/ui/json-bom-plus-crlf-multifile-aux.rs",
    "src/test/ui/lint/expansion-time-include.rs",
    "src/test/ui/macros/auxiliary/macro-comma-support.rs",
    "src/test/ui/macros/auxiliary/macro-include-items-expr.rs",
    "src/test/ui/parser/auxiliary/issue-21146-inc.rs",
];

pub fn base_dir_filter(entry: &DirEntry) -> bool {
    let path = entry.path();
    if path.is_dir() {
        return true; // otherwise walkdir does not visit the files
    }
    if path.extension().map(|e| e != "rs").unwrap_or(true) {
        return false;
    }

    let mut path_string = path.to_string_lossy();
    if cfg!(windows) {
        path_string = path_string.replace('\\', "/").into();
    }
    let path = if let Some(path) = path_string.strip_prefix("tests/rust/") {
        path
    } else {
        panic!("unexpected path in Rust dist: {}", path_string);
    };

    if path.starts_with("src/test/compile-fail") || path.starts_with("src/test/rustfix") {
        return false;
    }

    if path.starts_with("src/test/ui") {
        let stderr_path = entry.path().with_extension("stderr");
        if stderr_path.exists() {
            // Expected to fail in some way
            return false;
        }
    }

    !EXCLUDE.contains(&path)
}

#[allow(dead_code)]
pub fn edition(path: &Path) -> &'static str {
    if path.ends_with("dyn-2015-no-warnings-without-lints.rs") {
        "2015"
    } else {
        "2018"
    }
}

pub fn clone_rust() {
    let needs_clone = match fs::read_to_string("tests/rust/COMMIT") {
        Err(_) => true,
        Ok(contents) => contents.trim() != REVISION,
    };
    if needs_clone {
        download_and_unpack().unwrap();
    }
    let mut missing = String::new();
    let test_src = Path::new("tests/rust");
    for exclude in EXCLUDE {
        if !test_src.join(exclude).exists() {
            missing += "\ntests/rust/";
            missing += exclude;
        }
    }
    if !missing.is_empty() {
        panic!("excluded test file does not exist:{}\n", missing);
    }
}

fn download_and_unpack() -> Result<()> {
    let url = format!(
        "https://github.com/rust-lang/rust/archive/{}.tar.gz",
        REVISION
    );
    let response = reqwest::blocking::get(&url)?.error_for_status()?;
    let progress = Progress::new(response);
    let decoder = GzDecoder::new(progress);
    let mut archive = Archive::new(decoder);
    let prefix = format!("rust-{}", REVISION);

    let tests_rust = Path::new("tests/rust");
    if tests_rust.exists() {
        fs::remove_dir_all(tests_rust)?;
    }

    for entry in archive.entries()? {
        let mut entry = entry?;
        let path = entry.path()?;
        if path == Path::new("pax_global_header") {
            continue;
        }
        let relative = path.strip_prefix(&prefix)?;
        let out = tests_rust.join(relative);
        entry.unpack(&out)?;
    }

    fs::write("tests/rust/COMMIT", REVISION)?;
    Ok(())
}
<|endoftext|>


<commit_before>
#[macro_use(stack)]
extern crate ndarray;

use ndarray::{
    aview1,
    arr2,
    Axis,
    Ix,
    Array,
    ErrorKind,
};

#[test]
fn stacking() {
    let a = arr2(&[[2., 2.],
                   [3., 3.]]);
    let b = ndarray::stack(Axis(0), &[a.view(), a.view()]).unwrap();
    assert_eq!(b, arr2(&[[2., 2.],
                         [3., 3.],
                         [2., 2.],
                         [3., 3.]]));

    let c = stack![Axis(0), a, b];
    assert_eq!(c, arr2(&[[2., 2.],
                         [3., 3.],
                         [2., 2.],
                         [3., 3.],
                         [2., 2.],
                         [3., 3.]]));

    let d = stack![Axis(0), a.row(0), &[9., 9.]];
    assert_eq!(d, aview1(&[2., 2., 9., 9.]));

    let res = ndarray::stack(Axis(1), &[a.view(), c.view()]);
    assert_eq!(res.unwrap_err().kind(), ErrorKind::IncompatibleShape);

    let res = ndarray::stack(Axis(2), &[a.view(), c.view()]);
    assert_eq!(res.unwrap_err().kind(), ErrorKind::OutOfBounds);

    let res: Result<Array<f64, (Ix, Ix)>, _> = ndarray::stack(Axis(0), &[]);
    assert_eq!(res.unwrap_err().kind(), ErrorKind::Unsupported);
}
<commit_msg>Update tests/stacking.rs to use Array2<commit_after>
#[macro_use(stack)]
extern crate ndarray;

use ndarray::{
    aview1,
    arr2,
    Axis,
    Array2,
    ErrorKind,
};

#[test]
fn stacking() {
    let a = arr2(&[[2., 2.],
                   [3., 3.]]);
    let b = ndarray::stack(Axis(0), &[a.view(), a.view()]).unwrap();
    assert_eq!(b, arr2(&[[2., 2.],
                         [3., 3.],
                         [2., 2.],
                         [3., 3.]]));

    let c = stack![Axis(0), a, b];
    assert_eq!(c, arr2(&[[2., 2.],
                         [3., 3.],
                         [2., 2.],
                         [3., 3.],
                         [2., 2.],
                         [3., 3.]]));

    let d = stack![Axis(0), a.row(0), &[9., 9.]];
    assert_eq!(d, aview1(&[2., 2., 9., 9.]));

    let res = ndarray::stack(Axis(1), &[a.view(), c.view()]);
    assert_eq!(res.unwrap_err().kind(), ErrorKind::IncompatibleShape);

    let res = ndarray::stack(Axis(2), &[a.view(), c.view()]);
    assert_eq!(res.unwrap_err().kind(), ErrorKind::OutOfBounds);

    let res: Result<Array2<f64>, _> = ndarray::stack(Axis(0), &[]);
    assert_eq!(res.unwrap_err().kind(), ErrorKind::Unsupported);
}
<|endoftext|>


<commit_before>extern mod extra;
extern mod http;

use std::rt::io::net::ip::{SocketAddr, Ipv4Addr};
use extra::time;

use http::server::{Config, Server, Request, ResponseWriter};
use http::headers::content_type::MediaType;

use application::*;
use application::response::*;

#[deriving(Clone)]
pub struct WidmannServer<T> {
  application: Application<T>
}

impl<T> WidmannServer<T> {
  pub fn new(application: Application<T>) -> WidmannServer<T> {
    WidmannServer { application: application }
  }
}

impl<T: ToResponse> Server for WidmannServer<T> {
    fn get_config(&self) -> Config {
      Config { bind_address: SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 8001 } }
    }

    fn handle_request(&self, r: &Request, w: &mut ResponseWriter) {
      let response = self.application.call(r);

      w.headers.date = Some(time::now_utc());
      w.headers.content_type = Some(MediaType {
          type_: ~"text",
          subtype: ~"plain",
          parameters: ~[(~"charset", ~"UTF-8")]
      });
      w.headers.server = Some(~"Example");

      w.headers.content_length = Some(response.body.len());

      w.status = response.status;
      //w.write(response.body.as_bytes());
    }
}<commit_msg>Buggy version to ilustrate problems<commit_after>extern mod extra;
extern mod http;

use std::rt::io::net::ip::{SocketAddr, Ipv4Addr};
use std::rt::io::Writer;
use extra::time;

use http::server::{Config, Server, Request, ResponseWriter};
use http::headers::content_type::MediaType;

use application::*;
use application::response::*;

#[deriving(Clone)]
pub struct WidmannServer<T> {
  application: Application<T>
}

impl<T> WidmannServer<T> {
  pub fn new(application: Application<T>) -> WidmannServer<T> {
    WidmannServer { application: application }
  }
}

impl<T: ToResponse> Server for WidmannServer<T> {
    fn get_config(&self) -> Config {
      Config { bind_address: SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 8001 } }
    }

    fn handle_request(&self, r: &Request, w: &mut ResponseWriter) {
      let response = self.application.call(r);

      w.headers.date = Some(time::now_utc());
      w.headers.content_type = Some(MediaType {
          type_: ~"text",
          subtype: ~"plain",
          parameters: ~[(~"charset", ~"UTF-8")]
      });
      w.headers.server = Some(~"Example");

      w.headers.content_length = Some(response.body.len());

      w.status = response.status;
      w.write(response.body.as_bytes());
    }
}<|endoftext|>


<commit_before>use nom::{Consumer, FileProducer};
use std::io::{Error, ErrorKind, Result};
use std::u32;

use metadata::{
  Block, BlockData,
  StreamInfo, CueSheet, VorbisComment, Picture,
  PictureType,
  MetaDataConsumer,
};

macro_rules! optional_eq (
  ($compare: expr, $option: expr) => (
    $option.map_or(true, |compare| $compare == compare);
  );
);

pub fn get_metadata(filename: &str) -> Result<Vec<Block>> {
  FileProducer::new(filename, 1024).and_then(|mut producer| {
    let mut consumer = MetaDataConsumer::new();

    consumer.run(&mut producer);

    if !consumer.data.is_empty() {
      Ok(consumer.data)
    } else {
      let error_str = "parser: couldn't find any metadata";

      Err(Error::new(ErrorKind::InvalidData, error_str))
    }
  })
}

/// Reads and returns the `StreamInfo` metadata block of the given FLAC
/// file.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the given filename isn't found
///   or there is no `StreamInfo` within the file.
/// * `ErrorKind::InvalidData` is returned when the data within the file
///   isn't valid FLAC data.
pub fn get_stream_info(filename: &str) -> Result<StreamInfo> {
  get_metadata(filename).and_then(|blocks| {
    let error_str  = "metadata: couldn't find StreamInfo";
    let mut result = Err(Error::new(ErrorKind::NotFound, error_str));

    for block in blocks {
      if let BlockData::StreamInfo(stream_info) = block.data {
        result = Ok(stream_info);
        break;
      }
    }

    result
  })
}

/// Reads and returns the `VorbisComment` metadata block of the given FLAC
/// file.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the given filename isn't found
///   or there is no `VorbisComment` within the file.
/// * `ErrorKind::InvalidData` is returned when the data within the file
///   isn't valid FLAC data.
pub fn get_vorbis_comment(filename: &str) -> Result<VorbisComment> {
  get_metadata(filename).and_then(|blocks| {
    let error_str  = "metadata: couldn't find VorbisComment";
    let mut result = Err(Error::new(ErrorKind::NotFound, error_str));

    for block in blocks {
      if let BlockData::VorbisComment(vorbis_comment) = block.data {
        result = Ok(vorbis_comment);
        break;
      }
    }

    result
  })
}

/// Reads and returns the `CueSheet` metadata block of the given FLAC file.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the given filename isn't found
///   or there is no `CueSheet` within the file.
/// * `ErrorKind::InvalidData` is returned when the data within the file
///   isn't valid FLAC data.
pub fn get_cue_sheet(filename: &str) -> Result<CueSheet> {
  get_metadata(filename).and_then(|blocks| {
    let error_str  = "metadata: couldn't find CueSheet";
    let mut result = Err(Error::new(ErrorKind::NotFound, error_str));

    for block in blocks {
      if let BlockData::CueSheet(cue_sheet) = block.data {
        result = Ok(cue_sheet);
        break;
      }
    }

    result
  })
}

/// Reads and returns a `Picture` metadata block of the given FLAC file.
///
/// There can be more than one `Picture` block in a file, this function
/// takes optional, that being `Option<T>`, parameters that act as
/// constraints to search within. The `Picture` with the largest area
/// matching all constraints will be returned.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the given filename isn't found,
///   there is no `Picture` within the file, or no `Picture` that fits the
///   given constraints.
/// * `ErrorKind::InvalidData` is returned when the data within the file
///   isn't valid FLAC data.
pub fn get_picture(filename: &str,
                   picture_type: Option<PictureType>,
                   mime_type: Option<&str>,
                   description: Option<&str>,
                   max_width: Option<u32>,
                   max_height: Option<u32>,
                   max_depth: Option<u32>,
                   max_colors: Option<u32>)
                   -> Result<Picture> {
  get_metadata(filename).and_then(|blocks| {
    let error_str  = "metadata: couldn't find any Picture";
    let mut result = Err(Error::new(ErrorKind::NotFound, error_str));

    let mut max_area_seen  = 0;
    let mut max_depth_seen = 0;

    let max_value      = u32::max_value();
    let max_width_num  = max_width.unwrap_or(max_value);
    let max_height_num = max_height.unwrap_or(max_value);
    let max_depth_num  = max_depth.unwrap_or(max_value);
    let max_colors_num = max_colors.unwrap_or(max_value);

    for block in blocks {
      if let BlockData::Picture(picture) = block.data {
        let area = (picture.width as u64) * (picture.height as u64);

        if optional_eq!(picture.picture_type, picture_type) &&
           optional_eq!(picture.mime_type, mime_type) &&
           optional_eq!(picture.description, description) &&
           picture.width <= max_width_num &&
           picture.height <= max_height_num &&
           picture.depth <= max_depth_num &&
           picture.colors <= max_colors_num &&
           (area > max_area_seen || (area == max_area_seen &&
                                     picture.depth > max_depth_seen)) {
          max_area_seen  = area;
          max_depth_seen = picture.depth;
          result         = Ok(picture);
        }
      }
    }

    result
  })
}

#[cfg(test)]
mod tests {
  use super::*;
  use std::io::ErrorKind;

  #[test]
  #[should_panic]
  fn test_panic_optional_eq() {
    assert!(optional_eq!(0, Some(1)));
  }

  #[test]
  fn test_optional_eq() {
    assert!(optional_eq!(0, None), "Should always return true when None");
    assert!(optional_eq!(0, Some(0)), "Should return true (Some(0) == 0)");
  }

  #[test]
  fn test_get_metadata() {
    let not_found    = get_metadata("non-existent/file.txt");
    let invalid_data = get_metadata("README.md");
    let result       = get_metadata("tests/assets/input-SVAUP.flac");

    assert_eq!(not_found.unwrap_err().kind(), ErrorKind::NotFound);
    assert_eq!(invalid_data.unwrap_err().kind(), ErrorKind::InvalidData);
    assert!(result.is_ok());
  }
}
<commit_msg>Add documentation for get_metadata<commit_after>use nom::{Consumer, FileProducer};
use std::io::{Error, ErrorKind, Result};
use std::u32;

use metadata::{
  Block, BlockData,
  StreamInfo, CueSheet, VorbisComment, Picture,
  PictureType,
  MetaDataConsumer,
};

macro_rules! optional_eq (
  ($compare: expr, $option: expr) => (
    $option.map_or(true, |compare| $compare == compare);
  );
);

// With the given filename, return all metadata blocks available.
//
// This function expects a flac file, but will return a proper `Result::Err`
// when things go wrong.
//
// # Failures
//
// * `ErrorKind::NotFound` is returned when the given filename isn't found.
// * `ErrorKind::InvalidData` is returned when the data within the file
//   isn't valid FLAC data.
pub fn get_metadata(filename: &str) -> Result<Vec<Block>> {
  FileProducer::new(filename, 1024).and_then(|mut producer| {
    let mut consumer = MetaDataConsumer::new();

    consumer.run(&mut producer);

    if !consumer.data.is_empty() {
      Ok(consumer.data)
    } else {
      let error_str = "parser: couldn't find any metadata";

      Err(Error::new(ErrorKind::InvalidData, error_str))
    }
  })
}

/// Reads and returns the `StreamInfo` metadata block of the given FLAC
/// file.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the given filename isn't found
///   or there is no `StreamInfo` within the file.
/// * `ErrorKind::InvalidData` is returned when the data within the file
///   isn't valid FLAC data.
pub fn get_stream_info(filename: &str) -> Result<StreamInfo> {
  get_metadata(filename).and_then(|blocks| {
    let error_str  = "metadata: couldn't find StreamInfo";
    let mut result = Err(Error::new(ErrorKind::NotFound, error_str));

    for block in blocks {
      if let BlockData::StreamInfo(stream_info) = block.data {
        result = Ok(stream_info);
        break;
      }
    }

    result
  })
}

/// Reads and returns the `VorbisComment` metadata block of the given FLAC
/// file.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the given filename isn't found
///   or there is no `VorbisComment` within the file.
/// * `ErrorKind::InvalidData` is returned when the data within the file
///   isn't valid FLAC data.
pub fn get_vorbis_comment(filename: &str) -> Result<VorbisComment> {
  get_metadata(filename).and_then(|blocks| {
    let error_str  = "metadata: couldn't find VorbisComment";
    let mut result = Err(Error::new(ErrorKind::NotFound, error_str));

    for block in blocks {
      if let BlockData::VorbisComment(vorbis_comment) = block.data {
        result = Ok(vorbis_comment);
        break;
      }
    }

    result
  })
}

/// Reads and returns the `CueSheet` metadata block of the given FLAC file.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the given filename isn't found
///   or there is no `CueSheet` within the file.
/// * `ErrorKind::InvalidData` is returned when the data within the file
///   isn't valid FLAC data.
pub fn get_cue_sheet(filename: &str) -> Result<CueSheet> {
  get_metadata(filename).and_then(|blocks| {
    let error_str  = "metadata: couldn't find CueSheet";
    let mut result = Err(Error::new(ErrorKind::NotFound, error_str));

    for block in blocks {
      if let BlockData::CueSheet(cue_sheet) = block.data {
        result = Ok(cue_sheet);
        break;
      }
    }

    result
  })
}

/// Reads and returns a `Picture` metadata block of the given FLAC file.
///
/// There can be more than one `Picture` block in a file, this function
/// takes optional, that being `Option<T>`, parameters that act as
/// constraints to search within. The `Picture` with the largest area
/// matching all constraints will be returned.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the given filename isn't found,
///   there is no `Picture` within the file, or no `Picture` that fits the
///   given constraints.
/// * `ErrorKind::InvalidData` is returned when the data within the file
///   isn't valid FLAC data.
pub fn get_picture(filename: &str,
                   picture_type: Option<PictureType>,
                   mime_type: Option<&str>,
                   description: Option<&str>,
                   max_width: Option<u32>,
                   max_height: Option<u32>,
                   max_depth: Option<u32>,
                   max_colors: Option<u32>)
                   -> Result<Picture> {
  get_metadata(filename).and_then(|blocks| {
    let error_str  = "metadata: couldn't find any Picture";
    let mut result = Err(Error::new(ErrorKind::NotFound, error_str));

    let mut max_area_seen  = 0;
    let mut max_depth_seen = 0;

    let max_value      = u32::max_value();
    let max_width_num  = max_width.unwrap_or(max_value);
    let max_height_num = max_height.unwrap_or(max_value);
    let max_depth_num  = max_depth.unwrap_or(max_value);
    let max_colors_num = max_colors.unwrap_or(max_value);

    for block in blocks {
      if let BlockData::Picture(picture) = block.data {
        let area = (picture.width as u64) * (picture.height as u64);

        if optional_eq!(picture.picture_type, picture_type) &&
           optional_eq!(picture.mime_type, mime_type) &&
           optional_eq!(picture.description, description) &&
           picture.width <= max_width_num &&
           picture.height <= max_height_num &&
           picture.depth <= max_depth_num &&
           picture.colors <= max_colors_num &&
           (area > max_area_seen || (area == max_area_seen &&
                                     picture.depth > max_depth_seen)) {
          max_area_seen  = area;
          max_depth_seen = picture.depth;
          result         = Ok(picture);
        }
      }
    }

    result
  })
}

#[cfg(test)]
mod tests {
  use super::*;
  use std::io::ErrorKind;

  #[test]
  #[should_panic]
  fn test_panic_optional_eq() {
    assert!(optional_eq!(0, Some(1)));
  }

  #[test]
  fn test_optional_eq() {
    assert!(optional_eq!(0, None), "Should always return true when None");
    assert!(optional_eq!(0, Some(0)), "Should return true (Some(0) == 0)");
  }

  #[test]
  fn test_get_metadata() {
    let not_found    = get_metadata("non-existent/file.txt");
    let invalid_data = get_metadata("README.md");
    let result       = get_metadata("tests/assets/input-SVAUP.flac");

    assert_eq!(not_found.unwrap_err().kind(), ErrorKind::NotFound);
    assert_eq!(invalid_data.unwrap_err().kind(), ErrorKind::InvalidData);
    assert!(result.is_ok());
  }
}
<|endoftext|>


<commit_before>// Allow single character names so clippy doesn't lint on x, y, r, g, b, which
// are reasonable variable names in this domain.
#![allow(clippy::many_single_char_names)]

use std::process::exit;
use std::{cmp::min, io::Write, os::unix::io::AsRawFd};

use wayland_client::{
    event_enum,
    protocol::{wl_compositor, wl_keyboard, wl_pointer, wl_seat, wl_shm},
    Display, Filter, GlobalManager,
};
use wayland_protocols::xdg_shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};

// declare an event enum containing the events we want to receive in the iterator
event_enum!(
    Events |
    Pointer => wl_pointer::WlPointer,
    Keyboard => wl_keyboard::WlKeyboard
);

fn main() {
    let display = Display::connect_to_env().unwrap();

    let mut event_queue = display.create_event_queue();

    let attached_display = (*display).clone().attach(event_queue.token());

    let globals = GlobalManager::new(&attached_display);

    // Make a synchronized roundtrip to the wayland server.
    //
    // When this returns it must be true that the server has already
    // sent us all available globals.
    event_queue.sync_roundtrip(&mut (), |_, _, _| unreachable!()).unwrap();

    /*
     * Create a buffer with window contents
     */

    // buffer (and window) width and height
    let buf_x: u32 = 320;
    let buf_y: u32 = 240;

    // create a tempfile to write the contents of the window on
    let mut tmp = tempfile::tempfile().expect("Unable to create a tempfile.");
    // write the contents to it, lets put a nice color gradient
    for i in 0..(buf_x * buf_y) {
        let x = i % buf_x;
        let y = i / buf_x;
        let a = 0xFF;
        let r = min(((buf_x - x) * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
        let g = min((x * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
        let b = min(((buf_x - x) * 0xFF) / buf_x, (y * 0xFF) / buf_y);
        tmp.write_all(&((a << 24) + (r << 16) + (g << 8) + b).to_ne_bytes()).unwrap();
    }
    let _ = tmp.flush();

    /*
     * Init wayland objects
     */

    // The compositor allows us to creates surfaces
    let compositor = globals.instantiate_exact::<wl_compositor::WlCompositor>(1).unwrap();
    let surface = compositor.create_surface();

    // The SHM allows us to share memory with the server, and create buffers
    // on this shared memory to paint our surfaces
    let shm = globals.instantiate_exact::<wl_shm::WlShm>(1).unwrap();
    let pool = shm.create_pool(
        tmp.as_raw_fd(),            // RawFd to the tempfile serving as shared memory
        (buf_x * buf_y * 4) as i32, // size in bytes of the shared memory (4 bytes per pixel)
    );
    let buffer = pool.create_buffer(
        0,                        // Start of the buffer in the pool
        buf_x as i32,             // width of the buffer in pixels
        buf_y as i32,             // height of the buffer in pixels
        (buf_x * 4) as i32,       // number of bytes between the beginning of two consecutive lines
        wl_shm::Format::Argb8888, // chosen encoding for the data
    );

    let xdg_wm_base = globals
        .instantiate_exact::<xdg_wm_base::XdgWmBase>(2)
        .expect("Compositor does not support xdg_shell");

    xdg_wm_base.quick_assign(|xdg_wm_base, event, _| {
        if let xdg_wm_base::Event::Ping { serial } = event {
            xdg_wm_base.pong(serial);
        };
    });

    let xdg_surface = xdg_wm_base.get_xdg_surface(&surface);
    xdg_surface.quick_assign(move |xdg_surface, event, _| match event {
        xdg_surface::Event::Configure { serial } => {
            println!("xdg_surface (Configure)");
            xdg_surface.ack_configure(serial);
        }
        _ => unreachable!(),
    });

    let xdg_toplevel = xdg_surface.get_toplevel();
    xdg_toplevel.quick_assign(move |_, event, _| match event {
        xdg_toplevel::Event::Close => {
            exit(0);
        }
        xdg_toplevel::Event::Configure { width, height, states } => {
            println!(
                "xdg_toplevel (Configure) width: {}, height: {}, states: {:?}",
                width, height, states
            );
        }
        _ => unreachable!(),
    });
    xdg_toplevel.set_title("Simple Window".to_string());

    // initialize a seat to retrieve pointer & keyboard events
    //
    // example of using a common filter to handle both pointer & keyboard events
    let common_filter = Filter::new(move |event, _, _| match event {
        Events::Pointer { event, .. } => match event {
            wl_pointer::Event::Enter { surface_x, surface_y, .. } => {
                println!("Pointer entered at ({}, {}).", surface_x, surface_y);
            }
            wl_pointer::Event::Leave { .. } => {
                println!("Pointer left.");
            }
            wl_pointer::Event::Motion { surface_x, surface_y, .. } => {
                println!("Pointer moved to ({}, {}).", surface_x, surface_y);
            }
            wl_pointer::Event::Button { button, state, .. } => {
                println!("Button {} was {:?}.", button, state);
            }
            _ => {}
        },
        Events::Keyboard { event, .. } => match event {
            wl_keyboard::Event::Enter { .. } => {
                println!("Gained keyboard focus.");
            }
            wl_keyboard::Event::Leave { .. } => {
                println!("Lost keyboard focus.");
            }
            wl_keyboard::Event::Key { key, state, .. } => {
                println!("Key with id {} was {:?}.", key, state);
            }
            _ => (),
        },
    });
    // to be handled properly this should be more dynamic, as more
    // than one seat can exist (and they can be created and destroyed
    // dynamically), however most "traditional" setups have a single
    // seat, so we'll keep it simple here
    let mut pointer_created = false;
    let mut keyboard_created = false;
    globals.instantiate_exact::<wl_seat::WlSeat>(1).unwrap().quick_assign(move |seat, event, _| {
        // The capabilities of a seat are known at runtime and we retrieve
        // them via an events. 3 capabilities exists: pointer, keyboard, and touch
        // we are only interested in pointer & keyboard here
        use wayland_client::protocol::wl_seat::{Capability, Event as SeatEvent};

        if let SeatEvent::Capabilities { capabilities } = event {
            if !pointer_created && capabilities.contains(Capability::Pointer) {
                // create the pointer only once
                pointer_created = true;
                seat.get_pointer().assign(common_filter.clone());
            }
            if !keyboard_created && capabilities.contains(Capability::Keyboard) {
                // create the keyboard only once
                keyboard_created = true;
                seat.get_keyboard().assign(common_filter.clone());
            }
        }
    });

    event_queue.sync_roundtrip(&mut (), |_, _, _| { /* we ignore unfiltered messages */ }).unwrap();

    surface.attach(Some(&buffer), 0, 0);
    surface.commit();

    loop {
        event_queue.dispatch(&mut (), |_, _, _| { /* we ignore unfiltered messages */ }).unwrap();
    }
}
<commit_msg>client: fix simple_window example<commit_after>// Allow single character names so clippy doesn't lint on x, y, r, g, b, which
// are reasonable variable names in this domain.
#![allow(clippy::many_single_char_names)]

use std::process::exit;
use std::{cmp::min, io::Write, os::unix::io::AsRawFd};

use wayland_client::{
    event_enum,
    protocol::{wl_compositor, wl_keyboard, wl_pointer, wl_seat, wl_shm},
    Display, Filter, GlobalManager,
};
use wayland_protocols::xdg_shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};

// declare an event enum containing the events we want to receive in the iterator
event_enum!(
    Events |
    Pointer => wl_pointer::WlPointer,
    Keyboard => wl_keyboard::WlKeyboard
);

fn main() {
    let display = Display::connect_to_env().unwrap();

    let mut event_queue = display.create_event_queue();

    let attached_display = (*display).clone().attach(event_queue.token());

    let globals = GlobalManager::new(&attached_display);

    // Make a synchronized roundtrip to the wayland server.
    //
    // When this returns it must be true that the server has already
    // sent us all available globals.
    event_queue.sync_roundtrip(&mut (), |_, _, _| unreachable!()).unwrap();

    /*
     * Create a buffer with window contents
     */

    // buffer (and window) width and height
    let buf_x: u32 = 320;
    let buf_y: u32 = 240;

    // create a tempfile to write the contents of the window on
    let mut tmp = tempfile::tempfile().expect("Unable to create a tempfile.");
    // write the contents to it, lets put a nice color gradient
    for i in 0..(buf_x * buf_y) {
        let x = i % buf_x;
        let y = i / buf_x;
        let a = 0xFF;
        let r = min(((buf_x - x) * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
        let g = min((x * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
        let b = min(((buf_x - x) * 0xFF) / buf_x, (y * 0xFF) / buf_y);
        tmp.write_all(&((a << 24) + (r << 16) + (g << 8) + b).to_ne_bytes()).unwrap();
    }
    let _ = tmp.flush();

    /*
     * Init wayland objects
     */

    // The compositor allows us to creates surfaces
    let compositor = globals.instantiate_exact::<wl_compositor::WlCompositor>(1).unwrap();
    let surface = compositor.create_surface();

    // The SHM allows us to share memory with the server, and create buffers
    // on this shared memory to paint our surfaces
    let shm = globals.instantiate_exact::<wl_shm::WlShm>(1).unwrap();
    let pool = shm.create_pool(
        tmp.as_raw_fd(),            // RawFd to the tempfile serving as shared memory
        (buf_x * buf_y * 4) as i32, // size in bytes of the shared memory (4 bytes per pixel)
    );
    let buffer = pool.create_buffer(
        0,                        // Start of the buffer in the pool
        buf_x as i32,             // width of the buffer in pixels
        buf_y as i32,             // height of the buffer in pixels
        (buf_x * 4) as i32,       // number of bytes between the beginning of two consecutive lines
        wl_shm::Format::Argb8888, // chosen encoding for the data
    );

    let xdg_wm_base = globals
        .instantiate_exact::<xdg_wm_base::XdgWmBase>(2)
        .expect("Compositor does not support xdg_shell");

    xdg_wm_base.quick_assign(|xdg_wm_base, event, _| {
        if let xdg_wm_base::Event::Ping { serial } = event {
            xdg_wm_base.pong(serial);
        };
    });

    let xdg_surface = xdg_wm_base.get_xdg_surface(&surface);
    xdg_surface.quick_assign(move |xdg_surface, event, _| match event {
        xdg_surface::Event::Configure { serial } => {
            println!("xdg_surface (Configure)");
            xdg_surface.ack_configure(serial);
        }
        _ => unreachable!(),
    });

    let xdg_toplevel = xdg_surface.get_toplevel();
    xdg_toplevel.quick_assign(move |_, event, _| match event {
        xdg_toplevel::Event::Close => {
            exit(0);
        }
        xdg_toplevel::Event::Configure { width, height, states } => {
            println!(
                "xdg_toplevel (Configure) width: {}, height: {}, states: {:?}",
                width, height, states
            );
        }
        _ => unreachable!(),
    });
    xdg_toplevel.set_title("Simple Window".to_string());

    // initialize a seat to retrieve pointer & keyboard events
    //
    // example of using a common filter to handle both pointer & keyboard events
    let common_filter = Filter::new(move |event, _, _| match event {
        Events::Pointer { event, .. } => match event {
            wl_pointer::Event::Enter { surface_x, surface_y, .. } => {
                println!("Pointer entered at ({}, {}).", surface_x, surface_y);
            }
            wl_pointer::Event::Leave { .. } => {
                println!("Pointer left.");
            }
            wl_pointer::Event::Motion { surface_x, surface_y, .. } => {
                println!("Pointer moved to ({}, {}).", surface_x, surface_y);
            }
            wl_pointer::Event::Button { button, state, .. } => {
                println!("Button {} was {:?}.", button, state);
            }
            _ => {}
        },
        Events::Keyboard { event, .. } => match event {
            wl_keyboard::Event::Enter { .. } => {
                println!("Gained keyboard focus.");
            }
            wl_keyboard::Event::Leave { .. } => {
                println!("Lost keyboard focus.");
            }
            wl_keyboard::Event::Key { key, state, .. } => {
                println!("Key with id {} was {:?}.", key, state);
            }
            _ => (),
        },
    });
    // to be handled properly this should be more dynamic, as more
    // than one seat can exist (and they can be created and destroyed
    // dynamically), however most "traditional" setups have a single
    // seat, so we'll keep it simple here
    let mut pointer_created = false;
    let mut keyboard_created = false;
    globals.instantiate_exact::<wl_seat::WlSeat>(1).unwrap().quick_assign(move |seat, event, _| {
        // The capabilities of a seat are known at runtime and we retrieve
        // them via an events. 3 capabilities exists: pointer, keyboard, and touch
        // we are only interested in pointer & keyboard here
        use wayland_client::protocol::wl_seat::{Capability, Event as SeatEvent};

        if let SeatEvent::Capabilities { capabilities } = event {
            if !pointer_created && capabilities.contains(Capability::Pointer) {
                // create the pointer only once
                pointer_created = true;
                seat.get_pointer().assign(common_filter.clone());
            }
            if !keyboard_created && capabilities.contains(Capability::Keyboard) {
                // create the keyboard only once
                keyboard_created = true;
                seat.get_keyboard().assign(common_filter.clone());
            }
        }
    });

    surface.commit();

    event_queue.sync_roundtrip(&mut (), |_, _, _| { /* we ignore unfiltered messages */ }).unwrap();

    surface.attach(Some(&buffer), 0, 0);
    surface.commit();

    loop {
        event_queue.dispatch(&mut (), |_, _, _| { /* we ignore unfiltered messages */ }).unwrap();
    }
}
<|endoftext|>


<commit_before>
// compiler/abstract_tree.rs

#[derive(Debug)]
pub enum TokenType {
    Symbol,
    Str,
    Int,
    Float,
}

#[derive(Debug)]
pub enum AbstractTree<'a> {
    Node(Vec<AbstractTree<'a>>),
    Token(TokenType, &'a str),
}

impl<'a> AbstractTree<'a> {
    pub fn visit_before(&mut self, f: fn(&mut AbstractTree)) {
        match self {
            &mut AbstractTree::Node(ref mut ats) => {
                ats.iter_mut().map(|mut x| {
                    x.visit_before(f);
                    f(x);
                }).collect::<Vec<_>>();
            }
            _ => { }
        }
    }

    pub fn visit_after(&mut self, f: fn(&mut AbstractTree)) {
        match self {
            &mut AbstractTree::Node(ref mut ats) => {
                ats.iter_mut().map(|mut x| {
                    f(x);
                    x.visit_after(f);
                }).collect::<Vec<_>>();
            }
            _ => { }
        }
    }

    pub fn match_symbol(&mut self, s: &'a str, f: fn (&mut AbstractTree)) {
        let matched = match self {
            &mut AbstractTree::Node(ref mut ats) => {
                match ats.get_mut(0) {
                    Some(&mut AbstractTree::Token(TokenType::Symbol, a)) => s == a,
                    _ => false,
                }
            },
            _ => false,
        };

        if matched {
            f(self);
        }

        match self {
            &mut AbstractTree::Node(ref mut ats) => {
                ats.iter_mut().map(|mut x| {
                    x.match_symbol(s, f);
                }).collect::<Vec<_>>();
            }
            _ => { return }
        }
    }
}

#[cfg(test)]
mod tests {

    #[test]
    fn test_test() {

    }

}
<commit_msg>Wrote test for match_symbol<commit_after>
// compiler/abstract_tree.rs

#[derive(Debug)]
pub enum TokenType {
    Symbol,
    Str,
    Int,
    Float,
}

#[derive(Debug)]
pub enum AbstractTree<'a> {
    Node(Vec<AbstractTree<'a>>),
    Token(TokenType, &'a str),
}

impl<'a> AbstractTree<'a> {
    pub fn visit_before(&mut self, f: fn(&mut AbstractTree)) {
        match self {
            &mut AbstractTree::Node(ref mut ats) => {
                ats.iter_mut().map(|mut x| {
                    x.visit_before(f);
                    f(x);
                }).collect::<Vec<_>>();
            }
            _ => { }
        }
    }

    pub fn visit_after(&mut self, f: fn(&mut AbstractTree)) {
        match self {
            &mut AbstractTree::Node(ref mut ats) => {
                ats.iter_mut().map(|mut x| {
                    f(x);
                    x.visit_after(f);
                }).collect::<Vec<_>>();
            }
            _ => { }
        }
    }

    pub fn match_symbol(&mut self, s: &'a str, f: fn (&mut AbstractTree)) {
        let matched = match self {
            &mut AbstractTree::Node(ref mut ats) => {
                match ats.get_mut(0) {
                    Some(&mut AbstractTree::Token(TokenType::Symbol, a)) => s == a,
                    _ => false,
                }
            },
            _ => false,
        };

        if matched {
            f(self);
        }

        match self {
            &mut AbstractTree::Node(ref mut ats) => {
                ats.iter_mut().map(|mut x| {
                    x.match_symbol(s, f);
                }).collect::<Vec<_>>();
            }
            _ => { return }
        }
    }
}

#[cfg(test)]
mod tests {

    use super::AbstractTree;
    use super::AbstractTree::*;
    use super::TokenType::*;

    fn generate_data<'a>() -> AbstractTree<'a> {
        return Node(vec![
                Node(vec![Token(Symbol, "foo"), Token(Int, "2"), Token(Int, "2"), Node(vec![
                                                                                Token(Symbol, "foo"),
                                                                                Token(Symbol, "foo"),
                ])]),
                Node(vec![Token(Symbol, "define"), Token(Int, "a"), Token(Int, "2")]),
        ]);
    }

    static mut foo_visitor_count: i64 = 0;
    fn foo_visitor(at: &mut AbstractTree) { unsafe { foo_visitor_count += 1; } }

    #[test]
    fn test_match_symbol() {
        let mut data = generate_data();
        data.match_symbol("foo", foo_visitor);
        assert_eq!(unsafe { foo_visitor_count }, 2)
    }

}
<|endoftext|>


<commit_before>// Copyright 2015 Pierre Talbot (IRCAM)

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use solver::propagator::Propagator;
use solver::propagator::Status as PStatus;
use solver::variable::Variable;
use solver::dependencies::VarEventDependencies;
use solver::agenda::Agenda;
use solver::event::VarEvent;
use std::rc::Rc;
use std::cell::RefCell;

#[derive(Copy, Show, PartialEq, Eq)]
pub enum Status {
  Satisfiable,
  Unsatisfiable,
  Unknown
}

pub struct Solver<V: Variable, D, A> {
  propagators: Vec<Box<Propagator<Event=V::Event> + 'static>>,
  variables: Vec<Rc<RefCell<V>>>,
  deps: D,
  agenda: A
}

impl<V, D, A> Solver<V, D, A> where
 V: Variable,
 D: VarEventDependencies,
 A: Agenda
{
  pub fn new() -> Solver<V, D, A> {
    Solver {
      propagators: vec![],
      variables: vec![],
      deps: VarEventDependencies::new(0,0),
      agenda: Agenda::new(0)
    }
  }

  pub fn newvar(&mut self, dom: <V as Variable>::Domain) -> Rc<RefCell<V>> {
    let var_idx = self.variables.len();
    self.variables.push(Rc::new(RefCell::new(Variable::new(var_idx as u32, dom))));
    self.variables[var_idx].clone()
  }

  pub fn add(&mut self, p: Box<Propagator<Event=<V as Variable>::Event> + 'static>) {
    self.propagators.push(p);
  }

  pub fn solve(&mut self) -> Status {
    self.prepare();
    self.propagation_loop()
  }

  fn prepare(&mut self) {
    self.init_deps();
    self.init_agenda();
  }

  fn init_deps(&mut self) {
    self.deps = VarEventDependencies::new(self.variables.len(), <<V as Variable>::Event as VarEvent>::size());
    for (p_idx, p) in self.propagators.iter().enumerate() {
      for (v, ev) in p.dependencies().into_iter() {
        self.deps.subscribe(v as usize, ev, p_idx);
      }
    }
  }

  fn init_agenda(&mut self) {
    self.agenda = Agenda::new(self.propagators.len());
  }

  fn propagation_loop(&mut self) -> Status {
    let mut unsatisfiable = false;
    while let Some(p_idx) = self.agenda.pop() {
      unsatisfiable = !self.propagate_one(p_idx);
      if unsatisfiable { break; }
    }
    if unsatisfiable { Status::Unsatisfiable }
    else if self.deps.is_empty() { Status::Satisfiable }
    else { Status::Unknown }
  }

  fn propagate_one(&mut self, p_idx: usize) -> bool {
    let (events, status) = {
      let mut prop = &mut self.propagators[p_idx];
      if let Some(events) = prop.propagate() {
        (events, prop.status())
      } else {
        return false
      }
    };
    match status {
      PStatus::Disentailed => return false,
      PStatus::Entailed => self.unlink_prop(p_idx),
      PStatus::Unknown => self.reschedule_prop(&events, p_idx)
    };
    self.react(events);
    true
  }

  fn reschedule_prop(&mut self, events: &Vec<(u32, <V as Variable>::Event)>, p_idx: usize) {
    if !events.is_empty() {
      self.agenda.schedule(p_idx);
    }
  }

  fn react(&mut self, events: Vec<(u32, <V as Variable>::Event)>) {
    for (v, ev) in events.into_iter() {
      let mut reactions = self.deps.react(v as usize, ev);
      for &p in *reactions {
        self.agenda.schedule(p);
      }
    }
  }

  fn unlink_prop(&mut self, p_idx: usize) {
    self.agenda.unschedule(p_idx);
    let deps = self.propagators[p_idx].dependencies();
    for &(var, ev) in deps.iter() {
      self.deps.unsubscribe(var as usize, ev, p_idx)
    }
  }
}

#[cfg(test)]
mod test {
  use super::*;
  use solver::fd::var::*;
  use solver::fd::propagator::*;
  use solver::agenda::RelaxedFifoAgenda;
  use solver::dependencies::VarEventDepsVector;

  type FDSolver = Solver<FDVar, VarEventDepsVector, RelaxedFifoAgenda>;

  // #[test]
  // fn basic_test() {
  //   let mut solver: FDSolver = Solver::new();
  //   let var1 = solver.newvar(Interval::new(1,4));
  //   let var2 = solver.newvar(Interval::new(1,4));
  //   let var3 = solver.newvar(Interval::new(1,1));

  //   solver.add(Box::new(XLessThanY::new(var1.clone(), var2)));
  //   assert_eq!(solver.solve(), Status::Unknown);

  //   solver.add(Box::new(XEqualY::new(var1, var3)));
  //   assert_eq!(solver.solve(), Status::Satisfiable);
  // }

  // #[test]
  // fn example_nqueens() {}

  // fn nqueens(u32 n) {
  //   let mut solver: FDSolver = Solver::new();
  // }
}
<commit_msg>Create the trait Space and implement it for Solver.<commit_after>// Copyright 2015 Pierre Talbot (IRCAM)

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use solver::propagator::Propagator;
use solver::propagator::Status as PStatus;
use solver::variable::Variable;
use solver::dependencies::VarEventDependencies;
use solver::agenda::Agenda;
use solver::event::VarEvent;
use std::rc::Rc;
use std::cell::RefCell;

#[derive(Copy, Show, PartialEq, Eq)]
pub enum Status {
  Satisfiable,
  Unsatisfiable,
  Unknown
}

pub trait Space {
  type Constraint;
  type Variable;
  type Domain;

  fn newvar(&mut self, dom: Self::Domain) -> Self::Variable;
  fn add(&mut self, c: Self::Constraint);
  fn solve(&mut self) -> Status;
}

pub struct Solver<V: Variable, D, A> {
  propagators: Vec<Box<Propagator<Event=V::Event> + 'static>>,
  variables: Vec<Rc<RefCell<V>>>,
  deps: D,
  agenda: A
}

impl<V, D, A> Space for Solver<V, D, A> where
 V: Variable,
 D: VarEventDependencies,
 A: Agenda
{
  type Constraint = Box<Propagator<Event=<V as Variable>::Event> + 'static>;
  type Variable = Rc<RefCell<V>>;
  type Domain = <V as Variable>::Domain;

  fn newvar(&mut self, dom: <Solver<V, D, A> as Space>::Domain) ->
   <Solver<V, D, A> as Space>::Variable {
    let var_idx = self.variables.len();
    self.variables.push(Rc::new(RefCell::new(Variable::new(var_idx as u32, dom))));
    self.variables[var_idx].clone()
  }

  fn add(&mut self, p: <Solver<V, D, A> as Space>::Constraint) {
    self.propagators.push(p);
  }

  fn solve(&mut self) -> Status {
    self.prepare();
    self.propagation_loop()
  }
}

impl<V, D, A> Solver<V, D, A> where
 V: Variable,
 D: VarEventDependencies,
 A: Agenda
{
  pub fn new() -> Solver<V, D, A> {
    Solver {
      propagators: vec![],
      variables: vec![],
      deps: VarEventDependencies::new(0,0),
      agenda: Agenda::new(0)
    }
  }

  fn prepare(&mut self) {
    self.init_deps();
    self.init_agenda();
  }

  fn init_deps(&mut self) {
    self.deps = VarEventDependencies::new(self.variables.len(), <<V as Variable>::Event as VarEvent>::size());
    for (p_idx, p) in self.propagators.iter().enumerate() {
      for (v, ev) in p.dependencies().into_iter() {
        self.deps.subscribe(v as usize, ev, p_idx);
      }
    }
  }

  fn init_agenda(&mut self) {
    self.agenda = Agenda::new(self.propagators.len());
  }

  fn propagation_loop(&mut self) -> Status {
    let mut unsatisfiable = false;
    while let Some(p_idx) = self.agenda.pop() {
      unsatisfiable = !self.propagate_one(p_idx);
      if unsatisfiable { break; }
    }
    if unsatisfiable { Status::Unsatisfiable }
    else if self.deps.is_empty() { Status::Satisfiable }
    else { Status::Unknown }
  }

  fn propagate_one(&mut self, p_idx: usize) -> bool {
    let (events, status) = {
      let mut prop = &mut self.propagators[p_idx];
      if let Some(events) = prop.propagate() {
        (events, prop.status())
      } else {
        return false
      }
    };
    match status {
      PStatus::Disentailed => return false,
      PStatus::Entailed => self.unlink_prop(p_idx),
      PStatus::Unknown => self.reschedule_prop(&events, p_idx)
    };
    self.react(events);
    true
  }

  fn reschedule_prop(&mut self, events: &Vec<(u32, <V as Variable>::Event)>, p_idx: usize) {
    if !events.is_empty() {
      self.agenda.schedule(p_idx);
    }
  }

  fn react(&mut self, events: Vec<(u32, <V as Variable>::Event)>) {
    for (v, ev) in events.into_iter() {
      let mut reactions = self.deps.react(v as usize, ev);
      for &p in *reactions {
        self.agenda.schedule(p);
      }
    }
  }

  fn unlink_prop(&mut self, p_idx: usize) {
    self.agenda.unschedule(p_idx);
    let deps = self.propagators[p_idx].dependencies();
    for &(var, ev) in deps.iter() {
      self.deps.unsubscribe(var as usize, ev, p_idx)
    }
  }
}

#[cfg(test)]
mod test {
  use super::*;
  use solver::fd::var::*;
  use solver::fd::propagator::*;
  use solver::agenda::RelaxedFifoAgenda;
  use solver::dependencies::VarEventDepsVector;

  type FDSolver = Solver<FDVar, VarEventDepsVector, RelaxedFifoAgenda>;

  // #[test]
  // fn basic_test() {
  //   let mut solver: FDSolver = Solver::new();
  //   let var1 = solver.newvar(Interval::new(1,4));
  //   let var2 = solver.newvar(Interval::new(1,4));
  //   let var3 = solver.newvar(Interval::new(1,1));

  //   solver.add(Box::new(XLessThanY::new(var1.clone(), var2)));
  //   assert_eq!(solver.solve(), Status::Unknown);

  //   solver.add(Box::new(XEqualY::new(var1, var3)));
  //   assert_eq!(solver.solve(), Status::Satisfiable);
  // }

  // #[test]
  // fn example_nqueens() {}

  // fn nqueens(u32 n) {
  //   let mut solver: FDSolver = Solver::new();
  // }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use std::fs;
use std::path::{Path,PathBuf};
use image_lib;
use std::cmp;
use image_lib::GenericImage;
use image_lib::imageops::overlay;
use std::collections::{HashMap,hash_map};

/// Output the value in pixels, using CSS conventions.
pub fn px(x:u32) -> String {
    format!("{}{}", x, if x!=0 {"px"} else {""})
}

/// Output the position, using CSS conventions.
pub fn position(x:u32 , y:u32) -> String {
    format!("{} {}", px(x), px(y) )
}

/// Information about one of the embedded images.
#[derive(Debug)]
pub struct SpriteRegion {
    pub x: u32,
    pub y: u32,
    pub width: u32,
    pub height: u32,
    pub file_name: String,
    pub name: String
}

impl SpriteRegion {

    /// Output the position for this region, using the offsets.
    pub fn position(&self, offset_x: u32, offset_y: u32) -> String {
        position(self.x + offset_x, self.y + offset_y)
    }
}

/// The sprite map holding all the images for a given invocation.
pub struct SpriteMap {
    pub width: u32,
    pub height: u32,
    pub regions: HashMap<String, SpriteRegion>,
    pub url: String
}

impl SpriteMap {

    /// Layout all the image matching the given glob.
    /// The code reads the images, gets their dimensions and initialize the regions
    /// with the proper layout information. Right now all the images
    /// are assembled in an horizontal strip.
    /// Returns (width, height, regions)
    fn layout(glob:&str) -> (u32,u32,HashMap<String,SpriteRegion>) {
        let paths = fs::read_dir(&Path::new(glob)).unwrap();

        let mut regions = HashMap::new();
        let mut width = 0;
        let mut height = 0;
        for entry in paths {
            let path = entry.unwrap().path();
            if path.extension().unwrap() == "png" {
                match image_lib::open(&path) {
                    Ok(img) => {
                        let (image_width,image_height) = img.dimensions();
                        let region = SpriteRegion {
                            x: width,
                            y:0,
                            width: image_width,
                            height: image_height,
                            file_name: path.to_str().unwrap().to_string(),
                            name: path.file_stem().unwrap().to_str().unwrap().to_string()
                        };
                        regions.insert( region.name.clone(), region);
                        width = width + image_width;
                        height = cmp::max(height, image_height);
                    },
                    Err(_) => {

                    }
                }
            }
        }
        (width, height, regions)

    }

    /// Render a previously laid out region.
    /// Returns the path to the generated file.
    fn render(width:u32, height:u32, regions:hash_map::Values<String,SpriteRegion>,output:&Path) -> PathBuf {
        let mut generated = image_lib::ImageBuffer::new(width, height);
        for region in regions {
            let image_path = Path::new(&region.file_name);
            match image_lib::open(&image_path) {
                Ok(image) => {
                    let buffer = image.to_rgba();
                    overlay( &mut generated, &buffer, region.x, region.y)
                },
                Err(_) => println!("Cannot load image {}", &region.file_name)
            }

        }
        let ref mut fout = fs::File::create(output).unwrap();
        let generated_image = image_lib::ImageRgba8(generated);
        let _ = generated_image.save(fout, image_lib::PNG);
        output.to_path_buf()
    }

    /// Generates a css sprite map from the files matching the glob pattern.
    /// Notes:
    ///   - the glob parameter is a folder for the time being
    ///   - only PNG files can be made into css sprites at this time.
    pub fn build(glob:&str,output:&Path) -> SpriteMap {
        let layout = SpriteMap::layout(glob);
        let output = SpriteMap::render(layout.0, layout.1, layout.2.values(), output);
        SpriteMap {
            width: layout.0,
            height: layout.1,
            regions: layout.2,
            url: output.to_str().unwrap().to_string()
        }
    }

    /// Returns the image and background position for use in a single shorthand property.
    /// For example:
    ///    background: spriteMap.sprite("name")
    /// could generate:
    ///    url('/images/icons.png') 0 -24px no-repeat;
    pub fn sprite(&self, name:&str) -> Option<String> {
        self.regions.get(name).and_then(|r|
            Some(format!("url('/images/{}') {} no-repeat", self.url, r.position(0,0)
        )))

    }

    /// Returns the position for the original image in the sprite.
    /// This is suitable for use as a value to background-position.
    pub fn sprite_position(&self, name:&str, offset_x: u32, offset_y: u32) -> Option<String> {
        self.regions.get(name).and_then(|r| Some(r.position(offset_x, offset_y)))
    }
}

#[test]
fn builds() {
    let sprite_map = SpriteMap::build("data/my-icons",&Path::new("data/out.png"));
    let mut names:Vec<&str> = vec!();
    for k in sprite_map.regions.keys() {
       names.push(&*k);
    }
    names.sort();
    assert_eq!(names,vec!("edit1","edit2","edit3"));
    assert_eq!(sprite_map.height, 32 as u32);
    assert_eq!(sprite_map.width, (names.len()*32) as u32);
    let edit2 = &*sprite_map.sprite("edit2").unwrap();
    assert!( edit2.starts_with("url('/images/data/out.png') "));
    assert!( edit2.ends_with("px 0 no-repeat"));
}
<commit_msg>Remove flaky test<commit_after>#![allow(dead_code)]

use std::fs;
use std::path::{Path,PathBuf};
use image_lib;
use std::cmp;
use image_lib::GenericImage;
use image_lib::imageops::overlay;
use std::collections::{HashMap,hash_map};

/// Output the value in pixels, using CSS conventions.
pub fn px(x:u32) -> String {
    format!("{}{}", x, if x!=0 {"px"} else {""})
}

/// Output the position, using CSS conventions.
pub fn position(x:u32 , y:u32) -> String {
    format!("{} {}", px(x), px(y) )
}

/// Information about one of the embedded images.
#[derive(Debug)]
pub struct SpriteRegion {
    pub x: u32,
    pub y: u32,
    pub width: u32,
    pub height: u32,
    pub file_name: String,
    pub name: String
}

impl SpriteRegion {

    /// Output the position for this region, using the offsets.
    pub fn position(&self, offset_x: u32, offset_y: u32) -> String {
        position(self.x + offset_x, self.y + offset_y)
    }
}

/// The sprite map holding all the images for a given invocation.
pub struct SpriteMap {
    pub width: u32,
    pub height: u32,
    pub regions: HashMap<String, SpriteRegion>,
    pub url: String
}

impl SpriteMap {

    /// Layout all the image matching the given glob.
    /// The code reads the images, gets their dimensions and initialize the regions
    /// with the proper layout information. Right now all the images
    /// are assembled in an horizontal strip.
    /// Returns (width, height, regions)
    fn layout(glob:&str) -> (u32,u32,HashMap<String,SpriteRegion>) {
        let paths = fs::read_dir(&Path::new(glob)).unwrap();

        let mut regions = HashMap::new();
        let mut width = 0;
        let mut height = 0;
        for entry in paths {
            let path = entry.unwrap().path();
            if path.extension().unwrap() == "png" {
                match image_lib::open(&path) {
                    Ok(img) => {
                        let (image_width,image_height) = img.dimensions();
                        let region = SpriteRegion {
                            x: width,
                            y:0,
                            width: image_width,
                            height: image_height,
                            file_name: path.to_str().unwrap().to_string(),
                            name: path.file_stem().unwrap().to_str().unwrap().to_string()
                        };
                        regions.insert( region.name.clone(), region);
                        width = width + image_width;
                        height = cmp::max(height, image_height);
                    },
                    Err(_) => {

                    }
                }
            }
        }
        (width, height, regions)

    }

    /// Render a previously laid out region.
    /// Returns the path to the generated file.
    fn render(width:u32, height:u32, regions:hash_map::Values<String,SpriteRegion>,output:&Path) -> PathBuf {
        let mut generated = image_lib::ImageBuffer::new(width, height);
        for region in regions {
            let image_path = Path::new(&region.file_name);
            match image_lib::open(&image_path) {
                Ok(image) => {
                    let buffer = image.to_rgba();
                    overlay( &mut generated, &buffer, region.x, region.y)
                },
                Err(_) => println!("Cannot load image {}", &region.file_name)
            }

        }
        let ref mut fout = fs::File::create(output).unwrap();
        let generated_image = image_lib::ImageRgba8(generated);
        let _ = generated_image.save(fout, image_lib::PNG);
        output.to_path_buf()
    }

    /// Generates a css sprite map from the files matching the glob pattern.
    /// Notes:
    ///   - the glob parameter is a folder for the time being
    ///   - only PNG files can be made into css sprites at this time.
    pub fn build(glob:&str,output:&Path) -> SpriteMap {
        let layout = SpriteMap::layout(glob);
        let output = SpriteMap::render(layout.0, layout.1, layout.2.values(), output);
        SpriteMap {
            width: layout.0,
            height: layout.1,
            regions: layout.2,
            url: output.to_str().unwrap().to_string()
        }
    }

    /// Returns the image and background position for use in a single shorthand property.
    /// For example:
    ///    background: spriteMap.sprite("name")
    /// could generate:
    ///    url('/images/icons.png') 0 -24px no-repeat;
    pub fn sprite(&self, name:&str) -> Option<String> {
        self.regions.get(name).and_then(|r|
            Some(format!("url('/images/{}') {} no-repeat", self.url, r.position(0,0)
        )))

    }

    /// Returns the position for the original image in the sprite.
    /// This is suitable for use as a value to background-position.
    pub fn sprite_position(&self, name:&str, offset_x: u32, offset_y: u32) -> Option<String> {
        self.regions.get(name).and_then(|r| Some(r.position(offset_x, offset_y)))
    }
}

#[test]
fn builds() {
    let sprite_map = SpriteMap::build("data/my-icons",&Path::new("data/out.png"));
    let mut names:Vec<&str> = vec!();
    for k in sprite_map.regions.keys() {
       names.push(&*k);
    }
    names.sort();
    assert_eq!(names,vec!("edit1","edit2","edit3"));
    assert_eq!(sprite_map.height, 32 as u32);
    assert_eq!(sprite_map.width, (names.len()*32) as u32);
}
<|endoftext|>