<commit_before>//! Page management.
//!
//! Pages are virtual data units of size 4088 bytes. They're represented on disk somewhat
//! non-obviously, since clusters can hold more than one page at once (compression). Every cluster
//! will maximize the number of pages held and when it's filled up, a new cluster will be fetched.

/// A metacluster.
///
/// Metaclusters holds some number of links to other clusters, forming a link in an unrolled
/// freelist.
struct Metacluster {
    /// The free clusters in this metacluster.
    ///
    /// If any, the bottom pointer points to another metacluster.
    free: Vec<cluster::Pointer>,
}

/// A page management error.
enum Error {
    /// No clusters left in the freelist.
    ///
    /// This is the equivalent to OOM, but with disk space.
    OutOfClusters,
    /// A disk error.
    Disk(disk::Error),
}

/// A state of a page manager.
struct State {
    /// The state block.
    ///
    /// The state block stores the state of the file system including allocation state,
    /// configuration, and more.
    state_block: state_block::StateBlock,
    /// The head of the freelist.
    ///
    /// This list is used as the allocation primitive of TFS. It is a simple freelist-based extent
    /// allocation system, but there is one twist: To optimize the data locality, the list is
    /// unrolled.
    freelist_head: Metacluster,
}

/// The page manager.
///
/// This is the center point of the I/O stack, providing allocation, deallocation, compression,
/// etc. It manages the clusters (with the page abstraction) and caches the disks.
struct Manager<D> {
    /// The cached disk header.
    ///
    /// The disk header contains various very basic information about the disk and how to interact
    /// with it.
    ///
    /// In reality, we could fetch this from the `disk` field as-we-go, but that hurts performance,
    /// so we cache it in memory.
    header: header::DiskHeader,
    /// The inner disk.
    disk: Cache<D>,
    /// The state of the manager.
    state: State,
    /// The state of the manager on the time of last cache commit.
    ///
    /// This contains the state of the page manager upon the last cache commit (pipeline flush). It
    /// is used to roll back the page manager when an error occurs.
    committed_state: State,
}

impl<D: Disk> Manager<D> {
    /// Commit the transactions in the pipeline to the cache.
    ///
    /// This runs over the transactions in the pipeline and applies them to the cache. In a sense,
    /// it can be seen as a form of checkpoint as you can revert to the last commit through
    /// `.revert()`, as it stores the old state.
    fn commit(&mut self) {
        // Update the stored committed state to the current state, which we will commit.
        self.committed_state = self.state.clone();
        // Commit the cache pipeline.
        self.disk.commit();
    }

    /// Revert to the last commit.
    ///
    /// This will reset the state to after the previous cache commit.
    fn revert(&mut self) {
        // Revert the state to when it was committed last time.
        self.state = self.committed_state.clone();
        // Revert the cache pipeline.
        self.disk.revert();
    }

    /// Queue a pop from the freelist.
    ///
    /// This adds a new transaction to the cache pipeline, which will pop from the top of the
    /// freelist and return the result.
    fn queue_freelist_pop(&mut self) -> Result<cluster::Pointer, Error> {
        // Pop from the metacluster.
        if let Some(cluster) = self.freelist_head.free.pop() {
            if self.freelist.head.free.is_empty() {
                // The head metacluster is exhausted, so we load the next metacluster (specified to be
                // the last pointer in the metacluster), i.e. `cluster`. The old metacluster is then
                // used as the popped cluster.
                mem::swap(&mut self.state.state_block.cluster, &mut cluster);
                self.state.freelist_head.update(self.disk.read(self.state.freelist_head.cluster)?);

                // We've updated the state block, so we queue a flush to the disk.
                self.queue_state_block_flush();
            } else {
                // Since the freelist head was changed after the pop, we queue a flush.
                self.queue_freelist_head_flush();
            }

            Ok(cluster)
        } else {
            // We ran out of clusters :(.
            Err(Error::OutOfClusters)
        }
    }

    /// Queue a push to the freelist.
    ///
    /// This adds a new transaction to the cache pipeline, which will push some free cluster to the
    /// top of the freelist.
    fn queue_freelist_push(&mut self, cluster: cluster::Pointer) -> Result<(), Error> {
        if self.state.freelist_head.free.len() == cluster::SIZE / cluster::POINTER_SIZE {
            // The freelist head is full, and therefore we use following algorithm:
            //
            // 1. Create a new metacluster at `cluster`.
            // 2. Link said metacluster to the old metacluster.
            // 3. Queue a flush.

            // Clear the in-memory freelist head mirror.
            self.state.freelist_head.free.clear();
            // Put the link to the old freelist head into the new metacluster.
            self.state.freelist_head.push(state_block.freelist_head);

            // Update the freelist head pointer to point to the new metacluster.
            self.state.state_block.freelist_head = cluster;
            // Queue a flush of the new freelist head. This won't leave the system in an
            // inconsistent state as it merely creates a new metacluster, which is first linked
            // later. If the state block flush fails, the metacluster will merely be an orphan
            // cluster, and therefore simply leaked space.
            self.queue_freelist_head_flush();
            // Queue a flush of the state block (or, in particular, the freelist head pointer).
            // This is completely consistent as the freelist head must flush before, thus rendering
            // the pointed cluster a valid metacluster.
            self.queue_state_block_flush();
        } else {
            // There is space for more clusters in the head metacluster.

            // Push the cluster pointer to the freelist head.
            self.state.freelist_head.free.push(cluster);
            // Queue a flush of the new freelist head.
            self.queue_freelist_head_flush();

            // lulz @ these comments. like shit, ticki, they add basically nothing you fuking dumb
            // monkey. seriously stop it
        }
    }
}
<commit_msg>Add checksum calculation and freelist head flushes.<commit_after>//! Page management.
//!
//! Pages are virtual data units of size 4088 bytes. They're represented on disk somewhat
//! non-obviously, since clusters can hold more than one page at once (compression). Every cluster
//! will maximize the number of pages held and when it's filled up, a new cluster will be fetched.

/// A page management error.
enum Error {
    /// No clusters left in the freelist.
    ///
    /// This is the equivalent to OOM, but with disk space.
    OutOfClusters,
    /// A disk error.
    Disk(disk::Error),
}

/// A state of a page manager.
struct State {
    /// The state block.
    ///
    /// The state block stores the state of the file system including allocation state,
    /// configuration, and more.
    state_block: state_block::StateBlock,
    /// The first chunk of the freelist.
    ///
    /// This list is used as the allocation primitive of TFS. It is a simple freelist-based extent
    /// allocation system, but there is one twist: To optimize the data locality, the list is
    /// unrolled.
    ///
    /// The first element (if any) points to _another_ freelist chunk (a "metacluster"), which can
    /// be used to traverse to the next metacluster when needed.
    freelist: Vec<cluster::Pointer>,
}

/// The page manager.
///
/// This is the center point of the I/O stack, providing allocation, deallocation, compression,
/// etc. It manages the clusters (with the page abstraction) and caches the disks.
struct Manager<D> {
    /// The cached disk header.
    ///
    /// The disk header contains various very basic information about the disk and how to interact
    /// with it.
    ///
    /// In reality, we could fetch this from the `disk` field as-we-go, but that hurts performance,
    /// so we cache it in memory.
    header: header::DiskHeader,
    /// The inner disk.
    disk: Cache<D>,
    /// The state of the manager.
    state: State,
    /// The state of the manager on the time of last cache commit.
    ///
    /// This contains the state of the page manager upon the last cache commit (pipeline flush). It
    /// is used to roll back the page manager when an error occurs.
    committed_state: State,
}

impl<D: Disk> Manager<D> {
    /// Commit the transactions in the pipeline to the cache.
    ///
    /// This runs over the transactions in the pipeline and applies them to the cache. In a sense,
    /// it can be seen as a form of checkpoint as you can revert to the last commit through
    /// `.revert()`, as it stores the old state.
    fn commit(&mut self) {
        // Update the stored committed state to the current state, which we will commit.
        self.committed_state = self.state.clone();
        // Commit the cache pipeline.
        self.disk.commit();
    }

    /// Revert to the last commit.
    ///
    /// This will reset the state to after the previous cache commit.
    fn revert(&mut self) {
        // Revert the state to when it was committed last time.
        self.state = self.committed_state.clone();
        // Revert the cache pipeline.
        self.disk.revert();
    }

    /// Calculate the checksum of some buffer, based on the user configuration.
    fn checksum(&self, buf: &[u8]) -> u16 {
        // The behavior depends on the chosen checksum algorithm.
        match self.state.state_block.checksum {
            // Constant checksums. These are a bit weird, but in the end it makes sense: a number
            // is fixed to some value (in this case, the highest 16-bit integer), under the
            // assumption that if a sector is damaged, all of it is affected, hence the number
            // shouldn't match. Obviously, this isn't true for every disk, therefore one must
            // be careful before using this.
            ChecksumAlgorithm::Constant => 0xFFFF,
            // Hash the thing via SeaHash, then take the 16 lowest bits (truncating cast).
            ChecksumAlgorithm::SeaHash => seahash::hash(buf) as u16,
        }
    }

    /// Queue a state block flush.
    ///
    /// This queues a new transaction flushing the state block.
    fn queue_state_block_flush(&mut self) {
        self.disk.queue(self.header.state_block_address, self.state.state_block.into());
    }

    /// Queue a freelist head flush.
    ///
    /// This queues a new transaction flushing the freelist head.
    fn queue_freelist_head_flush(&mut self) {
        // Start with an all-null cluster buffer.
        let mut buf = Box::new([0; disk::SECTOR_SIZE]);

        // Write every pointer of the freelist into the buffer.
        for (n, i) in self.free.iter().enumerate() {
            LittleEndian::write(&mut buf[cluster::POINTER_SIZE * i], i);
        }

        // Checksum the non-checksum part of the buffer, and write it at the start of the buffer.
        LittleEndian::write(&mut buf, self.checksum(&buf[2..]));

        // Queue the write of the updated buffer.
        self.disk.queue(self.state.state_block.freelist_head, buf);
    }

    /// Queue a pop from the freelist.
    ///
    /// This adds a new transaction to the cache pipeline, which will pop from the top of the
    /// freelist and return the result.
    fn queue_freelist_pop(&mut self) -> Result<cluster::Pointer, Error> {
        // Pop from the metacluster.
        if let Some(cluster) = self.state.freelist.pop() {
            if self.freelist.head.free.is_empty() {
                // The head metacluster is exhausted, so we load the next metacluster (specified to be
                // the last pointer in the metacluster), i.e. `cluster`. The old metacluster is then
                // used as the popped cluster.
                mem::swap(&mut self.state.state_block.cluster, &mut cluster);
                self.state.load_freelist(self.disk.read(self.state_block.freelist_head)?);

                // We've updated the state block, so we queue a flush to the disk.
                self.queue_state_block_flush();
            } else {
                // Since the freelist head was changed after the pop, we queue a flush.
                self.queue_freelist_head_flush();
            }

            Ok(cluster)
        } else {
            // We ran out of clusters :(.
            Err(Error::OutOfClusters)
        }
    }

    /// Queue a push to the freelist.
    ///
    /// This adds a new transaction to the cache pipeline, which will push some free cluster to the
    /// top of the freelist.
    fn queue_freelist_push(&mut self, cluster: cluster::Pointer) -> Result<(), Error> {
        if self.state.freelist.len() == cluster::SIZE / cluster::POINTER_SIZE {
            // The freelist head is full, and therefore we use following algorithm:
            //
            // 1. Create a new metacluster at `cluster`.
            // 2. Link said metacluster to the old metacluster.
            // 3. Queue a flush.

            // Clear the in-memory freelist head mirror.
            self.state.freelist.clear();
            // Put the link to the old freelist head into the new metacluster.
            self.state.freelist.push(state_block.freelist_head);

            // Update the freelist head pointer to point to the new metacluster.
            self.state.state_block.freelist_head = cluster;
            // Queue a flush of the new freelist head. This won't leave the system in an
            // inconsistent state as it merely creates a new metacluster, which is first linked
            // later. If the state block flush fails, the metacluster will merely be an orphan
            // cluster, and therefore simply leaked space.
            self.queue_freelist_head_flush();
            // Queue a flush of the state block (or, in particular, the freelist head pointer).
            // This is completely consistent as the freelist head must flush before, thus rendering
            // the pointed cluster a valid metacluster.
            self.queue_state_block_flush();
        } else {
            // There is space for more clusters in the head metacluster.

            // Push the cluster pointer to the freelist head.
            self.state.freelist.push(cluster);
            // Queue a flush of the new freelist head.
            self.queue_freelist_head_flush();

            // lulz @ these comments. like shit, ticki, they add basically nothing you fuking dumb
            // monkey. seriously stop it
        }
    }
}
<|endoftext|>


<commit_before>// (c) 2017 Productize SPRL <joost@productize.be>

use std::iter::Peekable;
use std::slice::Iter;

use Sexp;
pub use Result as SResult;

/// convert an `&Sexp` to something
pub trait FromSexp
    where Self: Sized
{
    /// convert from a symbolic-expression to something
    fn from_sexp(&Sexp) -> SResult<Self>;
}

/// convert from a symbolic-expression to something (dispatcher)
pub fn from_sexp<T: FromSexp>(s: &Sexp) -> SResult<T> {
    T::from_sexp(s)
}

/// Atom iterator wrapper
pub struct IterAtom<'a> {
    name: String,
    /// containing iterable (pub for now)
    pub iter: Peekable<Iter<'a, Sexp>>,
}

impl<'a> IterAtom<'a> {
    /// deconstruct a `Sexp` that is a list and starts with 'name'
    pub fn new(s: &'a Sexp, name: &str) -> SResult<IterAtom<'a>> {
        let v = s.list()?;
        let mut i = v.iter();
        let st = match i.next() {
            None => return Err(format!("missing first element {} in list {}", name, s).into()),
            Some(e) => e.string()?,
        };
        if st != name {
            return Err(format!("list {} doesn't start with {}, but with {}", s, name, st).into());
        }
        let i = i.peekable();
        Ok(IterAtom {
            name: name.into(),
            iter: i,
        })
    }

    /// deconstruct a `Sexp` that is a list and doesn't start with a name
    pub fn new_nameless(s: &'a Sexp, name:&str) -> SResult<IterAtom<'a>> {
        Ok(IterAtom {
            name: name.into(),
            iter: s.list()?.iter().peekable(),
        })
    }

    fn expect<T, F>(&mut self, name: &str, get: F) -> SResult<T>
        where F: Fn(&Sexp) -> SResult<T>
    {
        match self.iter.next() {
            Some(x) => get(x),
            None => return Err(format!("missing {} field in {}", name, self.name).into()),
        }
    }

    /// expect an integer while iterating a `Sexp` list
    pub fn i(&mut self, name: &str) -> SResult<i64> {
        self.expect(name, |x| x.i().map_err(From::from))
    }

    /// expect a float while iterating a `Sexp` list
    pub fn f(&mut self, name: &str) -> SResult<f64> {
        self.expect(name, |x| x.f().map_err(From::from))
    }

    /// expect a String while iterating a `Sexp` list
    pub fn s(&mut self, name: &str) -> SResult<String> {
        self.expect(name, |x| x.s().map_err(From::from))
    }

    /// expect a list contained String while iterating a `Sexp` list
    pub fn s_in_list(&mut self, name: &str) -> SResult<String> {
        self.expect(name,
                    |x| x.named_value_s(name).map_err(From::from))
    }

    /// expect a list contained i64 while iterating a `Sexp` list
    pub fn i_in_list(&mut self, name: &str) -> SResult<i64> {
        self.expect(name, |x| x.named_value_i(name).map_err(From::from))
    }

    /// expect a list contained f64 while iterating a `Sexp` list
    pub fn f_in_list(&mut self, name: &str) -> SResult<f64> {
        self.expect(name, |x| x.named_value_f(name).map_err(From::from))
    }

    /// expect a `Sexp` while iterating a `Sexp` list
    pub fn t<T: FromSexp>(&mut self, name: &str) -> SResult<T> {
        self.expect(name, |x| T::from_sexp(x))
    }

        /// expect a list containing a `Sexp` while iterating a `Sexp` list
    pub fn t_in_list<T: FromSexp>(&mut self, name: &str) -> SResult<T> {
        self.expect(name, |x| T::from_sexp(x.named_value(name)?))
    }

    /// expect remainder of iterator to be a `Vec<T>`
    pub fn vec<T: FromSexp>(&mut self) -> SResult<Vec<T>> {
        let mut res = Vec::new();
        loop {
            match self.iter.next() {
                None => break,
                Some(e) => {
                    let p = from_sexp(e)?;
                    res.push(p);
                }
            }
        }
        Ok(res)
    }

    /// maybe something while iterating a `Sexp` list
    fn maybe<X, F>(&mut self, convert: F) -> Option<X>
        where F: Fn(&Sexp) -> SResult<X>
    {
        let res = match self.iter.peek() {
            None => None,
            Some(s) => {
                match convert(s) {
                    Ok(t) => Some(t),
                    Err(_) => None,
                }
            }
        };
        match res {
            Some(x) => {
                let _ = self.iter.next();
                Some(x)
            }
            x => x,
        }
    }

    /// maybe a `FromSexp` while iterating a `Sexp` list
    pub fn maybe_t<T: FromSexp>(&mut self) -> Option<T> {
        self.maybe(|x| T::from_sexp(x))
    }

    /// maybe a `String` while iterating a `Sexp` list
    pub fn maybe_s(&mut self) -> Option<String> {
        self.maybe(|x| x.s())
    }

    /// maybe an `i64` while iterating a `Sexp` list
    pub fn maybe_i(&mut self) -> Option<i64> {
        self.maybe(|x| x.i())
    }

    /// maybe an `f64` while iterating a `Sexp` list
    pub fn maybe_f(&mut self) -> Option<f64> {
        self.maybe(|x| x.f())
    }

    /// maybe a list containing a `String` while iterating a `Sexp` list
    pub fn maybe_s_in_list(&mut self, name:&str) -> Option<String> {
        self.maybe(|x| x.named_value_s(name))
    }

    /// maybe a list containing an `i64` while iterating a `Sexp` list
    pub fn maybe_i_in_list(&mut self, name:&str) -> Option<i64> {
        self.maybe(|x| x.named_value_i(name))
    }

    /// maybe a list containing an `f64` while iterating a `Sexp` list
    pub fn maybe_f_in_list(&mut self, name:&str) -> Option<f64> {
        self.maybe(|x| x.named_value_f(name))
    }
}
<commit_msg>close method for IterAtom<commit_after>// (c) 2017 Productize SPRL <joost@productize.be>

use std::iter::Peekable;
use std::slice::Iter;

use Sexp;
pub use Result as SResult;

/// convert an `&Sexp` to something
pub trait FromSexp
    where Self: Sized
{
    /// convert from a symbolic-expression to something
    fn from_sexp(&Sexp) -> SResult<Self>;
}

/// convert from a symbolic-expression to something (dispatcher)
pub fn from_sexp<T: FromSexp>(s: &Sexp) -> SResult<T> {
    T::from_sexp(s)
}

/// Atom iterator wrapper
pub struct IterAtom<'a> {
    name: String,
    /// containing iterable (pub for now)
    pub iter: Peekable<Iter<'a, Sexp>>,
}

impl<'a> IterAtom<'a> {
    /// deconstruct a `Sexp` that is a list and starts with 'name'
    pub fn new(s: &'a Sexp, name: &str) -> SResult<IterAtom<'a>> {
        let v = s.list()?;
        let mut i = v.iter();
        let st = match i.next() {
            None => return Err(format!("missing first element {} in list {}", name, s).into()),
            Some(e) => e.string()?,
        };
        if st != name {
            return Err(format!("list {} doesn't start with {}, but with {}", s, name, st).into());
        }
        let i = i.peekable();
        Ok(IterAtom {
            name: name.into(),
            iter: i,
        })
    }

    /// deconstruct a `Sexp` that is a list and doesn't start with a name
    pub fn new_nameless(s: &'a Sexp, name:&str) -> SResult<IterAtom<'a>> {
        Ok(IterAtom {
            name: name.into(),
            iter: s.list()?.iter().peekable(),
        })
    }

    fn expect<T, F>(&mut self, name: &str, get: F) -> SResult<T>
        where F: Fn(&Sexp) -> SResult<T>
    {
        match self.iter.next() {
            Some(x) => get(x),
            None => return Err(format!("missing {} field in {}", name, self.name).into()),
        }
    }

    /// expect an integer while iterating a `Sexp` list
    pub fn i(&mut self, name: &str) -> SResult<i64> {
        self.expect(name, |x| x.i().map_err(From::from))
    }

    /// expect a float while iterating a `Sexp` list
    pub fn f(&mut self, name: &str) -> SResult<f64> {
        self.expect(name, |x| x.f().map_err(From::from))
    }

    /// expect a String while iterating a `Sexp` list
    pub fn s(&mut self, name: &str) -> SResult<String> {
        self.expect(name, |x| x.s().map_err(From::from))
    }

    /// expect a list contained String while iterating a `Sexp` list
    pub fn s_in_list(&mut self, name: &str) -> SResult<String> {
        self.expect(name,
                    |x| x.named_value_s(name).map_err(From::from))
    }

    /// expect a list contained i64 while iterating a `Sexp` list
    pub fn i_in_list(&mut self, name: &str) -> SResult<i64> {
        self.expect(name, |x| x.named_value_i(name).map_err(From::from))
    }

    /// expect a list contained f64 while iterating a `Sexp` list
    pub fn f_in_list(&mut self, name: &str) -> SResult<f64> {
        self.expect(name, |x| x.named_value_f(name).map_err(From::from))
    }

    /// expect a `Sexp` while iterating a `Sexp` list
    pub fn t<T: FromSexp>(&mut self, name: &str) -> SResult<T> {
        self.expect(name, |x| T::from_sexp(x))
    }

        /// expect a list containing a `Sexp` while iterating a `Sexp` list
    pub fn t_in_list<T: FromSexp>(&mut self, name: &str) -> SResult<T> {
        self.expect(name, |x| T::from_sexp(x.named_value(name)?))
    }

    /// expect remainder of iterator to be a `Vec<T>`
    pub fn vec<T: FromSexp>(&mut self) -> SResult<Vec<T>> {
        let mut res = Vec::new();
        loop {
            match self.iter.next() {
                None => break,
                Some(e) => {
                    let p = from_sexp(e)?;
                    res.push(p);
                }
            }
        }
        Ok(res)
    }

    /// maybe something while iterating a `Sexp` list
    fn maybe<X, F>(&mut self, convert: F) -> Option<X>
        where F: Fn(&Sexp) -> SResult<X>
    {
        let res = match self.iter.peek() {
            None => None,
            Some(s) => {
                match convert(s) {
                    Ok(t) => Some(t),
                    Err(_) => None,
                }
            }
        };
        match res {
            Some(x) => {
                let _ = self.iter.next();
                Some(x)
            }
            x => x,
        }
    }

    /// maybe a `FromSexp` while iterating a `Sexp` list
    pub fn maybe_t<T: FromSexp>(&mut self) -> Option<T> {
        self.maybe(|x| T::from_sexp(x))
    }

    /// maybe a `String` while iterating a `Sexp` list
    pub fn maybe_s(&mut self) -> Option<String> {
        self.maybe(|x| x.s())
    }

    /// maybe an `i64` while iterating a `Sexp` list
    pub fn maybe_i(&mut self) -> Option<i64> {
        self.maybe(|x| x.i())
    }

    /// maybe an `f64` while iterating a `Sexp` list
    pub fn maybe_f(&mut self) -> Option<f64> {
        self.maybe(|x| x.f())
    }

    /// maybe a list containing a `String` while iterating a `Sexp` list
    pub fn maybe_s_in_list(&mut self, name:&str) -> Option<String> {
        self.maybe(|x| x.named_value_s(name))
    }

    /// maybe a list containing an `i64` while iterating a `Sexp` list
    pub fn maybe_i_in_list(&mut self, name:&str) -> Option<i64> {
        self.maybe(|x| x.named_value_i(name))
    }

    /// maybe a list containing an `f64` while iterating a `Sexp` list
    pub fn maybe_f_in_list(&mut self, name:&str) -> Option<f64> {
        self.maybe(|x| x.named_value_f(name))
    }

    /// make sure we consumed all of the iterator
    pub fn close<T>(&mut self, x:T) -> SResult<T> {
        match self.iter.next() {
            Some(x) => Err(format!("Unexpected {} in {}", x, self.name).into()),
            None => Ok(x)
        }
    }
}
<|endoftext|>


<commit_before>//! API to generate `.rs` files.
//!
//! This API does not require `protoc` command present in `$PATH`.
//!
//! ```
//! extern crate protoc_rust;
//!
//! fn main() {
//!     protobuf_codegen_pure::Codegen::new()
//!         .out_dir("src/protos")
//!         .inputs(&["protos/a.proto", "protos/b.proto"])
//!         .include("protos")
//!         .run()
//!         .expect("Codegen failed.");
//! }
//! ```
//!
//! It is advisable that `protobuf-codegen-pure` build-dependecy version be the same as
//! `protobuf` dependency.
//!
//! The alternative is to use `protoc-rust` crate.

#![deny(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]

extern crate protobuf;
extern crate protobuf_codegen;

mod convert;

use std::error::Error;
use std::fmt;
use std::fs;
use std::io;
use std::path::Path;
use std::path::PathBuf;
use std::path::StripPrefixError;
use std::process;

mod linked_hash_map;
mod model;
mod parse_dependencies;
mod parser;
mod path;
mod rel_path;

use crate::parser::ParserErrorWithLocation;
use crate::rel_path::RelPath;
use crate::rel_path::RelPathBuf;
use linked_hash_map::LinkedHashMap;
use protobuf::descriptor::FileDescriptorProto;
use protobuf_codegen::amend_io_error;
pub use protobuf_codegen::Customize;

#[cfg(test)]
mod test_against_protobuf_protos;

/// Invoke pure rust codegen. See [crate docs](crate) for example.
// TODO: merge with protoc-rust def
#[derive(Debug, Default)]
pub struct Codegen {
    /// --lang_out= param
    out_dir: PathBuf,
    /// -I args
    includes: Vec<PathBuf>,
    /// List of .proto files to compile
    inputs: Vec<PathBuf>,
    /// Customize code generation
    customize: Customize,
}

impl Codegen {
    /// Fresh new codegen object.
    pub fn new() -> Self {
        Self::default()
    }

    /// Set the output directory for codegen.
    pub fn out_dir(&mut self, out_dir: impl AsRef<Path>) -> &mut Self {
        self.out_dir = out_dir.as_ref().to_owned();
        self
    }

    /// Add an include directory.
    pub fn include(&mut self, include: impl AsRef<Path>) -> &mut Self {
        self.includes.push(include.as_ref().to_owned());
        self
    }

    /// Add include directories.
    pub fn includes(&mut self, includes: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
        for include in includes {
            self.include(include);
        }
        self
    }

    /// Add an input (`.proto` file).
    pub fn input(&mut self, input: impl AsRef<Path>) -> &mut Self {
        self.inputs.push(input.as_ref().to_owned());
        self
    }

    /// Add inputs (`.proto` files).
    pub fn inputs(&mut self, inputs: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
        for input in inputs {
            self.input(input);
        }
        self
    }

    /// Specify generated code [`Customize`] object.
    pub fn customize(&mut self, customize: Customize) -> &mut Self {
        self.customize = customize;
        self
    }

    /// Like `protoc --rust_out=...` but without requiring `protoc` or `protoc-gen-rust`
    /// commands in `$PATH`.
    pub fn run(&self) -> io::Result<()> {
        let p = parse_and_typecheck(&self.includes, &self.inputs)?;

        protobuf_codegen::gen_and_write(
            &p.file_descriptors,
            &format!("protobuf-codegen-pure={}", env!("CARGO_PKG_VERSION")),
            &p.relative_paths,
            &self.out_dir,
            &self.customize,
        )
    }

    /// Similar to `run`, but prints the message to stderr and exits the process on error.
    pub fn run_from_script(&self) {
        if let Err(e) = self.run() {
            eprintln!("pure rust codegen failed: {}", e);
            process::exit(1);
        }
    }
}

#[derive(Clone)]
pub(crate) struct FileDescriptorPair {
    parsed: model::FileDescriptor,
    descriptor: protobuf::descriptor::FileDescriptorProto,
}

#[derive(Debug)]
enum CodegenError {
    ParserErrorWithLocation(parser::ParserErrorWithLocation),
    ConvertError(convert::ConvertError),
}

impl fmt::Display for CodegenError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            CodegenError::ParserErrorWithLocation(e) => write!(f, "{}", e),
            CodegenError::ConvertError(e) => write!(f, "{}", e),
        }
    }
}

impl From<parser::ParserErrorWithLocation> for CodegenError {
    fn from(e: parser::ParserErrorWithLocation) -> Self {
        CodegenError::ParserErrorWithLocation(e)
    }
}

impl From<convert::ConvertError> for CodegenError {
    fn from(e: convert::ConvertError) -> Self {
        CodegenError::ConvertError(e)
    }
}

#[derive(Debug)]
struct WithFileError {
    file: String,
    error: CodegenError,
}

impl fmt::Display for WithFileError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "error in {}: {}", self.file, self.error)
    }
}

impl Error for WithFileError {}

struct Run<'a> {
    parsed_files: LinkedHashMap<PathBuf, FileDescriptorPair>,
    includes: &'a [PathBuf],
}

impl<'a> Run<'a> {
    fn get_file_and_all_deps_already_parsed(
        &self,
        protobuf_path: &Path,
        result: &mut LinkedHashMap<PathBuf, FileDescriptorPair>,
    ) {
        if let Some(_) = result.get(protobuf_path) {
            return;
        }

        let pair = self
            .parsed_files
            .get(protobuf_path)
            .expect("must be already parsed");
        result.insert(protobuf_path.to_owned(), pair.clone());

        self.get_all_deps_already_parsed(&pair.parsed, result);
    }

    fn get_all_deps_already_parsed(
        &self,
        parsed: &model::FileDescriptor,
        result: &mut LinkedHashMap<PathBuf, FileDescriptorPair>,
    ) {
        for import in &parsed.imports {
            self.get_file_and_all_deps_already_parsed(Path::new(&import.path), result);
        }
    }

    fn add_file(&mut self, protobuf_path: &Path, fs_path: &Path) -> io::Result<()> {
        if let Some(_) = self.parsed_files.get(protobuf_path) {
            return Ok(());
        }

        let content = fs::read_to_string(fs_path)
            .map_err(|e| amend_io_error(e, format!("failed to read {:?}", fs_path)))?;

        self.add_file_content(protobuf_path, fs_path, &content)
    }

    fn add_file_content(
        &mut self,
        protobuf_path: &Path,
        fs_path: &Path,
        content: &str,
    ) -> io::Result<()> {
        let parsed = model::FileDescriptor::parse(content).map_err(|e| {
            io::Error::new(
                io::ErrorKind::Other,
                WithFileError {
                    file: format!("{}", fs_path.display()),
                    error: e.into(),
                },
            )
        })?;

        for import in &parsed.imports {
            self.add_imported_file(Path::new(&import.path))?;
        }

        let mut this_file_deps = LinkedHashMap::new();
        self.get_all_deps_already_parsed(&parsed, &mut this_file_deps);

        let this_file_deps: Vec<_> = this_file_deps.into_iter().map(|(_, v)| v).collect();

        let descriptor = convert::file_descriptor(protobuf_path, &parsed, &this_file_deps)
            .map_err(|e| {
                io::Error::new(
                    io::ErrorKind::Other,
                    WithFileError {
                        file: format!("{}", fs_path.display()),
                        error: e.into(),
                    },
                )
            })?;

        self.parsed_files.insert(
            protobuf_path.to_owned(),
            FileDescriptorPair { parsed, descriptor },
        );

        Ok(())
    }

    fn add_imported_file(&mut self, protobuf_path: &Path) -> io::Result<()> {
        for include_dir in self.includes {
            let fs_path = include_dir.join(protobuf_path);
            if fs_path.exists() {
                return self.add_file(protobuf_path, &fs_path);
            }
        }

        let embedded = match protobuf_path.to_str() {
            Some("rustproto.proto") => Some(RUSTPROTO_PROTO),
            Some("google/protobuf/any.proto") => Some(ANY_PROTO),
            Some("google/protobuf/api.proto") => Some(API_PROTO),
            Some("google/protobuf/descriptor.proto") => Some(DESCRIPTOR_PROTO),
            Some("google/protobuf/duration.proto") => Some(DURATION_PROTO),
            Some("google/protobuf/empty.proto") => Some(EMPTY_PROTO),
            Some("google/protobuf/field_mask.proto") => Some(FIELD_MASK_PROTO),
            Some("google/protobuf/source_context.proto") => Some(SOURCE_CONTEXT_PROTO),
            Some("google/protobuf/struct.proto") => Some(STRUCT_PROTO),
            Some("google/protobuf/timestamp.proto") => Some(TIMESTAMP_PROTO),
            Some("google/protobuf/type.proto") => Some(TYPE_PROTO),
            Some("google/protobuf/wrappers.proto") => Some(WRAPPERS_PROTO),
            _ => None,
        };

        match embedded {
            Some(content) => self.add_file_content(protobuf_path, protobuf_path, content),
            None => Err(io::Error::new(
                io::ErrorKind::Other,
                format!(
                    "protobuf path {:?} is not found in import path {:?}",
                    protobuf_path, self.includes
                ),
            )),
        }
    }

    fn strip_prefix<'b>(path: &'b Path, prefix: &Path) -> Result<&'b Path, StripPrefixError> {
        // special handling of `.` to allow successful `strip_prefix("foo.proto", ".")
        if prefix == Path::new(".") {
            Ok(path)
        } else {
            path.strip_prefix(prefix)
        }
    }

    fn add_fs_file(&mut self, fs_path: &Path) -> io::Result<RelPathBuf> {
        let relative_path = self
            .includes
            .iter()
            .filter_map(|include_dir| Self::strip_prefix(fs_path, include_dir).ok())
            .next();

        match relative_path {
            Some(relative_path) => {
                let relative_path = RelPath::new(relative_path);
                self.add_file(relative_path, fs_path)?;
                Ok(relative_path.to_owned())
            }
            None => Err(io::Error::new(
                io::ErrorKind::Other,
                format!(
                    "file {:?} must reside in include path {:?}",
                    fs_path, self.includes
                ),
            )),
        }
    }
}

#[doc(hidden)]
pub struct ParsedAndTypechecked {
    pub relative_paths: Vec<PathBuf>,
    pub file_descriptors: Vec<protobuf::descriptor::FileDescriptorProto>,
}

#[doc(hidden)]
pub fn parse_and_typecheck(
    includes: &[PathBuf],
    input: &[PathBuf],
) -> io::Result<ParsedAndTypechecked> {
    let mut run = Run {
        parsed_files: LinkedHashMap::new(),
        includes,
    };

    let mut relative_paths = Vec::new();

    for input in input {
        relative_paths.push(run.add_fs_file(input)?.into_path_buf());
    }

    let file_descriptors: Vec<_> = run
        .parsed_files
        .into_iter()
        .map(|(_, v)| v.descriptor)
        .collect();

    Ok(ParsedAndTypechecked {
        relative_paths,
        file_descriptors,
    })
}

/// Parse imports from a `.proto` file.
///
/// The result is [`FileDescriptorProto`] object with only `*dependency` fields filled.
#[doc(hidden)]
pub fn parse_dependencies(content: &str) -> Result<FileDescriptorProto, ParserErrorWithLocation> {
    parse_dependencies::parse_dependencies(content)
}

// TODO: these include don't work when publishing to crates
const RUSTPROTO_PROTO: &str = include_str!("proto/rustproto.proto");
const ANY_PROTO: &str = include_str!("proto/google/protobuf/any.proto");
const API_PROTO: &str = include_str!("proto/google/protobuf/api.proto");
const DESCRIPTOR_PROTO: &str = include_str!("proto/google/protobuf/descriptor.proto");
const DURATION_PROTO: &str = include_str!("proto/google/protobuf/duration.proto");
const EMPTY_PROTO: &str = include_str!("proto/google/protobuf/empty.proto");
const FIELD_MASK_PROTO: &str = include_str!("proto/google/protobuf/field_mask.proto");
const SOURCE_CONTEXT_PROTO: &str = include_str!("proto/google/protobuf/source_context.proto");
const STRUCT_PROTO: &str = include_str!("proto/google/protobuf/struct.proto");
const TIMESTAMP_PROTO: &str = include_str!("proto/google/protobuf/timestamp.proto");
const TYPE_PROTO: &str = include_str!("proto/google/protobuf/type.proto");
const WRAPPERS_PROTO: &str = include_str!("proto/google/protobuf/wrappers.proto");
<commit_msg>Some doc<commit_after>//! API to generate `.rs` files.
//!
//! This API does not require `protoc` command present in `$PATH`.
//!
//! ```
//! extern crate protoc_rust;
//!
//! fn main() {
//!     protobuf_codegen_pure::Codegen::new()
//!         .out_dir("src/protos")
//!         .inputs(&["protos/a.proto", "protos/b.proto"])
//!         .include("protos")
//!         .run()
//!         .expect("Codegen failed.");
//! }
//! ```
//!
//! It is advisable that `protobuf-codegen-pure` build-dependecy version be the same as
//! `protobuf` dependency.
//!
//! The alternative is to use `protoc-rust` crate.

#![deny(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]

extern crate protobuf;
extern crate protobuf_codegen;

mod convert;

use std::error::Error;
use std::fmt;
use std::fs;
use std::io;
use std::path::Path;
use std::path::PathBuf;
use std::path::StripPrefixError;
use std::process;

mod linked_hash_map;
mod model;
mod parse_dependencies;
mod parser;
mod path;
mod rel_path;

use crate::parser::ParserErrorWithLocation;
use crate::rel_path::RelPath;
use crate::rel_path::RelPathBuf;
use linked_hash_map::LinkedHashMap;
use protobuf::descriptor::FileDescriptorProto;
use protobuf_codegen::amend_io_error;
pub use protobuf_codegen::Customize;

#[cfg(test)]
mod test_against_protobuf_protos;

/// Invoke pure rust codegen. See [crate docs](crate) for example.
// TODO: merge with protoc-rust def
#[derive(Debug, Default)]
pub struct Codegen {
    /// --lang_out= param
    out_dir: PathBuf,
    /// -I args
    includes: Vec<PathBuf>,
    /// List of .proto files to compile
    inputs: Vec<PathBuf>,
    /// Customize code generation
    customize: Customize,
}

impl Codegen {
    /// Fresh new codegen object.
    pub fn new() -> Self {
        Self::default()
    }

    /// Set the output directory for codegen.
    pub fn out_dir(&mut self, out_dir: impl AsRef<Path>) -> &mut Self {
        self.out_dir = out_dir.as_ref().to_owned();
        self
    }

    /// Add an include directory.
    pub fn include(&mut self, include: impl AsRef<Path>) -> &mut Self {
        self.includes.push(include.as_ref().to_owned());
        self
    }

    /// Add include directories.
    pub fn includes(&mut self, includes: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
        for include in includes {
            self.include(include);
        }
        self
    }

    /// Add an input (`.proto` file).
    pub fn input(&mut self, input: impl AsRef<Path>) -> &mut Self {
        self.inputs.push(input.as_ref().to_owned());
        self
    }

    /// Add inputs (`.proto` files).
    pub fn inputs(&mut self, inputs: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
        for input in inputs {
            self.input(input);
        }
        self
    }

    /// Specify generated code [`Customize`] object.
    pub fn customize(&mut self, customize: Customize) -> &mut Self {
        self.customize = customize;
        self
    }

    /// Like `protoc --rust_out=...` but without requiring `protoc` or `protoc-gen-rust`
    /// commands in `$PATH`.
    pub fn run(&self) -> io::Result<()> {
        let p = parse_and_typecheck(&self.includes, &self.inputs)?;

        protobuf_codegen::gen_and_write(
            &p.file_descriptors,
            &format!("protobuf-codegen-pure={}", env!("CARGO_PKG_VERSION")),
            &p.relative_paths,
            &self.out_dir,
            &self.customize,
        )
    }

    /// Similar to `run`, but prints the message to stderr and exits the process on error.
    pub fn run_from_script(&self) {
        if let Err(e) = self.run() {
            eprintln!("pure rust codegen failed: {}", e);
            process::exit(1);
        }
    }
}

#[derive(Clone)]
pub(crate) struct FileDescriptorPair {
    parsed: model::FileDescriptor,
    descriptor: protobuf::descriptor::FileDescriptorProto,
}

#[derive(Debug)]
enum CodegenError {
    ParserErrorWithLocation(parser::ParserErrorWithLocation),
    ConvertError(convert::ConvertError),
}

impl fmt::Display for CodegenError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            CodegenError::ParserErrorWithLocation(e) => write!(f, "{}", e),
            CodegenError::ConvertError(e) => write!(f, "{}", e),
        }
    }
}

impl From<parser::ParserErrorWithLocation> for CodegenError {
    fn from(e: parser::ParserErrorWithLocation) -> Self {
        CodegenError::ParserErrorWithLocation(e)
    }
}

impl From<convert::ConvertError> for CodegenError {
    fn from(e: convert::ConvertError) -> Self {
        CodegenError::ConvertError(e)
    }
}

#[derive(Debug)]
struct WithFileError {
    file: String,
    error: CodegenError,
}

impl fmt::Display for WithFileError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "error in {}: {}", self.file, self.error)
    }
}

impl Error for WithFileError {}

struct Run<'a> {
    parsed_files: LinkedHashMap<PathBuf, FileDescriptorPair>,
    includes: &'a [PathBuf],
}

impl<'a> Run<'a> {
    fn get_file_and_all_deps_already_parsed(
        &self,
        protobuf_path: &Path,
        result: &mut LinkedHashMap<PathBuf, FileDescriptorPair>,
    ) {
        if let Some(_) = result.get(protobuf_path) {
            return;
        }

        let pair = self
            .parsed_files
            .get(protobuf_path)
            .expect("must be already parsed");
        result.insert(protobuf_path.to_owned(), pair.clone());

        self.get_all_deps_already_parsed(&pair.parsed, result);
    }

    fn get_all_deps_already_parsed(
        &self,
        parsed: &model::FileDescriptor,
        result: &mut LinkedHashMap<PathBuf, FileDescriptorPair>,
    ) {
        for import in &parsed.imports {
            self.get_file_and_all_deps_already_parsed(Path::new(&import.path), result);
        }
    }

    fn add_file(&mut self, protobuf_path: &Path, fs_path: &Path) -> io::Result<()> {
        if let Some(_) = self.parsed_files.get(protobuf_path) {
            return Ok(());
        }

        let content = fs::read_to_string(fs_path)
            .map_err(|e| amend_io_error(e, format!("failed to read {:?}", fs_path)))?;

        self.add_file_content(protobuf_path, fs_path, &content)
    }

    fn add_file_content(
        &mut self,
        protobuf_path: &Path,
        fs_path: &Path,
        content: &str,
    ) -> io::Result<()> {
        let parsed = model::FileDescriptor::parse(content).map_err(|e| {
            io::Error::new(
                io::ErrorKind::Other,
                WithFileError {
                    file: format!("{}", fs_path.display()),
                    error: e.into(),
                },
            )
        })?;

        for import in &parsed.imports {
            self.add_imported_file(Path::new(&import.path))?;
        }

        let mut this_file_deps = LinkedHashMap::new();
        self.get_all_deps_already_parsed(&parsed, &mut this_file_deps);

        let this_file_deps: Vec<_> = this_file_deps.into_iter().map(|(_, v)| v).collect();

        let descriptor = convert::file_descriptor(protobuf_path, &parsed, &this_file_deps)
            .map_err(|e| {
                io::Error::new(
                    io::ErrorKind::Other,
                    WithFileError {
                        file: format!("{}", fs_path.display()),
                        error: e.into(),
                    },
                )
            })?;

        self.parsed_files.insert(
            protobuf_path.to_owned(),
            FileDescriptorPair { parsed, descriptor },
        );

        Ok(())
    }

    fn add_imported_file(&mut self, protobuf_path: &Path) -> io::Result<()> {
        for include_dir in self.includes {
            let fs_path = include_dir.join(protobuf_path);
            if fs_path.exists() {
                return self.add_file(protobuf_path, &fs_path);
            }
        }

        let embedded = match protobuf_path.to_str() {
            Some("rustproto.proto") => Some(RUSTPROTO_PROTO),
            Some("google/protobuf/any.proto") => Some(ANY_PROTO),
            Some("google/protobuf/api.proto") => Some(API_PROTO),
            Some("google/protobuf/descriptor.proto") => Some(DESCRIPTOR_PROTO),
            Some("google/protobuf/duration.proto") => Some(DURATION_PROTO),
            Some("google/protobuf/empty.proto") => Some(EMPTY_PROTO),
            Some("google/protobuf/field_mask.proto") => Some(FIELD_MASK_PROTO),
            Some("google/protobuf/source_context.proto") => Some(SOURCE_CONTEXT_PROTO),
            Some("google/protobuf/struct.proto") => Some(STRUCT_PROTO),
            Some("google/protobuf/timestamp.proto") => Some(TIMESTAMP_PROTO),
            Some("google/protobuf/type.proto") => Some(TYPE_PROTO),
            Some("google/protobuf/wrappers.proto") => Some(WRAPPERS_PROTO),
            _ => None,
        };

        match embedded {
            Some(content) => self.add_file_content(protobuf_path, protobuf_path, content),
            None => Err(io::Error::new(
                io::ErrorKind::Other,
                format!(
                    "protobuf path {:?} is not found in import path {:?}",
                    protobuf_path, self.includes
                ),
            )),
        }
    }

    fn strip_prefix<'b>(path: &'b Path, prefix: &Path) -> Result<&'b Path, StripPrefixError> {
        // special handling of `.` to allow successful `strip_prefix("foo.proto", ".")
        if prefix == Path::new(".") {
            Ok(path)
        } else {
            path.strip_prefix(prefix)
        }
    }

    fn add_fs_file(&mut self, fs_path: &Path) -> io::Result<RelPathBuf> {
        let relative_path = self
            .includes
            .iter()
            .filter_map(|include_dir| Self::strip_prefix(fs_path, include_dir).ok())
            .next();

        match relative_path {
            Some(relative_path) => {
                let relative_path = RelPath::new(relative_path);
                self.add_file(relative_path, fs_path)?;
                Ok(relative_path.to_owned())
            }
            None => Err(io::Error::new(
                io::ErrorKind::Other,
                format!(
                    "file {:?} must reside in include path {:?}",
                    fs_path, self.includes
                ),
            )),
        }
    }
}

/// Result of parsing `.proto` files.
#[doc(hidden)]
pub struct ParsedAndTypechecked {
    /// One entry for each input `.proto` file.
    pub relative_paths: Vec<PathBuf>,
    /// All parsed `.proto` files including dependencies of input files.
    pub file_descriptors: Vec<protobuf::descriptor::FileDescriptorProto>,
}

#[doc(hidden)]
pub fn parse_and_typecheck(
    includes: &[PathBuf],
    input: &[PathBuf],
) -> io::Result<ParsedAndTypechecked> {
    let mut run = Run {
        parsed_files: LinkedHashMap::new(),
        includes,
    };

    let mut relative_paths = Vec::new();

    for input in input {
        relative_paths.push(run.add_fs_file(input)?.into_path_buf());
    }

    let file_descriptors: Vec<_> = run
        .parsed_files
        .into_iter()
        .map(|(_, v)| v.descriptor)
        .collect();

    Ok(ParsedAndTypechecked {
        relative_paths,
        file_descriptors,
    })
}

/// Parse imports from a `.proto` file.
///
/// The result is [`FileDescriptorProto`] object with only `*dependency` fields filled.
#[doc(hidden)]
pub fn parse_dependencies(content: &str) -> Result<FileDescriptorProto, ParserErrorWithLocation> {
    parse_dependencies::parse_dependencies(content)
}

// TODO: these include don't work when publishing to crates
const RUSTPROTO_PROTO: &str = include_str!("proto/rustproto.proto");
const ANY_PROTO: &str = include_str!("proto/google/protobuf/any.proto");
const API_PROTO: &str = include_str!("proto/google/protobuf/api.proto");
const DESCRIPTOR_PROTO: &str = include_str!("proto/google/protobuf/descriptor.proto");
const DURATION_PROTO: &str = include_str!("proto/google/protobuf/duration.proto");
const EMPTY_PROTO: &str = include_str!("proto/google/protobuf/empty.proto");
const FIELD_MASK_PROTO: &str = include_str!("proto/google/protobuf/field_mask.proto");
const SOURCE_CONTEXT_PROTO: &str = include_str!("proto/google/protobuf/source_context.proto");
const STRUCT_PROTO: &str = include_str!("proto/google/protobuf/struct.proto");
const TIMESTAMP_PROTO: &str = include_str!("proto/google/protobuf/timestamp.proto");
const TYPE_PROTO: &str = include_str!("proto/google/protobuf/type.proto");
const WRAPPERS_PROTO: &str = include_str!("proto/google/protobuf/wrappers.proto");
<|endoftext|>


<commit_before>use protocol::*;
use protocol::basic::gen_properties;
use types::*;
use types::generation::*;

use cookie_factory::GenError;

pub fn gen_protocol_header<'a>(x: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
    do_gen!(x,
        gen_slice!(metadata::NAME.as_bytes()) >>
        gen_slice!(&[0, metadata::MAJOR_VERSION, metadata::MINOR_VERSION, metadata::REVISION])
    )
}

pub fn gen_heartbeat_frame<'a>(x: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
    do_gen!(x, gen_slice!(&[constants::FRAME_HEARTBEAT, 0, 0, 0, 0, 0, 0, constants::FRAME_END]))
}

pub fn gen_content_header_frame<'a>(x: (&'a mut [u8], usize), channel_id: ShortUInt, class_id: ShortUInt, length: LongLongUInt, properties: basic::AMQPProperties) -> Result<(&'a mut [u8], usize), GenError> {
    do_gen!(x,
        gen_short_short_uint(&constants::FRAME_HEADER)                        >>
        gen_id(&channel_id)                                                   >>
        len: gen_skip!(4)                                                     >>
        start: do_gen!(
            gen_id(&class_id)           >>
            gen_short_uint(&0)          >> // weight
            gen_long_long_uint(&length) >>
            gen_properties(&properties)
        ) >>
        end: gen_at_offset!(len, gen_long_uint(&((end - start) as LongUInt))) >>
        gen_short_short_uint(&constants::FRAME_END)
   )
}

pub fn gen_content_body_frame<'a>(x: (&'a mut [u8], usize), channel_id: ShortUInt, content: &[u8]) -> Result<(&'a mut [u8], usize), GenError> {
    do_gen!(x,
        gen_short_short_uint(&constants::FRAME_BODY) >>
        gen_id(&channel_id)                          >>
        gen_long_uint(&(content.len() as LongUInt))  >>
        gen_slice!(content)                          >>
        gen_short_short_uint(&constants::FRAME_END)
    )
}
<commit_msg>protocol: add missing gen_method_frame<commit_after>use protocol::*;
use protocol::basic::gen_properties;
use types::*;
use types::generation::*;

use cookie_factory::GenError;

pub fn gen_protocol_header<'a>(x: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
    do_gen!(x,
        gen_slice!(metadata::NAME.as_bytes()) >>
        gen_slice!(&[0, metadata::MAJOR_VERSION, metadata::MINOR_VERSION, metadata::REVISION])
    )
}

pub fn gen_heartbeat_frame<'a>(x: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
    do_gen!(x, gen_slice!(&[constants::FRAME_HEARTBEAT, 0, 0, 0, 0, 0, 0, constants::FRAME_END]))
}

pub fn gen_method_frame<'a>(x:(&'a mut [u8], usize), channel_id: ShortUInt, class: &AMQPClass) -> Result<(&'a mut [u8], usize), GenError> {
    do_gen!(x,
        gen_short_short_uint(&constants::FRAME_METHOD)                          >>
        gen_id(&channel_id)                                                     >>
        len:   gen_skip!(4)                                                     >>
        start: gen_class(class)                                                 >>
        end:   gen_at_offset!(len, gen_long_uint(&((end - start) as LongUInt))) >>
        gen_short_short_uint(&constants::FRAME_END)
    )
}

pub fn gen_content_header_frame<'a>(x: (&'a mut [u8], usize), channel_id: ShortUInt, class_id: ShortUInt, length: LongLongUInt, properties: basic::AMQPProperties) -> Result<(&'a mut [u8], usize), GenError> {
    do_gen!(x,
        gen_short_short_uint(&constants::FRAME_HEADER)                          >>
        gen_id(&channel_id)                                                     >>
        len:   gen_skip!(4)                                                     >>
        start: do_gen!(
            gen_id(&class_id)           >>
            gen_short_uint(&0)          >> // weight
            gen_long_long_uint(&length) >>
            gen_properties(&properties)
        ) >>
        end:   gen_at_offset!(len, gen_long_uint(&((end - start) as LongUInt))) >>
        gen_short_short_uint(&constants::FRAME_END)
   )
}

pub fn gen_content_body_frame<'a>(x: (&'a mut [u8], usize), channel_id: ShortUInt, content: &[u8]) -> Result<(&'a mut [u8], usize), GenError> {
    do_gen!(x,
        gen_short_short_uint(&constants::FRAME_BODY) >>
        gen_id(&channel_id)                          >>
        gen_long_uint(&(content.len() as LongUInt))  >>
        gen_slice!(content)                          >>
        gen_short_short_uint(&constants::FRAME_END)
    )
}
<|endoftext|>


<commit_before>use std::env;
extern crate clingo;
use clingo::*;
extern crate libc;
use libc::c_void;
use std::ffi::CString;

fn error_main() {
    let error_message = safe_clingo_error_message();
    println!("{}", error_message);
    safe_clingo_error_code();
}

fn main() {

    // create a control object and pass command line arguments
    let logger: clingo_logger_t = None;
    let logger_data: *mut c_void = std::ptr::null_mut();
    let ctl = safe_clingo_control_new(env::args(), logger, logger_data, 20)
        .expect("Failed creating clingo_control");

    // add a logic program to the base part
    let parameters: Vec<&str> = Vec::new();
    if !safe_clingo_control_add(ctl, "base", parameters, "a. {b}. #external c.") {
        return error_main();
    }

    // ground the base part
    let part = safe_clingo_part {
        params: 0,
        size: 0,
        name: CString::new("base").unwrap(),
    };
    let parts = vec![part];
    let ground_callback: clingo_ground_callback_t = None;
    let ground_callback_data: *mut c_void = std::ptr::null_mut();

    if !safe_clingo_control_ground(ctl, parts, ground_callback, ground_callback_data) {
        return error_main();
    }

    // get symbolic atoms
    let ato = safe_clingo_control_symbolic_atoms(ctl).unwrap();

    println!("Symbolic atoms:");

    // get begin and end iterator
    let sig_ptr = std::ptr::null();
    let mut it_a = safe_clingo_symbolic_atoms_begin(ato, sig_ptr).unwrap();
    let ie_a = safe_clingo_symbolic_atoms_end(ato).unwrap();

    let mut equal = safe_clingo_symbolic_atoms_iterator_is_equal_to(ato, it_a, ie_a).unwrap();
    while !equal {

        let symbol = safe_clingo_symbolic_atoms_symbol(ato, it_a).unwrap();
        let atom_string = safe_clingo_symbol_to_string(&symbol).unwrap();
        print!("  {}", atom_string.to_str().unwrap());

        let fact = safe_clingo_symbolic_atoms_is_fact(ato, it_a).unwrap();
        if fact {
            print!(", fact");
        }
        let external = safe_clingo_symbolic_atoms_is_external(ato, it_a).unwrap();
        if external {
            print!(", external");
        }
        println!("");
        it_a = safe_clingo_symbolic_atoms_next(ato, it_a).unwrap();

        equal = safe_clingo_symbolic_atoms_iterator_is_equal_to(ato, it_a, ie_a).unwrap();
    }
}
<commit_msg>cleanup<commit_after>use std::env;
extern crate clingo;
use clingo::*;
extern crate libc;
use libc::c_void;
use std::ffi::CString;

fn error_main() {
    let error_message = safe_clingo_error_message();
    println!("{}", error_message);
    safe_clingo_error_code();
}

fn main() {

    // create a control object and pass command line arguments
    let logger: clingo_logger_t = None;
    let logger_data: *mut c_void = std::ptr::null_mut();
    let ctl = safe_clingo_control_new(env::args(), logger, logger_data, 20)
        .expect("Failed creating clingo_control");

    // add a logic program to the base part
    let parameters: Vec<&str> = Vec::new();
    if !safe_clingo_control_add(ctl, "base", parameters, "a. {b}. #external c.") {
        return error_main();
    }

    // ground the base part
    let part = safe_clingo_part {
        params: 0,
        size: 0,
        name: CString::new("base").unwrap(),
    };
    let parts = vec![part];
    let ground_callback: clingo_ground_callback_t = None;
    let ground_callback_data: *mut c_void = std::ptr::null_mut();

    if !safe_clingo_control_ground(ctl, parts, ground_callback, ground_callback_data) {
        return error_main();
    }

    // get symbolic atoms
    let ato = safe_clingo_control_symbolic_atoms(ctl).unwrap();

    println!("Symbolic atoms:");

    // get begin and end iterator
    let sig_ptr = std::ptr::null();
    let mut it_a = safe_clingo_symbolic_atoms_begin(ato, sig_ptr).unwrap();
    let ie_a = safe_clingo_symbolic_atoms_end(ato).unwrap();

    loop {
        let equal = safe_clingo_symbolic_atoms_iterator_is_equal_to(ato, it_a, ie_a).unwrap();
        if equal { break; }
        let symbol = safe_clingo_symbolic_atoms_symbol(ato, it_a).unwrap();
        let atom_string = safe_clingo_symbol_to_string(&symbol).unwrap();
        print!("  {}", atom_string.to_str().unwrap());

        let fact = safe_clingo_symbolic_atoms_is_fact(ato, it_a).unwrap();
        if fact {
            print!(", fact");
        }
        let external = safe_clingo_symbolic_atoms_is_external(ato, it_a).unwrap();
        if external {
            print!(", external");
        }
        println!("");

        it_a = safe_clingo_symbolic_atoms_next(ato, it_a).unwrap();
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::net::{self, SocketAddr};
use std::rc::Rc;

use futures::{Async, Poll, Stream};
use kcp::{get_conv, set_conv};
use tokio_core::net::UdpSocket;
use tokio_core::reactor::Handle;

use config::KcpConfig;
use session::KcpServerSessionUpdater;
use skcp::KcpOutputHandle;
use stream::ServerKcpStream;

/// A KCP Socket server
pub struct KcpListener {
    udp: Rc<UdpSocket>,
    sessions: KcpServerSessionUpdater,
    handle: Handle,
    config: KcpConfig,
    buf: Vec<u8>,
    output_handle: KcpOutputHandle,
}

/// An iterator that infinitely accepts connections on a `KcpListener`
pub struct Incoming {
    inner: KcpListener,
}

impl Stream for Incoming {
    type Item = (ServerKcpStream, SocketAddr);
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, io::Error> {
        Ok(Async::Ready(Some(try_nb!(self.inner.accept()))))
    }
}

impl KcpListener {
    fn from_udp_with_config(udp: UdpSocket, handle: &Handle, config: KcpConfig) -> io::Result<KcpListener> {
        KcpServerSessionUpdater::new(handle).map(|updater| {
            let shared_udp = Rc::new(udp);
            let output_handle = KcpOutputHandle::new(shared_udp.clone(), handle);

            KcpListener {
                udp: shared_udp,
                sessions: updater,
                handle: handle.clone(),
                config: config,
                buf: vec![0u8; config.mtu.unwrap_or(1400)],
                output_handle: output_handle,
            }
        })
    }
    /// Creates a new `KcpListener` which will be bound to the specific address.
    ///
    /// The returned listener is ready for accepting connections.
    pub fn bind_with_config(addr: &SocketAddr, handle: &Handle, config: KcpConfig) -> io::Result<KcpListener> {
        UdpSocket::bind(addr, handle).and_then(|udp| Self::from_udp_with_config(udp, handle, config))
    }

    /// Creates a new `KcpListener` which will be bound to the specific address with default config.
    ///
    /// The returned listener is ready for accepting connections.
    pub fn bind(addr: &SocketAddr, handle: &Handle) -> io::Result<KcpListener> {
        KcpListener::bind_with_config(addr, handle, KcpConfig::default())
    }

    /// Creates a new `KcpListener` from prepared std::net::UdpSocket with default config.
    ///
    /// The returned listener is ready for accepting connections.
    pub fn from_std_udp(udp: net::UdpSocket, handle: &Handle) -> io::Result<KcpListener> {
        UdpSocket::from_socket(udp, handle)
            .and_then(|udp| Self::from_udp_with_config(udp, handle, KcpConfig::default()))
    }

    /// Creates a new `KcpListener` from prepared std::net::UdpSocket.
    ///
    /// The returned listener is ready for accepting connections.
    pub fn from_std_udp_with_config(udp: net::UdpSocket,
                                    handle: &Handle,
                                    config: KcpConfig)
                                    -> io::Result<KcpListener> {
        UdpSocket::from_socket(udp, handle).and_then(|udp| Self::from_udp_with_config(udp, handle, config))
    }

    /// Returns the local socket address of this listener.
    pub fn local_addr(&self) -> io::Result<SocketAddr> {
        self.udp.local_addr()
    }

    /// Accept a new incoming connection from this listener.
    pub fn accept(&mut self) -> io::Result<(ServerKcpStream, SocketAddr)> {
        loop {
            let (size, addr) = self.udp.recv_from(&mut self.buf)?;

            let buf = &mut self.buf[..size];
            let mut conv = get_conv(&*buf);
            trace!("[RECV] size={} conv={} addr={} {:?}", size, conv, addr, ::debug::BsDebug(buf));

            if self.sessions.input_by_conv(conv, &addr, buf)? {
                continue;
            }

            trace!("[ACPT] Accepted connection {}", addr);

            // Set `conv` to 0 means let the server allocate a `conv` for it
            if conv == 0 {
                conv = self.sessions.get_free_conv();
                trace!("[ACPT] Allocated conv={} for {}", conv, addr);

                // Set to buffer
                set_conv(buf, conv);
            }

            let mut stream = ServerKcpStream::new_with_config(conv,
                                                              self.output_handle.clone(),
                                                              &addr,
                                                              &self.handle,
                                                              &mut self.sessions,
                                                              &self.config)?;

            // Input the initial packet
            stream.input(&*buf)?;

            return Ok((stream, addr));
        }
    }

    /// Returns an iterator over the connections being received on this listener.
    pub fn incoming(self) -> Incoming {
        Incoming { inner: self }
    }
}
<commit_msg>Stop updater while listener is dropping<commit_after>use std::io;
use std::net::{self, SocketAddr};
use std::rc::Rc;

use futures::{Async, Poll, Stream};
use kcp::{get_conv, set_conv};
use tokio_core::net::UdpSocket;
use tokio_core::reactor::Handle;

use config::KcpConfig;
use session::KcpServerSessionUpdater;
use skcp::KcpOutputHandle;
use stream::ServerKcpStream;

/// A KCP Socket server
pub struct KcpListener {
    udp: Rc<UdpSocket>,
    sessions: KcpServerSessionUpdater,
    handle: Handle,
    config: KcpConfig,
    buf: Vec<u8>,
    output_handle: KcpOutputHandle,
}

/// An iterator that infinitely accepts connections on a `KcpListener`
pub struct Incoming {
    inner: KcpListener,
}

impl Stream for Incoming {
    type Item = (ServerKcpStream, SocketAddr);
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, io::Error> {
        Ok(Async::Ready(Some(try_nb!(self.inner.accept()))))
    }
}

impl KcpListener {
    fn from_udp_with_config(udp: UdpSocket, handle: &Handle, config: KcpConfig) -> io::Result<KcpListener> {
        KcpServerSessionUpdater::new(handle).map(|updater| {
            let shared_udp = Rc::new(udp);
            let output_handle = KcpOutputHandle::new(shared_udp.clone(), handle);

            KcpListener {
                udp: shared_udp,
                sessions: updater,
                handle: handle.clone(),
                config: config,
                buf: vec![0u8; config.mtu.unwrap_or(1400)],
                output_handle: output_handle,
            }
        })
    }
    /// Creates a new `KcpListener` which will be bound to the specific address.
    ///
    /// The returned listener is ready for accepting connections.
    pub fn bind_with_config(addr: &SocketAddr, handle: &Handle, config: KcpConfig) -> io::Result<KcpListener> {
        UdpSocket::bind(addr, handle).and_then(|udp| Self::from_udp_with_config(udp, handle, config))
    }

    /// Creates a new `KcpListener` which will be bound to the specific address with default config.
    ///
    /// The returned listener is ready for accepting connections.
    pub fn bind(addr: &SocketAddr, handle: &Handle) -> io::Result<KcpListener> {
        KcpListener::bind_with_config(addr, handle, KcpConfig::default())
    }

    /// Creates a new `KcpListener` from prepared std::net::UdpSocket with default config.
    ///
    /// The returned listener is ready for accepting connections.
    pub fn from_std_udp(udp: net::UdpSocket, handle: &Handle) -> io::Result<KcpListener> {
        UdpSocket::from_socket(udp, handle)
            .and_then(|udp| Self::from_udp_with_config(udp, handle, KcpConfig::default()))
    }

    /// Creates a new `KcpListener` from prepared std::net::UdpSocket.
    ///
    /// The returned listener is ready for accepting connections.
    pub fn from_std_udp_with_config(udp: net::UdpSocket,
                                    handle: &Handle,
                                    config: KcpConfig)
                                    -> io::Result<KcpListener> {
        UdpSocket::from_socket(udp, handle).and_then(|udp| Self::from_udp_with_config(udp, handle, config))
    }

    /// Returns the local socket address of this listener.
    pub fn local_addr(&self) -> io::Result<SocketAddr> {
        self.udp.local_addr()
    }

    /// Accept a new incoming connection from this listener.
    pub fn accept(&mut self) -> io::Result<(ServerKcpStream, SocketAddr)> {
        loop {
            let (size, addr) = self.udp.recv_from(&mut self.buf)?;

            let buf = &mut self.buf[..size];
            let mut conv = get_conv(&*buf);
            trace!("[RECV] size={} conv={} addr={} {:?}", size, conv, addr, ::debug::BsDebug(buf));

            if self.sessions.input_by_conv(conv, &addr, buf)? {
                continue;
            }

            trace!("[ACPT] Accepted connection {}", addr);

            // Set `conv` to 0 means let the server allocate a `conv` for it
            if conv == 0 {
                conv = self.sessions.get_free_conv();
                trace!("[ACPT] Allocated conv={} for {}", conv, addr);

                // Set to buffer
                set_conv(buf, conv);
            }

            let mut stream = ServerKcpStream::new_with_config(conv,
                                                              self.output_handle.clone(),
                                                              &addr,
                                                              &self.handle,
                                                              &mut self.sessions,
                                                              &self.config)?;

            // Input the initial packet
            stream.input(&*buf)?;

            return Ok((stream, addr));
        }
    }

    /// Returns an iterator over the connections being received on this listener.
    pub fn incoming(self) -> Incoming {
        Incoming { inner: self }
    }
}

impl Drop for KcpListener {
    fn drop(&mut self) {
        self.sessions.stop();
    }
}
<|endoftext|>


<commit_before>//! Roundtrip (write-read) tests for supported LAS versions and attributes.

#[macro_use]
extern crate approx;
extern crate chrono;
extern crate las;
extern crate uuid;

use las::{Header, Point, Reader, Writer};
use std::io::Cursor;

pub fn roundtrip(header: Header, point: Point, should_succeed: bool) {
    let mut cursor = Cursor::new(Vec::new());
    {
        match Writer::new(&mut cursor, header.clone()).and_then(
            |mut writer| {
                writer.write(point.clone())
            },
        ) {
            Ok(()) => if !should_succeed {
                panic!("Expected write to fail, but it succeeded");
            },
            Err(err) => {
                if should_succeed {
                    panic!("Write error: {}", err)
                } else {
                    return;
                }
            }
        }
    }
    cursor.set_position(0);
    let mut reader = Reader::new(cursor).unwrap();
    let other = reader
        .read()
        .expect("Error when reading the ont point")
        .unwrap();
    assert_eq!(point, other);
    assert_eq!(
        None,
        reader.read().expect("Error when reading past last point")
    );

    let other = reader.header();
    assert_eq!(header.file_source_id, other.file_source_id);
    assert_eq!(header.gps_time_type, other.gps_time_type);
    assert_eq!(
        header.has_synthetic_return_numbers,
        other.has_synthetic_return_numbers
    );
    assert_eq!(header.guid, other.guid);
    assert_eq!(header.version, other.version);
    assert_eq!(header.system_identifier, other.system_identifier);
    assert_eq!(header.generating_software, other.generating_software);
    assert_eq!(header.date, other.date);
    assert_eq!(header.padding, other.padding);
    assert_eq!(header.point_format, other.point_format);
    assert_eq!(header.transforms, other.transforms);
    assert_relative_eq!(point.x, other.bounds.min.x);
    assert_relative_eq!(point.x, other.bounds.max.x);
    assert_relative_eq!(point.y, other.bounds.min.y);
    assert_relative_eq!(point.y, other.bounds.max.y);
    assert_relative_eq!(point.z, other.bounds.min.z);
    assert_relative_eq!(point.z, other.bounds.max.z);
    assert_eq!(1, other.number_of_points);
    if point.return_number > 0 {
        assert_eq!(1, other.number_of_points_by_return[&point.return_number]);
    }
    assert_eq!(
        header.vlrs().collect::<Vec<_>>(),
        other.vlrs().collect::<Vec<_>>()
    );
    assert_eq!(
        header.evlrs().collect::<Vec<_>>(),
        other.evlrs().collect::<Vec<_>>()
    );
}

macro_rules! roundtrip_point {
    ($name:ident, $modify_point:expr) => {
        roundtrip_point!($name, $modify_point, 0);
    };
    ($name:ident, $modify_point:expr, $min_version_minor:expr) => {
        roundtrip_point!($name, $modify_point, $min_version_minor, |_f| {});
    };
    ($name:ident, $modify_point:expr, $min_version_minor:expr, $modify_point_format:expr) => {
        #[test]
        fn $name() {
            use las::{Header, Point, Version};
            use las::point::Format;

            let version = super::version();
            let should_succeed = version >= Version::new(1, $min_version_minor);
            let mut point_format = Format::default();
            $modify_point_format(&mut point_format);
            let mut point = Point::default();
            $modify_point(&mut point);
            let mut header = Header::default();
            header.version = version;
            header.point_format = point_format;
            ::roundtrip(header, point, should_succeed);
        }
    };
}

macro_rules! roundtrip_header {
    ($name:ident, $modify_header:expr) => {
        roundtrip_header!($name, $modify_header, 0);
    };
    ($name:ident, $modify_header:expr, $min_version_minor:expr) => {
        #[test]
        fn $name() {
            use las::{Version, Header, Point};

            let version = super::version();
            let should_succeed = version >= Version::new(1, $min_version_minor);
            let mut header = Header::default();
            header.version = version;
            $modify_header(&mut header);
            ::roundtrip(header, Point::default(), should_succeed);
        }
    };
}

macro_rules! version { ($name:ident, $major:expr, $minor:expr) => {
mod $name {
    use las::Version;

    fn version() -> Version {
        Version::new($major, $minor)
    }

    mod point {
        use las::Color;
        use las::point::{Classification, ScanDirection};

        roundtrip_point!(xyz, |p: &mut Point| { p.x = 1.; p.y = 2.; p.z = 3.; });
        roundtrip_point!(intensity, |p: &mut Point| p.intensity = 42);
        roundtrip_point!(return_number, |p: &mut Point| p.return_number = 2);
        roundtrip_point!(number_of_returns, |p: &mut Point| p.number_of_returns = 2);
        roundtrip_point!(scan_direction, |p: &mut Point| p.scan_direction = ScanDirection::LeftToRight);
        roundtrip_point!(is_edge_of_flight_line, |p: &mut Point| p.is_edge_of_flight_line = true);
        roundtrip_point!(classification, |p: &mut Point| p.classification = Classification::Ground);
        roundtrip_point!(is_synthetic, |p: &mut Point| p.is_synthetic = true);
        roundtrip_point!(is_key_point, |p: &mut Point| p.is_key_point = true);
        roundtrip_point!(is_withheld, |p: &mut Point| p.is_withheld = true);
        roundtrip_point!(is_overlap, |p: &mut Point| {
            p.classification = Classification::Unclassified;
            p.is_overlap = true;
        });
        roundtrip_point!(scanner_channel, |p: &mut Point| {
            p.scanner_channel = 1;
            p.gps_time = Some(42.);
        }, 4, |f: &mut Format| f.extend());
        roundtrip_point!(scan_angle_rank, |p: &mut Point| p.scan_angle = 3.);
        roundtrip_point!(user_data, |p: &mut Point| p.user_data = 42);
        roundtrip_point!(point_source_id, |p: &mut Point| p.point_source_id = 42);
        roundtrip_point!(gps_time, |p: &mut Point| p.gps_time = Some(42.), 0, |f: &mut Format| f.has_gps_time = true);
        roundtrip_point!(color, |p: &mut Point| p.color = Some(Color { red: 1, green: 2, blue: 3 }), 2, |f: &mut Format| f.has_color = true);
        // TODO waveform
        roundtrip_point!(nir, |p: &mut Point| {
            p.color = Some(Color { red: 1, green: 2, blue: 3});
            p.nir = Some(42);
            p.gps_time = Some(42.);
        }, 4, |f: &mut Format| {
            f.extend();
            f.has_color = true;
            f.has_nir = true;
        });
        roundtrip_point!(extra_bytes, |p: &mut Point| p.extra_bytes = vec![42], 0, |f: &mut Format| f.extra_bytes = 1);
    }

    mod header {
        use chrono::{Utc, TimeZone};
        use las::GpsTimeType;
        use uuid::Uuid;

        roundtrip_header!(file_source_id, |h: &mut Header| h.file_source_id = 42, 1);
        roundtrip_header!(gps_time_type, |h: &mut Header| h.gps_time_type = GpsTimeType::Standard, 2);
        roundtrip_header!(has_synthetic_return_numbers, |h: &mut Header| h.has_synthetic_return_numbers = true, 3);
        roundtrip_header!(guid, |h: &mut Header| h.guid = Uuid::from_bytes(&[42; 16]).unwrap());
        roundtrip_header!(system_identifier, |h: &mut Header| h.system_identifier = "roundtrip test".to_string());
        roundtrip_header!(generating_software, |h: &mut Header| h.generating_software = "roundtrip test".to_string());
        roundtrip_header!(date, |h: &mut Header| h.date = Some(Utc.ymd(2017, 10, 30)));
        roundtrip_header!(padding, |h: &mut Header| h.padding = b"You probably shouldn't do this".to_vec());
        roundtrip_header!(vlr_padding, |h: &mut Header| h.vlr_padding = b"You probably shouldn't do this either".to_vec());
        roundtrip_header!(transforms, |h: &mut Header| {
            use las::{Transform, Vector};

            let transform = Transform { scale: 0.1, offset: -1. };
            h.transforms = Vector {
                x: transform,
                y: transform,
                z: transform,
            };
        });
        roundtrip_header!(vlrs, |h: &mut Header| h.push_vlr(Default::default()));
        roundtrip_header!(evlrs, |h: &mut Header| {
            use std::u16;
            use las::Vlr;

            let mut vlr = Vlr::default();
            vlr.is_extended = true;
            vlr.data = vec![42; u16::MAX as usize + 1];
            h.push_vlr(vlr);
        }, 4);
        roundtrip_header!(end_of_points_padding, |h: &mut Header| h.end_of_points_padding = vec![42]);
    }
}
}}

version!(las_1_0, 1, 0);
version!(las_1_1, 1, 1);
version!(las_1_2, 1, 2);
version!(las_1_3, 1, 3);
version!(las_1_4, 1, 4);
<commit_msg>Pass by reference in roundtrip<commit_after>//! Roundtrip (write-read) tests for supported LAS versions and attributes.

#[macro_use]
extern crate approx;
extern crate chrono;
extern crate las;
extern crate uuid;

use las::{Header, Point, Reader, Writer};
use std::io::Cursor;

pub fn roundtrip(header: &Header, point: &Point, should_succeed: bool) {
    let mut cursor = Cursor::new(Vec::new());
    {
        match Writer::new(&mut cursor, header.clone()).and_then(
            |mut writer| {
                writer.write(point.clone())
            },
        ) {
            Ok(()) => if !should_succeed {
                panic!("Expected write to fail, but it succeeded");
            },
            Err(err) => {
                if should_succeed {
                    panic!("Write error: {}", err)
                } else {
                    return;
                }
            }
        }
    }
    cursor.set_position(0);
    let mut reader = Reader::new(cursor).unwrap();
    let other = reader
        .read()
        .expect("Error when reading the ont point")
        .unwrap();
    assert_eq!(point, &other);
    assert_eq!(
        None,
        reader.read().expect("Error when reading past last point")
    );

    let other = reader.header();
    assert_eq!(header.file_source_id, other.file_source_id);
    assert_eq!(header.gps_time_type, other.gps_time_type);
    assert_eq!(
        header.has_synthetic_return_numbers,
        other.has_synthetic_return_numbers
    );
    assert_eq!(header.guid, other.guid);
    assert_eq!(header.version, other.version);
    assert_eq!(header.system_identifier, other.system_identifier);
    assert_eq!(header.generating_software, other.generating_software);
    assert_eq!(header.date, other.date);
    assert_eq!(header.padding, other.padding);
    assert_eq!(header.point_format, other.point_format);
    assert_eq!(header.transforms, other.transforms);
    assert_relative_eq!(point.x, other.bounds.min.x);
    assert_relative_eq!(point.x, other.bounds.max.x);
    assert_relative_eq!(point.y, other.bounds.min.y);
    assert_relative_eq!(point.y, other.bounds.max.y);
    assert_relative_eq!(point.z, other.bounds.min.z);
    assert_relative_eq!(point.z, other.bounds.max.z);
    assert_eq!(1, other.number_of_points);
    if point.return_number > 0 {
        assert_eq!(1, other.number_of_points_by_return[&point.return_number]);
    }
    assert_eq!(
        header.vlrs().collect::<Vec<_>>(),
        other.vlrs().collect::<Vec<_>>()
    );
    assert_eq!(
        header.evlrs().collect::<Vec<_>>(),
        other.evlrs().collect::<Vec<_>>()
    );
}

macro_rules! roundtrip_point {
    ($name:ident, $modify_point:expr) => {
        roundtrip_point!($name, $modify_point, 0);
    };
    ($name:ident, $modify_point:expr, $min_version_minor:expr) => {
        roundtrip_point!($name, $modify_point, $min_version_minor, |_f| {});
    };
    ($name:ident, $modify_point:expr, $min_version_minor:expr, $modify_point_format:expr) => {
        #[test]
        fn $name() {
            use las::{Header, Point, Version};
            use las::point::Format;

            let version = super::version();
            let should_succeed = version >= Version::new(1, $min_version_minor);
            let mut point_format = Format::default();
            $modify_point_format(&mut point_format);
            let mut point = Point::default();
            $modify_point(&mut point);
            let mut header = Header::default();
            header.version = version;
            header.point_format = point_format;
            ::roundtrip(&header, &point, should_succeed);
        }
    };
}

macro_rules! roundtrip_header {
    ($name:ident, $modify_header:expr) => {
        roundtrip_header!($name, $modify_header, 0);
    };
    ($name:ident, $modify_header:expr, $min_version_minor:expr) => {
        #[test]
        fn $name() {
            use las::{Version, Header, Point};

            let version = super::version();
            let should_succeed = version >= Version::new(1, $min_version_minor);
            let mut header = Header::default();
            header.version = version;
            $modify_header(&mut header);
            ::roundtrip(&header, &Point::default(), should_succeed);
        }
    };
}

macro_rules! version { ($name:ident, $major:expr, $minor:expr) => {
mod $name {
    use las::Version;

    fn version() -> Version {
        Version::new($major, $minor)
    }

    mod point {
        use las::Color;
        use las::point::{Classification, ScanDirection};

        roundtrip_point!(xyz, |p: &mut Point| { p.x = 1.; p.y = 2.; p.z = 3.; });
        roundtrip_point!(intensity, |p: &mut Point| p.intensity = 42);
        roundtrip_point!(return_number, |p: &mut Point| p.return_number = 2);
        roundtrip_point!(number_of_returns, |p: &mut Point| p.number_of_returns = 2);
        roundtrip_point!(scan_direction, |p: &mut Point| p.scan_direction = ScanDirection::LeftToRight);
        roundtrip_point!(is_edge_of_flight_line, |p: &mut Point| p.is_edge_of_flight_line = true);
        roundtrip_point!(classification, |p: &mut Point| p.classification = Classification::Ground);
        roundtrip_point!(is_synthetic, |p: &mut Point| p.is_synthetic = true);
        roundtrip_point!(is_key_point, |p: &mut Point| p.is_key_point = true);
        roundtrip_point!(is_withheld, |p: &mut Point| p.is_withheld = true);
        roundtrip_point!(is_overlap, |p: &mut Point| {
            p.classification = Classification::Unclassified;
            p.is_overlap = true;
        });
        roundtrip_point!(scanner_channel, |p: &mut Point| {
            p.scanner_channel = 1;
            p.gps_time = Some(42.);
        }, 4, |f: &mut Format| f.extend());
        roundtrip_point!(scan_angle_rank, |p: &mut Point| p.scan_angle = 3.);
        roundtrip_point!(user_data, |p: &mut Point| p.user_data = 42);
        roundtrip_point!(point_source_id, |p: &mut Point| p.point_source_id = 42);
        roundtrip_point!(gps_time, |p: &mut Point| p.gps_time = Some(42.), 0, |f: &mut Format| f.has_gps_time = true);
        roundtrip_point!(color, |p: &mut Point| p.color = Some(Color { red: 1, green: 2, blue: 3 }), 2, |f: &mut Format| f.has_color = true);
        // TODO waveform
        roundtrip_point!(nir, |p: &mut Point| {
            p.color = Some(Color { red: 1, green: 2, blue: 3});
            p.nir = Some(42);
            p.gps_time = Some(42.);
        }, 4, |f: &mut Format| {
            f.extend();
            f.has_color = true;
            f.has_nir = true;
        });
        roundtrip_point!(extra_bytes, |p: &mut Point| p.extra_bytes = vec![42], 0, |f: &mut Format| f.extra_bytes = 1);
    }

    mod header {
        use chrono::{Utc, TimeZone};
        use las::GpsTimeType;
        use uuid::Uuid;

        roundtrip_header!(file_source_id, |h: &mut Header| h.file_source_id = 42, 1);
        roundtrip_header!(gps_time_type, |h: &mut Header| h.gps_time_type = GpsTimeType::Standard, 2);
        roundtrip_header!(has_synthetic_return_numbers, |h: &mut Header| h.has_synthetic_return_numbers = true, 3);
        roundtrip_header!(guid, |h: &mut Header| h.guid = Uuid::from_bytes(&[42; 16]).unwrap());
        roundtrip_header!(system_identifier, |h: &mut Header| h.system_identifier = "roundtrip test".to_string());
        roundtrip_header!(generating_software, |h: &mut Header| h.generating_software = "roundtrip test".to_string());
        roundtrip_header!(date, |h: &mut Header| h.date = Some(Utc.ymd(2017, 10, 30)));
        roundtrip_header!(padding, |h: &mut Header| h.padding = b"You probably shouldn't do this".to_vec());
        roundtrip_header!(vlr_padding, |h: &mut Header| h.vlr_padding = b"You probably shouldn't do this either".to_vec());
        roundtrip_header!(transforms, |h: &mut Header| {
            use las::{Transform, Vector};

            let transform = Transform { scale: 0.1, offset: -1. };
            h.transforms = Vector {
                x: transform,
                y: transform,
                z: transform,
            };
        });
        roundtrip_header!(vlrs, |h: &mut Header| h.push_vlr(Default::default()));
        roundtrip_header!(evlrs, |h: &mut Header| {
            use std::u16;
            use las::Vlr;

            let mut vlr = Vlr::default();
            vlr.is_extended = true;
            vlr.data = vec![42; u16::MAX as usize + 1];
            h.push_vlr(vlr);
        }, 4);
        roundtrip_header!(end_of_points_padding, |h: &mut Header| h.end_of_points_padding = vec![42]);
    }
}
}}

version!(las_1_0, 1, 0);
version!(las_1_1, 1, 1);
version!(las_1_2, 1, 2);
version!(las_1_3, 1, 3);
version!(las_1_4, 1, 4);
<|endoftext|>


<commit_before>#![cfg(test)]

use glib::translate::*;
use libc;
use std::env;
use std::path::PathBuf;

#[cfg(have_pangoft2)]

/// Given a filename from `test_generator::test_resources`, computes the correct fixture filename.
///
/// The `test_resources` procedural macro works by running a filename glob starting on
/// the toplevel of the Cargo workspace.  However, when a test function gets run,
/// its $cwd is the test crate's toplevel.  This function fixes the pathname generated
/// by `test_resources` so that it has the correct path.
pub fn fixture_path(filename_from_test_resources: &str) -> PathBuf {
    let crate_toplevel = PathBuf::from(
        env::var_os("CARGO_MANIFEST_DIR")
            .expect(r#"CARGO_MANIFEST_DIR" is not set, please set it or run under "cargo test""#),
    );

    let workspace_toplevel = crate_toplevel.parent().unwrap();

    workspace_toplevel.join(filename_from_test_resources)
}

#[cfg(have_pangoft2)]
pub fn setup_font_map() {
    use fontconfig_sys::fontconfig;
    use glib::prelude::*;
    use pangocairo::FontMap;

    unsafe {
        let font_paths = [
            "tests/resources/Roboto-Regular.ttf",
            "tests/resources/Roboto-Italic.ttf",
            "tests/resources/Roboto-Bold.ttf",
            "tests/resources/Roboto-BoldItalic.ttf",
        ];

        let config = fontconfig::FcConfigCreate();

        for path in &font_paths {
            let path = fixture_path(path);
            let str = path.to_str().unwrap();

            if fontconfig::FcConfigAppFontAddFile(config, str.as_ptr()) == 0 {
                panic!("Could not load font file {} for tests; aborting", str);
            }
        }

        let font_map = FontMap::new_for_font_type(cairo::FontType::FontTypeFt).unwrap();
        let raw_font_map: *mut pango_sys::PangoFontMap = font_map.to_glib_none().0;

        extern "C" {
            // pango_fc_font_map_set_config (PangoFcFontMap *fcfontmap,
	    //                               FcConfig       *fcconfig);
            // This is not bound in gtk-rs, and PangoFcFontMap is not even exposed, so we'll bind it by hand.
            fn pango_fc_font_map_set_config(
                font_map: *mut libc::c_void,
                config: *mut fontconfig::FcConfig,
            );
        }

        pango_fc_font_map_set_config (raw_font_map as *mut _, config);
        fontconfig::FcConfigDestroy(config);

        FontMap::set_default(Some(font_map.downcast::<pangocairo::FontMap>().unwrap()));
    }
}

#[cfg(not(have_pangoft2))]
pub fn setup_font_map() {}
<commit_msg>Move all the unsafe stuff to a submodule<commit_after>#![cfg(test)]

use glib::translate::*;
use libc;
use std::env;
use std::path::PathBuf;

#[cfg(have_pangoft2)]

/// Given a filename from `test_generator::test_resources`, computes the correct fixture filename.
///
/// The `test_resources` procedural macro works by running a filename glob starting on
/// the toplevel of the Cargo workspace.  However, when a test function gets run,
/// its $cwd is the test crate's toplevel.  This function fixes the pathname generated
/// by `test_resources` so that it has the correct path.
pub fn fixture_path(filename_from_test_resources: &str) -> PathBuf {
    let crate_toplevel = PathBuf::from(
        env::var_os("CARGO_MANIFEST_DIR")
            .expect(r#"CARGO_MANIFEST_DIR" is not set, please set it or run under "cargo test""#),
    );

    let workspace_toplevel = crate_toplevel.parent().unwrap();

    workspace_toplevel.join(filename_from_test_resources)
}

#[cfg(have_pangoft2)]
mod pango_ft2 {
    use fontconfig_sys::fontconfig;
    use glib::prelude::*;
    use pangocairo::FontMap;
    use super::*;

    extern "C" {
        // pango_fc_font_map_set_config (PangoFcFontMap *fcfontmap,
	//                               FcConfig       *fcconfig);
        // This is not bound in gtk-rs, and PangoFcFontMap is not even exposed, so we'll bind it by hand.
        fn pango_fc_font_map_set_config(
            font_map: *mut libc::c_void,
            config: *mut fontconfig::FcConfig,
        );
    }

    pub unsafe fn load_test_fonts() {
        let font_paths = [
            "tests/resources/Roboto-Regular.ttf",
            "tests/resources/Roboto-Italic.ttf",
            "tests/resources/Roboto-Bold.ttf",
            "tests/resources/Roboto-BoldItalic.ttf",
        ];

        let config = fontconfig::FcConfigCreate();

        for path in &font_paths {
            let path = fixture_path(path);
            let str = path.to_str().unwrap();

            if fontconfig::FcConfigAppFontAddFile(config, str.as_ptr()) == 0 {
                panic!("Could not load font file {} for tests; aborting", str);
            }
        }

        let font_map = FontMap::new_for_font_type(cairo::FontType::FontTypeFt).unwrap();
        let raw_font_map: *mut pango_sys::PangoFontMap = font_map.to_glib_none().0;

        pango_fc_font_map_set_config (raw_font_map as *mut _, config);
        fontconfig::FcConfigDestroy(config);

        FontMap::set_default(Some(font_map.downcast::<pangocairo::FontMap>().unwrap()));
    }
}

#[cfg(have_pangoft2)]
pub fn setup_font_map() {
    unsafe { self::pango_ft2::load_test_fonts(); }
}

#[cfg(not(have_pangoft2))]
pub fn setup_font_map() {}
<|endoftext|>


<commit_before>extern crate regex;

use self::regex::Regex;
use std::process::Command;

pub struct WindowsVer {
    pub version: Option<String>
}

pub fn parse(output: String) -> WindowsVer {
    let version_regex = Regex::new(r"^Microsoft Windows \[Version\s(\d+\.\d+\.\d+)\]$").unwrap();

    let version = match version_regex.captures_iter(&output).next() {
        Some(m) => {
            match m.at(1) {
                Some(version) => Some(version.to_string()),
                None => None
            }
        },
        None => None
    };
    WindowsVer { version: version }
}
<commit_msg>Add retrieve method for windows_ver<commit_after>extern crate regex;

use self::regex::Regex;
use std::process::Command;

pub struct WindowsVer {
    pub version: Option<String>
}

pub fn retrieve() -> Option<WindowsVer> {
    let output = match Command::new("ver").output() {
        Ok(o) => o,
        Err(_) => return None
    };
    let stdout = String::from_utf8_lossy(&output.stdout);
    Some(parse(stdout.to_string()))
}

pub fn parse(output: String) -> WindowsVer {
    let version_regex = Regex::new(r"^Microsoft Windows \[Version\s(\d+\.\d+\.\d+)\]$").unwrap();

    let version = match version_regex.captures_iter(&output).next() {
        Some(m) => {
            match m.at(1) {
                Some(version) => Some(version.to_string()),
                None => None
            }
        },
        None => None
    };
    WindowsVer { version: version }
}
<|endoftext|>


<commit_before>extern crate tar;
extern crate flate2;

use std::fs::File;
use std::io::{BufReader, Read, Write};
use std::path::Path;

use self::tar::Archive;
use self::flate2::read::GzDecoder;

fn extract_tar(input: &Path, output: &Path) {
    let file = File::open(input).unwrap();
    let mut archive = Archive::new(file);
    archive.unpack(output).unwrap();
}

fn extract_gz(input: &Path, output: &Path) {
    let file = File::open(input).unwrap();
    let buffer = BufReader::new(file);
    let archive = GzDecoder::new(buffer).unwrap();

    let mut buffer = File::create(output).unwrap();
    for byte in archive.bytes() {
        buffer.write(&[byte.unwrap()]);
    }
}

<commit_msg>Proper variable naming<commit_after>extern crate tar;
extern crate flate2;

use std::fs::File;
use std::io::{BufReader, Read, Write};
use std::path::Path;

use self::tar::Archive;
use self::flate2::read::GzDecoder;

fn extract_tar(input: &Path, output: &Path) {
    let file = File::open(input).unwrap();
    let mut archive = Archive::new(file);
    archive.unpack(output).unwrap();
}

fn extract_gz(input: &Path, output: &Path) {
    let file = File::open(input).unwrap();
    let buffer = BufReader::new(file);
    let archive = GzDecoder::new(buffer).unwrap();

    let mut target = File::create(output).unwrap();
    for byte in archive.bytes() {
        target.write(&[byte.unwrap()]);
    }
}

<|endoftext|>


<commit_before>/*
    Contribuidores
		- Heitor582
        - Dromedario de Chapéu

    Fatorial é uma função matematica que consistem em realizar
	a multiplicação de todos os antecessores de um numero.

	Ex: 5! = 5 * 4 * 3 * 2 * 1 = 120
*/

// Para realizar uma fatoração com recursão basta fazer o retorno
// de uma função ser valor * a propia função recebendo valor - 1
fn fatorial(valor: u128) -> u128 {
	// Para que não chege a multiplicar por 0 quando chegamos a 1 ou 0 é
	// retornado 1 para que o utlimo valor não sejá zerado ao multiplicar por zero
    match valor {
        0 | 1  => 1,
        2.. => valor * (fatorial(valor - 1)),
    }
}

fn main() {
    println!("{}", fatorial(21));
}

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn teste_fatorial() {
        assert_eq!(fatorial(0), 1);
        assert_eq!(fatorial(1), 1);
        assert_eq!(fatorial(10), 3628800);
    }
}<commit_msg>Update fatorial_recursiva.rs<commit_after>/*
    Contribuidores
        - Heitor582
        - Dromedario de Chapéu

    Fatorial é uma função matematica que consistem em realizar
	a multiplicação de todos os antecessores de um numero.

	Ex: 5! = 5 * 4 * 3 * 2 * 1 = 120
*/

// Para realizar uma fatoração com recursão basta fazer o retorno
// de uma função ser valor * a propia função recebendo valor - 1
fn fatorial(valor: u128) -> u128 {
    // Para que não chege a multiplicar por 0 quando chegamos a 1 ou 0 é
    // retornado 1 para que o utlimo valor não sejá zerado ao multiplicar por zero
    match valor {
        0 | 1  => 1,
        2.. => valor * (fatorial(valor - 1)),
    }
}

fn main() {
    println!("{}", fatorial(21));
}

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn teste_fatorial() {
        assert_eq!(fatorial(0), 1);
        assert_eq!(fatorial(1), 1);
        assert_eq!(fatorial(10), 3628800);
    }
}
<|endoftext|>