<commit_before>use std::net::{SocketAddrV4, SocketAddrV6};
use std::sync::MutexGuard;
use std::collections::HashMap;

use tracker::torrent::{Stats, Peers, Torrent};

pub struct Announce {
    pub info_hash: String,
    pub peer_id: String,
    pub ipv4: Option<SocketAddrV4>,
    pub ipv6: Option<SocketAddrV6>,
    pub ul: u64,
    pub dl: u64,
    pub left: u64,
    pub action: Action,
    pub numwant: u8,
    pub compact: bool,
}

#[derive(Clone)]
pub enum Action {
    Seeding,
    Leeching,
    Completed,
    Stopped,
}

pub struct AnnouncePeer {
    pub id: String,
    pub ipv4: Option<SocketAddrV4>,
    pub ipv6: Option<SocketAddrV6>,
}

pub struct AnnounceResponse {
    peers: Peers,
    stats: Stats,
    compact: bool
}

impl AnnouncePeer {
    pub fn get_ipv4_bytes(&self) -> Option<Vec<u8>> {
        match self.ipv4 {
            None => None,
            Some(sock) => {
                let mut v = Vec::with_capacity(6);
                v.extend(sock.ip().octets().to_vec());
                v.extend(u16_to_u8(sock.port()));
                Some(v)
            }
        }
    }

    pub fn get_ipv4_str(&self) -> Option<String> {
        match self.ipv4 {
            None => None,
            Some(sock) => {
                Some(format!("{}", sock.ip()))
            }
        }
    }

    pub fn get_ipv6_bytes(&self) -> Option<Vec<u8>> {
        match self.ipv6 {
            None => None,
            Some(sock) => {
                let mut v = Vec::with_capacity(18);
                for seg in sock.ip().segments().iter() {
                    v.extend(u16_to_u8(seg.clone()));
                }
                v.extend(u16_to_u8(sock.port()));
                Some(v)
            }
        }
    }

    pub fn get_ipv6_str(&self) -> Option<String> {
        match self.ipv6 {
            None => None,
            Some(sock) => {
                Some(format!("{}", sock.ip()))
            }
        }
    }
}

fn u16_to_u8(i: u16) -> Vec<u8> {
    vec![(i >> 8) as u8, (i & 0xff) as u8]
}

impl AnnounceResponse {
    pub fn new(peers: Peers, stats: Stats, compact: bool) -> AnnounceResponse {
        AnnounceResponse {
            peers: peers,
            stats: stats,
            compact: compact,
        }
    }

    pub fn peers(&self) -> &Peers {
        &self.peers
    }

    pub fn stats(&self) -> &Stats {
        &self.stats
    }

    pub fn compact(&self) -> bool {
        self.compact
    }
}
<commit_msg>Remove unused imports<commit_after>use std::net::{SocketAddrV4, SocketAddrV6};

use tracker::torrent::{Stats, Peers};

pub struct Announce {
    pub info_hash: String,
    pub peer_id: String,
    pub ipv4: Option<SocketAddrV4>,
    pub ipv6: Option<SocketAddrV6>,
    pub ul: u64,
    pub dl: u64,
    pub left: u64,
    pub action: Action,
    pub numwant: u8,
    pub compact: bool,
}

#[derive(Clone)]
pub enum Action {
    Seeding,
    Leeching,
    Completed,
    Stopped,
}

pub struct AnnouncePeer {
    pub id: String,
    pub ipv4: Option<SocketAddrV4>,
    pub ipv6: Option<SocketAddrV6>,
}

pub struct AnnounceResponse {
    peers: Peers,
    stats: Stats,
    compact: bool
}

impl AnnouncePeer {
    pub fn get_ipv4_bytes(&self) -> Option<Vec<u8>> {
        match self.ipv4 {
            None => None,
            Some(sock) => {
                let mut v = Vec::with_capacity(6);
                v.extend(sock.ip().octets().to_vec());
                v.extend(u16_to_u8(sock.port()));
                Some(v)
            }
        }
    }

    pub fn get_ipv4_str(&self) -> Option<String> {
        match self.ipv4 {
            None => None,
            Some(sock) => {
                Some(format!("{}", sock.ip()))
            }
        }
    }

    pub fn get_ipv6_bytes(&self) -> Option<Vec<u8>> {
        match self.ipv6 {
            None => None,
            Some(sock) => {
                let mut v = Vec::with_capacity(18);
                for seg in sock.ip().segments().iter() {
                    v.extend(u16_to_u8(seg.clone()));
                }
                v.extend(u16_to_u8(sock.port()));
                Some(v)
            }
        }
    }

    pub fn get_ipv6_str(&self) -> Option<String> {
        match self.ipv6 {
            None => None,
            Some(sock) => {
                Some(format!("{}", sock.ip()))
            }
        }
    }
}

fn u16_to_u8(i: u16) -> Vec<u8> {
    vec![(i >> 8) as u8, (i & 0xff) as u8]
}

impl AnnounceResponse {
    pub fn new(peers: Peers, stats: Stats, compact: bool) -> AnnounceResponse {
        AnnounceResponse {
            peers: peers,
            stats: stats,
            compact: compact,
        }
    }

    pub fn peers(&self) -> &Peers {
        &self.peers
    }

    pub fn stats(&self) -> &Stats {
        &self.stats
    }

    pub fn compact(&self) -> bool {
        self.compact
    }
}
<|endoftext|>


<commit_before>//! Traits giving structural informations on linear algebra objects or the space they live in.

use std::num::Zero;
use std::vec::{VecIterator, VecMutIterator};
use traits::operations::{RMul, LMul, ScalarAdd, ScalarSub};
use traits::geometry::{Dot, Norm, UniformSphereSample};

/// Traits of objects which can be created from an object of type `T`.
pub trait Cast<T> {
    /// Converts an element of type `T` to an element of type `Self`.
    fn from(t: T) -> Self;
}

/// Trait of matrices.
///
/// A matrix has rows and columns and are able to multiply them.
pub trait Mat<R, C> : Row<R> + Col<C> + RMul<R> + LMul<C> { }

impl<M: Row<R> + Col<C> + RMul<R> + LMul<C>, R, C> Mat<R, C> for M {
}

// XXX: we keep ScalarAdd and ScalarSub here to avoid trait impl conflict (overriding) between the
// different Add/Sub traits. This is _so_ unfortunate…

/// Trait grouping most common operations on vectors.
pub trait Vec<N>: Dim + Sub<Self, Self> + Add<Self, Self> + Neg<Self> + Zero + Eq + Mul<N, Self>
                  + Div<N, Self> + Dot<N> {
}

/// Trait of vector with components implementing the `Real` trait.
pub trait RealVec<N: Real>: Vec<N> + Norm<N> {
}

/// Trait grouping uncommon, low-level and borderline (from the mathematical point of view)
/// operations on vectors.
pub trait VecExt<N>: Vec<N> + Indexable<uint, N> + Iterable<N> +
                     UniformSphereSample + ScalarAdd<N> + ScalarSub<N> + Bounded + Orderable
{ }

/// Trait grouping uncommon, low-level and borderline (from the mathematical point of view)
/// operations on vectors.
pub trait RealVecExt<N: Real>: RealVec<N> + VecExt<N> + Basis + Round { }

impl<N, V: Dim + Sub<V, V> + Add<V, V> + Neg<V> + Zero + Eq + Mul<N, V> + Div<N, V> + Dot<N>>
Vec<N> for V { }

impl<N: Real, V: Vec<N> + Norm<N>> RealVec<N> for V { }

impl<N,
     V: Vec<N> + Indexable<uint, N> + Iterable<N> +
        UniformSphereSample + ScalarAdd<N> + ScalarSub<N> + Bounded + Orderable>
VecExt<N> for V { }

impl<N: Real, V: RealVec<N> + VecExt<N> + Basis + Round> RealVecExt<N> for V { }

// FIXME: return an iterator instead
/// Traits of objects which can form a basis (typically vectors).
pub trait Basis {
    /// Iterates through the canonical basis of the space in which this object lives.
    fn canonical_basis(|Self| -> bool);

    /// Iterates through a basis of the subspace orthogonal to `self`.
    fn orthonormal_subspace_basis(&Self, |Self| -> bool);
}

/// Trait to access rows of a matrix or a vector.
pub trait Row<R> {
    /// The number of column of `self`.
    fn nrows(&self) -> uint;
    /// Reads the `i`-th row of `self`.
    fn row(&self, i: uint) -> R;
    /// Writes the `i`-th row of `self`.
    fn set_row(&mut self, i: uint, R);

    // FIXME: add iterators on rows: this could be a very good way to generalize _and_ optimize
    // a lot of operations.
}

/// Trait to access columns of a matrix or vector.
pub trait Col<C> {
    /// The number of column of this matrix or vector.
    fn ncols(&self) -> uint;

    /// Reads the `i`-th column of `self`.
    fn col(&self, i: uint) -> C;

    /// Writes the `i`-th column of `self`.
    fn set_col(&mut self, i: uint, C);

    // FIXME: add iterators on columns: this could be a very good way to generalize _and_ optimize
    // a lot of operations.
}

/// Trait of objects having a spacial dimension known at compile time.
pub trait Dim {
    /// The dimension of the object.
    fn dim(unused_self: Option<Self>) -> uint;
}

// FIXME: this trait should not be on nalgebra.
// however, it is needed because std::ops::Index is (strangely) to poor: it
// does not have a function to set values.
// Also, using Index with tuples crashes.
/// This is a workaround of current Rust limitations.
///
/// It exists because the `Index` trait cannot be used to express write access.
/// Thus, this is the same as the `Index` trait but without the syntactic sugar and with a method
/// to write to a specific index.
pub trait Indexable<Index, Res> {
    /// Reads the `i`-th element of `self`.
    fn at(&self, i: Index) -> Res;
    /// Writes to the `i`-th element of `self`.
    fn set(&mut self, i: Index, Res);
    /// Swaps the `i`-th element of `self` with its `j`-th element.
    fn swap(&mut self, i: Index, j: Index);

    /// Reads the `i`-th element of `self`.
    ///
    /// `i` is not checked.
    unsafe fn unsafe_at(&self, i: Index) -> Res;
    /// Writes to the `i`-th element of `self`.
    ///
    /// `i` is not checked.
    unsafe fn unsafe_set(&mut self, i: Index, Res);
}

/// This is a workaround of current Rust limitations.
///
/// Traits of objects which can be iterated through like a vector.
pub trait Iterable<N> {
    /// Gets a vector-like read-only iterator.
    fn iter<'l>(&'l self) -> VecIterator<'l, N>;
}

/// This is a workaround of current Rust limitations.
///
/// Traits of mutable objects which can be iterated through like a vector.
pub trait IterableMut<N> {
    /// Gets a vector-like read-write iterator.
    fn mut_iter<'l>(&'l mut self) -> VecMutIterator<'l, N>;
}
<commit_msg>Update to the last Rust.<commit_after>//! Traits giving structural informations on linear algebra objects or the space they live in.

use std::num::Zero;
use std::vec::{VecIterator, VecMutIterator};
use traits::operations::{RMul, LMul, ScalarAdd, ScalarSub};
use traits::geometry::{Dot, Norm, UniformSphereSample};

/// Traits of objects which can be created from an object of type `T`.
pub trait Cast<T> {
    /// Converts an element of type `T` to an element of type `Self`.
    fn from(t: T) -> Self;
}

/// Trait of matrices.
///
/// A matrix has rows and columns and are able to multiply them.
pub trait Mat<R, C> : Row<R> + Col<C> + RMul<R> + LMul<C> { }

impl<M: Row<R> + Col<C> + RMul<R> + LMul<C>, R, C> Mat<R, C> for M {
}

// XXX: we keep ScalarAdd and ScalarSub here to avoid trait impl conflict (overriding) between the
// different Add/Sub traits. This is _so_ unfortunate…

/// Trait grouping most common operations on vectors.
pub trait Vec<N>: Dim + Sub<Self, Self> + Add<Self, Self> + Neg<Self> + Zero + Eq + Mul<N, Self>
                  + Div<N, Self> + Dot<N> {
}

/// Trait of vector with components implementing the `Real` trait.
pub trait RealVec<N: Real>: Vec<N> + Norm<N> {
}

/// Trait grouping uncommon, low-level and borderline (from the mathematical point of view)
/// operations on vectors.
pub trait VecExt<N>: Vec<N> + Indexable<uint, N> + Iterable<N> + UniformSphereSample + ScalarAdd<N>
                     + ScalarSub<N> + Orderable
{ }

/// Trait grouping uncommon, low-level and borderline (from the mathematical point of view)
/// operations on vectors.
pub trait RealVecExt<N: Real>: RealVec<N> + VecExt<N> + Basis + Round { }

impl<N, V: Dim + Sub<V, V> + Add<V, V> + Neg<V> + Zero + Eq + Mul<N, V> + Div<N, V> + Dot<N>>
Vec<N> for V { }

impl<N: Real, V: Vec<N> + Norm<N>> RealVec<N> for V { }

impl<N,
     V: Vec<N> + Indexable<uint, N> + Iterable<N> + UniformSphereSample + ScalarAdd<N> +
        ScalarSub<N> + Orderable>
VecExt<N> for V { }

impl<N: Real, V: RealVec<N> + VecExt<N> + Basis + Round> RealVecExt<N> for V { }

// FIXME: return an iterator instead
/// Traits of objects which can form a basis (typically vectors).
pub trait Basis {
    /// Iterates through the canonical basis of the space in which this object lives.
    fn canonical_basis(|Self| -> bool);

    /// Iterates through a basis of the subspace orthogonal to `self`.
    fn orthonormal_subspace_basis(&Self, |Self| -> bool);
}

/// Trait to access rows of a matrix or a vector.
pub trait Row<R> {
    /// The number of column of `self`.
    fn nrows(&self) -> uint;
    /// Reads the `i`-th row of `self`.
    fn row(&self, i: uint) -> R;
    /// Writes the `i`-th row of `self`.
    fn set_row(&mut self, i: uint, R);

    // FIXME: add iterators on rows: this could be a very good way to generalize _and_ optimize
    // a lot of operations.
}

/// Trait to access columns of a matrix or vector.
pub trait Col<C> {
    /// The number of column of this matrix or vector.
    fn ncols(&self) -> uint;

    /// Reads the `i`-th column of `self`.
    fn col(&self, i: uint) -> C;

    /// Writes the `i`-th column of `self`.
    fn set_col(&mut self, i: uint, C);

    // FIXME: add iterators on columns: this could be a very good way to generalize _and_ optimize
    // a lot of operations.
}

/// Trait of objects having a spacial dimension known at compile time.
pub trait Dim {
    /// The dimension of the object.
    fn dim(unused_self: Option<Self>) -> uint;
}

// FIXME: this trait should not be on nalgebra.
// however, it is needed because std::ops::Index is (strangely) to poor: it
// does not have a function to set values.
// Also, using Index with tuples crashes.
/// This is a workaround of current Rust limitations.
///
/// It exists because the `Index` trait cannot be used to express write access.
/// Thus, this is the same as the `Index` trait but without the syntactic sugar and with a method
/// to write to a specific index.
pub trait Indexable<Index, Res> {
    /// Reads the `i`-th element of `self`.
    fn at(&self, i: Index) -> Res;
    /// Writes to the `i`-th element of `self`.
    fn set(&mut self, i: Index, Res);
    /// Swaps the `i`-th element of `self` with its `j`-th element.
    fn swap(&mut self, i: Index, j: Index);

    /// Reads the `i`-th element of `self`.
    ///
    /// `i` is not checked.
    unsafe fn unsafe_at(&self, i: Index) -> Res;
    /// Writes to the `i`-th element of `self`.
    ///
    /// `i` is not checked.
    unsafe fn unsafe_set(&mut self, i: Index, Res);
}

/// This is a workaround of current Rust limitations.
///
/// Traits of objects which can be iterated through like a vector.
pub trait Iterable<N> {
    /// Gets a vector-like read-only iterator.
    fn iter<'l>(&'l self) -> VecIterator<'l, N>;
}

/// This is a workaround of current Rust limitations.
///
/// Traits of mutable objects which can be iterated through like a vector.
pub trait IterableMut<N> {
    /// Gets a vector-like read-write iterator.
    fn mut_iter<'l>(&'l mut self) -> VecMutIterator<'l, N>;
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.
use std::fmt::{Debug, Display, Formatter};
use regex::Error as RegexError;

/// An error type for failures that can occur during a session.
#[derive(Debug)]
pub enum Error {
    /// Action-specific failure.
    Action(Box<dyn std::error::Error>),
    /// Attempted to call an unknown or not implemented action.
    Dispatch(String),
    /// An error occurred when encoding bytes of a proto message.
    Encode(prost::EncodeError),
    /// An error occurred when parsing a proto message.
    Parse(ParseError),
}

impl Error {

    /// Converts an arbitrary action-issued error to a session error.
    ///
    /// This function should be used to construct session errors from action
    /// specific error types and propagate them further in the session pipeline.
    pub fn action<E>(error: E) -> Error
    where
        E: std::error::Error + 'static
    {
        Error::Action(Box::new(error))
    }
}

impl Display for Error {

    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
        use Error::*;

        match *self {
            Action(ref error) => {
                write!(fmt, "action error: {}", error)
            }
            Dispatch(ref name) if name.is_empty() => {
                write!(fmt, "missing action")
            }
            Dispatch(ref name) => {
                write!(fmt, "unknown action: {}", name)
            }
            Encode(ref error) => {
                write!(fmt, "failure during encoding proto message: {}", error)
            }
            Parse(ref error) => {
                write!(fmt, "malformed proto message: {}", error)
            }
        }
    }
}

impl std::error::Error for Error {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use Error::*;

        match *self {
            Action(ref error) => Some(error.as_ref()),
            Dispatch(_) => None,
            Encode(ref error) => Some(error),
            Parse(ref error) => Some(error),
        }
    }
}

impl From<prost::EncodeError> for Error {

    fn from(error: prost::EncodeError) -> Error {
        Error::Encode(error)
    }
}

impl From<ParseError> for Error {

    fn from(error: ParseError) -> Error {
        Error::Parse(error)
    }
}

/// An error type for failures that can occur when parsing proto messages.
#[derive(Debug)]
pub enum ParseError {
    /// An error occurred because the decoded proto message was malformed.
    Malformed(Box<dyn std::error::Error + Send + Sync>),
    /// An error occurred when decoding bytes of a proto message.
    Decode(prost::DecodeError),
}

impl ParseError {

    /// Converts a detailed error indicating a malformed proto to `ParseError`.
    ///
    /// This is just a convenience function for lifting custom error types that
    /// contain more specific information to generic `ParseError`.
    pub fn malformed<E>(error: E) -> ParseError
    where
        E: Into<Box<dyn std::error::Error + Send + Sync>>,
    {
        ParseError::Malformed(error.into())
    }
}

impl Display for ParseError {

    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
        use ParseError::*;

        match *self {
            Malformed(ref error) => {
                write!(fmt, "invalid proto message: {}", error)
            }
            Decode(ref error) => {
                write!(fmt, "failed to decode proto message: {}", error)
            }
        }
    }
}

impl std::error::Error for ParseError {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use ParseError::*;

        match *self {
            Malformed(ref error) => Some(error.as_ref()),
            Decode(ref error) => Some(error),
        }
    }
}

impl From<prost::DecodeError> for ParseError {

    fn from(error: prost::DecodeError) -> ParseError {
        ParseError::Decode(error)
    }
}

/// An error type for situations where required proto field is missing.
#[derive(Debug)]
pub struct MissingFieldError {
    /// A name of the missing field.
    name: &'static str,
}

impl MissingFieldError {

    /// Creates a new error indicating that required field `name` is missing.
    pub fn new(name: &'static str) -> MissingFieldError {
        MissingFieldError {
            name: name,
        }
    }
}

impl Display for MissingFieldError {

    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
        write!(fmt, "required field '{}' is missing", self.name)
    }
}

impl std::error::Error for MissingFieldError {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

impl From<MissingFieldError> for ParseError {

    fn from(error: MissingFieldError) -> ParseError {
        ParseError::malformed(error)
    }
}

/// An error type for situations where a given proto value is not supported.
#[derive(Debug)]
pub struct UnsupportedValueError<T> {
    /// A name of the field the value belongs to.
    pub name: &'static str,
    /// A value that is not supported.
    pub value: T,
}

impl<T: Debug> Display for UnsupportedValueError<T> {

    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
        write!(fmt, "unsupported value for '{}': {:?}", self.name, self.value)
    }
}

impl<T: Debug> std::error::Error for UnsupportedValueError<T> {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

/// An error type for situations where time micros cannot be converted
/// to `std::time::SystemTime`.
#[derive(Debug)]
pub struct TimeMicrosConversionError {
    /// Time micros value causing the conversion error.
    pub micros: u64,
}

impl Display for TimeMicrosConversionError {

    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
        write!(fmt, "cannot convert micros to std::time::SystemTime: {}", self.micros)
    }
}

impl std::error::Error for TimeMicrosConversionError {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

impl From<TimeMicrosConversionError> for ParseError {

    fn from(error: TimeMicrosConversionError) -> ParseError {
        ParseError::malformed(error)
    }
}

#[derive(Debug)]
pub struct RegexParseError {
    /// Raw data of the string which could not be converted to Regex.
    pub raw_data: Vec<u8>,
    /// Error message caught during the conversion.
    pub error: RegexError,
}

impl Display for RegexParseError {

    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
        write!(fmt, "Regex parse error happened on parsing '{:?}'. \
                     Regex error: '{}'",
               self.raw_data,
               self.error.to_string())
    }
}

impl std::error::Error for RegexParseError {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

impl From<RegexParseError> for ParseError {

    fn from(error: RegexParseError) -> ParseError {
        ParseError::malformed(error)
    }
}

/// An error type for situations where proto enum has a value for which
/// the definition is not known.
#[derive(Debug)]
pub struct UnknownEnumValueError {
    /// A name of the enum field having unknown enum value.
    pub name: &'static str,

    /// An enum value, which definition is not known.
    pub value: i32,
}

impl Display for UnknownEnumValueError {

    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
        write!(fmt, "protobuf enum '{}' has unrecognised value: '{}'",
               self.name, self.value)
    }
}

impl std::error::Error for UnknownEnumValueError {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

impl From<UnknownEnumValueError> for ParseError {

    fn from(error: UnknownEnumValueError) -> ParseError {
        ParseError::malformed(error)
    }
}
<commit_msg>Add the `protobuf` crate error to session errors.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.
use std::fmt::{Debug, Display, Formatter};
use regex::Error as RegexError;

/// An error type for failures that can occur during a session.
#[derive(Debug)]
pub enum Error {
    /// Action-specific failure.
    Action(Box<dyn std::error::Error>),
    /// Attempted to call an unknown or not implemented action.
    Dispatch(String),
    /// An error occurred when encoding bytes of a proto message.
    Encode(prost::EncodeError),
    /// An error occurred when encoding bytes of a proto message.
    // TODO: Rename to just `Encode`.
    // TODO: Determine whether we need this error type or we should just panic.
    EncodeProtobuf(protobuf::ProtobufError),
    /// An error occurred when parsing a proto message.
    Parse(ParseError),
}

impl Error {

    /// Converts an arbitrary action-issued error to a session error.
    ///
    /// This function should be used to construct session errors from action
    /// specific error types and propagate them further in the session pipeline.
    pub fn action<E>(error: E) -> Error
    where
        E: std::error::Error + 'static
    {
        Error::Action(Box::new(error))
    }
}

impl Display for Error {

    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
        use Error::*;

        match *self {
            Action(ref error) => {
                write!(fmt, "action error: {}", error)
            }
            Dispatch(ref name) if name.is_empty() => {
                write!(fmt, "missing action")
            }
            Dispatch(ref name) => {
                write!(fmt, "unknown action: {}", name)
            }
            Encode(ref error) => {
                write!(fmt, "failure during encoding proto message: {}", error)
            }
            EncodeProtobuf(ref error) => {
                write!(fmt, "failure during encoding proto message: {}", error)
            }
            Parse(ref error) => {
                write!(fmt, "malformed proto message: {}", error)
            }
        }
    }
}

impl std::error::Error for Error {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use Error::*;

        match *self {
            Action(ref error) => Some(error.as_ref()),
            Dispatch(_) => None,
            Encode(ref error) => Some(error),
            EncodeProtobuf(ref error) => Some(error),
            Parse(ref error) => Some(error),
        }
    }
}

impl From<prost::EncodeError> for Error {

    fn from(error: prost::EncodeError) -> Error {
        Error::Encode(error)
    }
}

impl From<protobuf::ProtobufError> for Error {

    fn from(error: protobuf::ProtobufError) -> Error {
        Error::EncodeProtobuf(error)
    }
}

impl From<ParseError> for Error {

    fn from(error: ParseError) -> Error {
        Error::Parse(error)
    }
}

/// An error type for failures that can occur when parsing proto messages.
#[derive(Debug)]
pub enum ParseError {
    /// An error occurred because the decoded proto message was malformed.
    Malformed(Box<dyn std::error::Error + Send + Sync>),
    /// An error occurred when decoding bytes of a proto message.
    Decode(prost::DecodeError),
}

impl ParseError {

    /// Converts a detailed error indicating a malformed proto to `ParseError`.
    ///
    /// This is just a convenience function for lifting custom error types that
    /// contain more specific information to generic `ParseError`.
    pub fn malformed<E>(error: E) -> ParseError
    where
        E: Into<Box<dyn std::error::Error + Send + Sync>>,
    {
        ParseError::Malformed(error.into())
    }
}

impl Display for ParseError {

    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
        use ParseError::*;

        match *self {
            Malformed(ref error) => {
                write!(fmt, "invalid proto message: {}", error)
            }
            Decode(ref error) => {
                write!(fmt, "failed to decode proto message: {}", error)
            }
        }
    }
}

impl std::error::Error for ParseError {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use ParseError::*;

        match *self {
            Malformed(ref error) => Some(error.as_ref()),
            Decode(ref error) => Some(error),
        }
    }
}

impl From<prost::DecodeError> for ParseError {

    fn from(error: prost::DecodeError) -> ParseError {
        ParseError::Decode(error)
    }
}

/// An error type for situations where required proto field is missing.
#[derive(Debug)]
pub struct MissingFieldError {
    /// A name of the missing field.
    name: &'static str,
}

impl MissingFieldError {

    /// Creates a new error indicating that required field `name` is missing.
    pub fn new(name: &'static str) -> MissingFieldError {
        MissingFieldError {
            name: name,
        }
    }
}

impl Display for MissingFieldError {

    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
        write!(fmt, "required field '{}' is missing", self.name)
    }
}

impl std::error::Error for MissingFieldError {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

impl From<MissingFieldError> for ParseError {

    fn from(error: MissingFieldError) -> ParseError {
        ParseError::malformed(error)
    }
}

/// An error type for situations where a given proto value is not supported.
#[derive(Debug)]
pub struct UnsupportedValueError<T> {
    /// A name of the field the value belongs to.
    pub name: &'static str,
    /// A value that is not supported.
    pub value: T,
}

impl<T: Debug> Display for UnsupportedValueError<T> {

    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
        write!(fmt, "unsupported value for '{}': {:?}", self.name, self.value)
    }
}

impl<T: Debug> std::error::Error for UnsupportedValueError<T> {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

/// An error type for situations where time micros cannot be converted
/// to `std::time::SystemTime`.
#[derive(Debug)]
pub struct TimeMicrosConversionError {
    /// Time micros value causing the conversion error.
    pub micros: u64,
}

impl Display for TimeMicrosConversionError {

    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
        write!(fmt, "cannot convert micros to std::time::SystemTime: {}", self.micros)
    }
}

impl std::error::Error for TimeMicrosConversionError {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

impl From<TimeMicrosConversionError> for ParseError {

    fn from(error: TimeMicrosConversionError) -> ParseError {
        ParseError::malformed(error)
    }
}

#[derive(Debug)]
pub struct RegexParseError {
    /// Raw data of the string which could not be converted to Regex.
    pub raw_data: Vec<u8>,
    /// Error message caught during the conversion.
    pub error: RegexError,
}

impl Display for RegexParseError {

    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
        write!(fmt, "Regex parse error happened on parsing '{:?}'. \
                     Regex error: '{}'",
               self.raw_data,
               self.error.to_string())
    }
}

impl std::error::Error for RegexParseError {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

impl From<RegexParseError> for ParseError {

    fn from(error: RegexParseError) -> ParseError {
        ParseError::malformed(error)
    }
}

/// An error type for situations where proto enum has a value for which
/// the definition is not known.
#[derive(Debug)]
pub struct UnknownEnumValueError {
    /// A name of the enum field having unknown enum value.
    pub name: &'static str,

    /// An enum value, which definition is not known.
    pub value: i32,
}

impl Display for UnknownEnumValueError {

    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
        write!(fmt, "protobuf enum '{}' has unrecognised value: '{}'",
               self.name, self.value)
    }
}

impl std::error::Error for UnknownEnumValueError {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

impl From<UnknownEnumValueError> for ParseError {

    fn from(error: UnknownEnumValueError) -> ParseError {
        ParseError::malformed(error)
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

extern crate devicemapper;
extern crate env_logger;
extern crate libstratis;
extern crate log;
extern crate loopdev;
extern crate tempdir;

mod util;

use std::fs::OpenOptions;
use std::io::{Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};

use loopdev::{LoopControl, LoopDevice};
use tempdir::TempDir;

use devicemapper::{Bytes, Sectors};

use libstratis::engine::IEC;
use libstratis::engine::strat_engine::device::wipe_sectors;

use util::logger::init_logger;
use util::blockdev_tests::test_blockdevmgr_used;
use util::blockdev_tests::test_force_flag_dirty;
use util::blockdev_tests::test_force_flag_stratis;
use util::blockdev_tests::test_pool_blockdevs;
use util::dm_tests::test_thinpool_device;
use util::dm_tests::test_linear_device;
use util::filesystem_tests::test_xfs_expand;
use util::pool_tests::test_filesystem_rename;
use util::pool_tests::test_thinpool_expand;
use util::pool_tests::test_thinpool_thindev_destroy;
use util::setup_tests::test_basic_metadata;
use util::setup_tests::test_initialize;
use util::setup_tests::test_pool_rename;
use util::setup_tests::test_pool_setup;
use util::setup_tests::test_setup;
use util::simple_tests::test_empty_pool;
use util::simple_tests::test_teardown;

pub struct LoopTestDev {
    ld: LoopDevice,
}

impl LoopTestDev {
    pub fn new(lc: &LoopControl, path: &Path) -> LoopTestDev {
        let mut f = OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .open(&path)
            .unwrap();

        // the proper way to do this is fallocate, but nix doesn't implement yet.
        // TODO: see https://github.com/nix-rust/nix/issues/596
        f.seek(SeekFrom::Start(IEC::Gi)).unwrap();
        f.write(&[0]).unwrap();
        f.flush().unwrap();

        let ld = lc.next_free().unwrap();
        ld.attach(path, 0).unwrap();
        // Wipe 1 MiB at the beginning, as data sits around on the files.
        wipe_sectors(&ld.get_path().unwrap(),
                     Sectors(0),
                     Bytes(IEC::Mi).sectors())
                .unwrap();

        LoopTestDev { ld: ld }
    }

    fn get_path(&self) -> PathBuf {
        self.ld.get_path().unwrap()
    }

    pub fn detach(&self) {
        self.ld.detach().unwrap()
    }
}

impl Drop for LoopTestDev {
    fn drop(&mut self) {
        self.detach()
    }
}

/// Setup count loop backed devices in dir.
/// Make sure each loop device is backed by a 1 GiB file.
/// Wipe the first 1 MiB of the file.
fn get_devices(count: u8, dir: &TempDir) -> Vec<LoopTestDev> {
    let lc = LoopControl::open().unwrap();
    let mut loop_devices = Vec::new();

    for index in 0..count {
        let path = dir.path().join(format!("store{}", &index));
        loop_devices.push(LoopTestDev::new(&lc, &path));
    }
    loop_devices
}

/// Set up count loopbacked devices.
/// Then, run the designated test.
/// Then, take down the loop devices.
fn test_with_spec<F>(count: u8, test: F) -> ()
    where F: Fn(&[&Path]) -> ()
{
    init_logger();
    let tmpdir = TempDir::new("stratis").unwrap();
    let loop_devices: Vec<LoopTestDev> = get_devices(count, &tmpdir);
    let device_paths: Vec<PathBuf> = loop_devices.iter().map(|x| x.get_path()).collect();
    let device_paths: Vec<&Path> = device_paths.iter().map(|x| x.as_path()).collect();

    test(&device_paths);

}

#[test]
pub fn loop_test_force_flag_stratis() {
    test_with_spec(1, test_force_flag_stratis);
    test_with_spec(2, test_force_flag_stratis);
    test_with_spec(3, test_force_flag_stratis);
}

#[test]
pub fn loop_test_linear_device() {
    test_with_spec(2, test_linear_device);
    test_with_spec(3, test_linear_device);
}

#[test]
pub fn loop_test_thinpool_device() {
    test_with_spec(3, test_thinpool_device);
}

#[test]
pub fn loop_test_thinpool_expand() {
    test_with_spec(3, test_thinpool_expand);
}

#[test]
pub fn loop_test_thinpool_thindev_destroy() {
    test_with_spec(3, test_thinpool_thindev_destroy);
}

#[test]
pub fn loop_test_pool_blockdevs() {
    test_with_spec(3, test_pool_blockdevs);
}

#[test]
pub fn loop_test_force_flag_dirty() {
    test_with_spec(3, test_force_flag_dirty);
}

#[test]
pub fn loop_test_teardown() {
    test_with_spec(2, test_teardown);
}

#[test]
pub fn loop_test_initialize() {
    test_with_spec(4, test_initialize);
}

#[test]
pub fn loop_test_empty_pool() {
    test_with_spec(0, test_empty_pool);
}

#[test]
pub fn loop_test_basic_metadata() {
    test_with_spec(4, test_basic_metadata);
}

#[test]
pub fn loop_test_setup() {
    test_with_spec(4, test_setup);
}

#[test]
pub fn loop_test_pool_rename() {
    test_with_spec(2, test_pool_rename);
}

#[test]
pub fn loop_test_blockdevmgr_used() {
    test_with_spec(2, test_blockdevmgr_used);
}

#[test]
pub fn loop_test_filesystem_rename() {
    test_with_spec(2, test_filesystem_rename);
}

#[test]
pub fn loop_test_pool_setup() {
    test_with_spec(2, test_pool_setup);
}

#[test]
pub fn loop_test_xfs_expand() {
    test_with_spec(3, test_xfs_expand);
}
<commit_msg>Use DeviceLimits to specify loopbacked tests<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

extern crate devicemapper;
extern crate env_logger;
extern crate libstratis;
extern crate log;
extern crate loopdev;
extern crate tempdir;

mod util;

use std::fs::OpenOptions;
use std::io::{Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};

use loopdev::{LoopControl, LoopDevice};
use tempdir::TempDir;

use devicemapper::{Bytes, Sectors};

use libstratis::engine::IEC;
use libstratis::engine::strat_engine::device::wipe_sectors;

use util::logger::init_logger;
use util::blockdev_tests::test_blockdevmgr_used;
use util::blockdev_tests::test_force_flag_dirty;
use util::blockdev_tests::test_force_flag_stratis;
use util::blockdev_tests::test_pool_blockdevs;
use util::dm_tests::test_thinpool_device;
use util::dm_tests::test_linear_device;
use util::filesystem_tests::test_xfs_expand;
use util::pool_tests::test_filesystem_rename;
use util::pool_tests::test_thinpool_expand;
use util::pool_tests::test_thinpool_thindev_destroy;
use util::setup_tests::test_basic_metadata;
use util::setup_tests::test_initialize;
use util::setup_tests::test_pool_rename;
use util::setup_tests::test_pool_setup;
use util::setup_tests::test_setup;
use util::simple_tests::test_empty_pool;
use util::simple_tests::test_teardown;

/// Ways of specifying range of numbers of devices to use for tests.
/// Unlike real tests, there is no AtLeast constructor, as, at least in theory
/// there is no upper bound to the number of loop devices that can be made.
enum DeviceLimits {
    Exactly(usize),
    Range(usize, usize), // inclusive
}

pub struct LoopTestDev {
    ld: LoopDevice,
}

impl LoopTestDev {
    pub fn new(lc: &LoopControl, path: &Path) -> LoopTestDev {
        let mut f = OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .open(&path)
            .unwrap();

        // the proper way to do this is fallocate, but nix doesn't implement yet.
        // TODO: see https://github.com/nix-rust/nix/issues/596
        f.seek(SeekFrom::Start(IEC::Gi)).unwrap();
        f.write(&[0]).unwrap();
        f.flush().unwrap();

        let ld = lc.next_free().unwrap();
        ld.attach(path, 0).unwrap();
        // Wipe 1 MiB at the beginning, as data sits around on the files.
        wipe_sectors(&ld.get_path().unwrap(),
                     Sectors(0),
                     Bytes(IEC::Mi).sectors())
                .unwrap();

        LoopTestDev { ld: ld }
    }

    fn get_path(&self) -> PathBuf {
        self.ld.get_path().unwrap()
    }

    pub fn detach(&self) {
        self.ld.detach().unwrap()
    }
}

impl Drop for LoopTestDev {
    fn drop(&mut self) {
        self.detach()
    }
}

/// Get a list of counts of devices to use for tests.
fn get_device_counts(limits: DeviceLimits) -> Vec<usize> {
    match limits {
        DeviceLimits::Exactly(num) => vec![num],
        DeviceLimits::Range(lower, upper) => {
            assert!(lower < upper);
            vec![lower, upper]
        }
    }
}

/// Setup count loop backed devices in dir.
/// Make sure each loop device is backed by a 1 GiB file.
/// Wipe the first 1 MiB of the file.
fn get_devices(count: usize, dir: &TempDir) -> Vec<LoopTestDev> {
    let lc = LoopControl::open().unwrap();
    let mut loop_devices = Vec::new();

    for index in 0..count {
        let path = dir.path().join(format!("store{}", &index));
        loop_devices.push(LoopTestDev::new(&lc, &path));
    }
    loop_devices
}

/// Run the designated tests according to the specification.
fn test_with_spec<F>(limits: DeviceLimits, test: F) -> ()
    where F: Fn(&[&Path]) -> ()
{
    let counts = get_device_counts(limits);

    init_logger();

    for count in counts {
        let tmpdir = TempDir::new("stratis").unwrap();
        let loop_devices: Vec<LoopTestDev> = get_devices(count, &tmpdir);
        let device_paths: Vec<PathBuf> = loop_devices.iter().map(|x| x.get_path()).collect();
        let device_paths: Vec<&Path> = device_paths.iter().map(|x| x.as_path()).collect();
        test(&device_paths);
    }
}

#[test]
pub fn loop_test_force_flag_stratis() {
    test_with_spec(DeviceLimits::Range(1, 3), test_force_flag_stratis);
}

#[test]
pub fn loop_test_linear_device() {
    test_with_spec(DeviceLimits::Range(1, 3), test_linear_device);
}

#[test]
pub fn loop_test_thinpool_device() {
    /// This test requires more than 1 GiB.
    test_with_spec(DeviceLimits::Range(2, 3), test_thinpool_device);
}

#[test]
pub fn loop_test_thinpool_expand() {
    /// This test requires more than 1 GiB.
    test_with_spec(DeviceLimits::Range(2, 3), test_thinpool_expand);
}

#[test]
pub fn loop_test_thinpool_thindev_destroy() {
    /// This test requires more than 1 GiB.
    test_with_spec(DeviceLimits::Range(2, 3), test_thinpool_thindev_destroy);
}

#[test]
pub fn loop_test_pool_blockdevs() {
    /// This test requires more than 1 GiB.
    test_with_spec(DeviceLimits::Range(2, 3), test_pool_blockdevs);
}

#[test]
pub fn loop_test_force_flag_dirty() {
    test_with_spec(DeviceLimits::Range(1, 3), test_force_flag_dirty);
}

#[test]
pub fn loop_test_teardown() {
    test_with_spec(DeviceLimits::Range(1, 3), test_teardown);
}

#[test]
pub fn loop_test_initialize() {
    test_with_spec(DeviceLimits::Range(2, 3), test_initialize);
}

#[test]
pub fn loop_test_empty_pool() {
    test_with_spec(DeviceLimits::Exactly(0), test_empty_pool);
}

#[test]
pub fn loop_test_basic_metadata() {
    test_with_spec(DeviceLimits::Range(2, 3), test_basic_metadata);
}

#[test]
pub fn loop_test_setup() {
    test_with_spec(DeviceLimits::Range(2, 3), test_setup);
}

#[test]
pub fn loop_test_pool_rename() {
    test_with_spec(DeviceLimits::Range(1, 3), test_pool_rename);
}

#[test]
pub fn loop_test_blockdevmgr_used() {
    test_with_spec(DeviceLimits::Range(1, 3), test_blockdevmgr_used);
}

#[test]
pub fn loop_test_filesystem_rename() {
    test_with_spec(DeviceLimits::Range(1, 3), test_filesystem_rename);
}

#[test]
pub fn loop_test_pool_setup() {
    test_with_spec(DeviceLimits::Range(1, 3), test_pool_setup);
}

#[test]
pub fn loop_test_xfs_expand() {
    test_with_spec(DeviceLimits::Range(1, 3), test_xfs_expand);
}
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Martin Kysel <code@martinkysel.com>
//  *
//  * For the full copyright and license information, please view the LICENSE file
//  * that was distributed with this source code.

// spell-checker:ignore (methods) isnt

#[macro_use]
extern crate uucore;

use std::{
    fs::File,
    io::{stdin, stdout, BufReader, Read, Stdout, Write},
    path::Path,
    time::Duration,
};

#[cfg(all(unix, not(target_os = "fuchsia")))]
extern crate nix;

use clap::{crate_version, App, Arg};
use crossterm::{
    event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
    execute, queue,
    style::Attribute,
    terminal,
};

use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;

const BELL: &str = "\x07";

pub mod options {
    pub const SILENT: &str = "silent";
    pub const LOGICAL: &str = "logical";
    pub const NO_PAUSE: &str = "no-pause";
    pub const PRINT_OVER: &str = "print-over";
    pub const CLEAN_PRINT: &str = "clean-print";
    pub const SQUEEZE: &str = "squeeze";
    pub const PLAIN: &str = "plain";
    pub const LINES: &str = "lines";
    pub const NUMBER: &str = "number";
    pub const PATTERN: &str = "pattern";
    pub const FROM_LINE: &str = "from-line";
    pub const FILES: &str = "files";
}

const MULTI_FILE_TOP_PROMPT: &str = "::::::::::::::\n{}\n::::::::::::::\n";

pub fn uumain(args: impl uucore::Args) -> i32 {
    let matches = App::new(executable!())
        .about("A file perusal filter for CRT viewing.")
        .version(crate_version!())
        .arg(
            Arg::with_name(options::SILENT)
                .short("d")
                .long(options::SILENT)
                .help("Display help instead of ringing bell"),
        )
        // The commented arguments below are unimplemented:
        /*
        .arg(
            Arg::with_name(options::LOGICAL)
                .short("f")
                .long(options::LOGICAL)
                .help("Count logical rather than screen lines"),
        )
        .arg(
            Arg::with_name(options::NO_PAUSE)
                .short("l")
                .long(options::NO_PAUSE)
                .help("Suppress pause after form feed"),
        )
        .arg(
            Arg::with_name(options::PRINT_OVER)
                .short("c")
                .long(options::PRINT_OVER)
                .help("Do not scroll, display text and clean line ends"),
        )
        .arg(
            Arg::with_name(options::CLEAN_PRINT)
                .short("p")
                .long(options::CLEAN_PRINT)
                .help("Do not scroll, clean screen and display text"),
        )
        .arg(
            Arg::with_name(options::SQUEEZE)
                .short("s")
                .long(options::SQUEEZE)
                .help("Squeeze multiple blank lines into one"),
        )
        .arg(
            Arg::with_name(options::PLAIN)
                .short("u")
                .long(options::PLAIN)
                .help("Suppress underlining and bold"),
        )
        .arg(
            Arg::with_name(options::LINES)
                .short("n")
                .long(options::LINES)
                .value_name("number")
                .takes_value(true)
                .help("The number of lines per screen full"),
        )
        .arg(
            Arg::with_name(options::NUMBER)
                .allow_hyphen_values(true)
                .long(options::NUMBER)
                .required(false)
                .takes_value(true)
                .help("Same as --lines"),
        )
        .arg(
            Arg::with_name(options::FROM_LINE)
                .short("F")
                .allow_hyphen_values(true)
                .required(false)
                .takes_value(true)
                .value_name("number")
                .help("Display file beginning from line number"),
        )
        .arg(
            Arg::with_name(options::PATTERN)
                .short("P")
                .allow_hyphen_values(true)
                .required(false)
                .takes_value(true)
                .help("Display file beginning from pattern match"),
        )
        */
        .arg(
            Arg::with_name(options::FILES)
                .required(false)
                .multiple(true)
                .help("Path to the files to be read"),
        )
        .get_matches_from(args);

    let mut buff = String::new();
    let silent = matches.is_present(options::SILENT);
    if let Some(files) = matches.values_of(options::FILES) {
        let mut stdout = setup_term();
        let length = files.len();

        let mut files_iter = files.peekable();
        while let (Some(file), next_file) = (files_iter.next(), files_iter.peek()) {
            let file = Path::new(file);
            if file.is_dir() {
                terminal::disable_raw_mode().unwrap();
                show_usage_error!("'{}' is a directory.", file.display());
                return 1;
            }
            if !file.exists() {
                terminal::disable_raw_mode().unwrap();
                show_error!("cannot open {}: No such file or directory", file.display());
                return 1;
            }
            if length > 1 {
                buff.push_str(&MULTI_FILE_TOP_PROMPT.replace("{}", file.to_str().unwrap()));
            }
            let mut reader = BufReader::new(File::open(file).unwrap());
            reader.read_to_string(&mut buff).unwrap();
            more(&buff, &mut stdout, next_file.copied(), silent);
            buff.clear();
        }
        reset_term(&mut stdout);
    } else if atty::isnt(atty::Stream::Stdin) {
        stdin().read_to_string(&mut buff).unwrap();
        let mut stdout = setup_term();
        more(&buff, &mut stdout, None, silent);
        reset_term(&mut stdout);
    } else {
        show_usage_error!("bad usage");
    }
    0
}

#[cfg(not(target_os = "fuchsia"))]
fn setup_term() -> std::io::Stdout {
    let stdout = stdout();
    terminal::enable_raw_mode().unwrap();
    stdout
}

#[cfg(target_os = "fuchsia")]
#[inline(always)]
fn setup_term() -> usize {
    0
}

#[cfg(not(target_os = "fuchsia"))]
fn reset_term(stdout: &mut std::io::Stdout) {
    terminal::disable_raw_mode().unwrap();
    // Clear the prompt
    queue!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
    // Move cursor to the beginning without printing new line
    print!("\r");
    stdout.flush().unwrap();
}

#[cfg(target_os = "fuchsia")]
#[inline(always)]
fn reset_term(_: &mut usize) {}

fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>, silent: bool) {
    let (cols, rows) = terminal::size().unwrap();
    let lines = break_buff(buff, usize::from(cols));

    let mut pager = Pager::new(rows as usize, lines, next_file, silent);
    pager.draw(stdout, false);
    if pager.should_close() {
        return;
    }

    loop {
        let mut wrong_key = false;
        if event::poll(Duration::from_millis(10)).unwrap() {
            match event::read().unwrap() {
                Event::Key(KeyEvent {
                    code: KeyCode::Char('q'),
                    modifiers: KeyModifiers::NONE,
                })
                | Event::Key(KeyEvent {
                    code: KeyCode::Char('c'),
                    modifiers: KeyModifiers::CONTROL,
                }) => {
                    reset_term(&mut stdout);
                    std::process::exit(0);
                }
                Event::Key(KeyEvent {
                    code: KeyCode::Down,
                    modifiers: KeyModifiers::NONE,
                })
                | Event::Key(KeyEvent {
                    code: KeyCode::Char(' '),
                    modifiers: KeyModifiers::NONE,
                }) => {
                    pager.page_down();
                }
                Event::Key(KeyEvent {
                    code: KeyCode::Up,
                    modifiers: KeyModifiers::NONE,
                }) => {
                    pager.page_up();
                }
                _ => {
                    wrong_key = true;
                }
            }

            pager.draw(stdout, wrong_key);
            if pager.should_close() {
                return;
            }
        }
    }
}

struct Pager<'a> {
    // The current line at the top of the screen
    upper_mark: usize,
    // The number of rows that fit on the screen
    content_rows: usize,
    lines: Vec<String>,
    next_file: Option<&'a str>,
    line_count: usize,
    close_on_down: bool,
    silent: bool,
}

impl<'a> Pager<'a> {
    fn new(rows: usize, lines: Vec<String>, next_file: Option<&'a str>, silent: bool) -> Self {
        let line_count = lines.len();
        Self {
            upper_mark: 0,
            content_rows: rows - 1,
            lines,
            next_file,
            line_count,
            close_on_down: false,
            silent,
        }
    }

    fn should_close(&mut self) -> bool {
        if self.upper_mark + self.content_rows >= self.line_count {
            if self.close_on_down {
                return true;
            }
            if self.next_file.is_none() {
                return true;
            } else {
                self.close_on_down = true;
            }
        } else {
            self.close_on_down = false;
        }
        false
    }

    fn page_down(&mut self) {
        self.upper_mark += self.content_rows;
    }

    fn page_up(&mut self) {
        self.upper_mark = self.upper_mark.saturating_sub(self.content_rows);
    }

    fn draw(&self, stdout: &mut std::io::Stdout, wrong_key: bool) {
        let lower_mark = self.line_count.min(self.upper_mark + self.content_rows);
        self.draw_lines(stdout);
        self.draw_prompt(stdout, lower_mark, wrong_key);
        stdout.flush().unwrap();
    }

    fn draw_lines(&self, stdout: &mut std::io::Stdout) {
        execute!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
        let displayed_lines = self
            .lines
            .iter()
            .skip(self.upper_mark)
            .take(self.content_rows);

        for line in displayed_lines {
            stdout
                .write_all(format!("\r{}\n", line).as_bytes())
                .unwrap();
        }
    }

    fn draw_prompt(&self, stdout: &mut Stdout, lower_mark: usize, wrong_key: bool) {
        let status_inner = if lower_mark == self.line_count {
            format!("Next file: {}", self.next_file.unwrap_or_default())
        } else {
            format!(
                "{}%",
                (lower_mark as f64 / self.line_count as f64 * 100.0).round() as u16
            )
        };

        let status = format!("--More--({})", status_inner);

        let banner = match (self.silent, wrong_key) {
            (true, true) => "[Press 'h' for instructions. (unimplemented)]".to_string(),
            (true, false) => format!("{}[Press space to continue, 'q' to quit.]", status),
            (false, true) => format!("{}{}", status, BELL),
            (false, false) => status,
        };

        write!(
            stdout,
            "\r{}{}{}",
            Attribute::Reverse,
            banner,
            Attribute::Reset
        )
        .unwrap();
    }
}

// Break the lines on the cols of the terminal
fn break_buff(buff: &str, cols: usize) -> Vec<String> {
    let mut lines = Vec::new();

    for l in buff.lines() {
        lines.append(&mut break_line(l, cols));
    }
    lines
}

fn break_line(line: &str, cols: usize) -> Vec<String> {
    let width = UnicodeWidthStr::width(line);
    let mut lines = Vec::new();
    if width < cols {
        lines.push(line.to_string());
        return lines;
    }

    let gr_idx = UnicodeSegmentation::grapheme_indices(line, true);
    let mut last_index = 0;
    let mut total_width = 0;
    for (index, grapheme) in gr_idx {
        let width = UnicodeWidthStr::width(grapheme);
        total_width += width;

        if total_width > cols {
            lines.push(line[last_index..index].to_string());
            last_index = index;
            total_width = width;
        }
    }

    if last_index != line.len() {
        lines.push(line[last_index..].to_string());
    }
    lines
}

#[cfg(test)]
mod tests {
    use super::break_line;
    use unicode_width::UnicodeWidthStr;

    #[test]
    fn test_break_lines_long() {
        let mut test_string = String::with_capacity(100);
        for _ in 0..200 {
            test_string.push('#');
        }

        let lines = break_line(&test_string, 80);
        let widths: Vec<usize> = lines
            .iter()
            .map(|s| UnicodeWidthStr::width(&s[..]))
            .collect();

        assert_eq!((80, 80, 40), (widths[0], widths[1], widths[2]));
    }

    #[test]
    fn test_break_lines_short() {
        let mut test_string = String::with_capacity(100);
        for _ in 0..20 {
            test_string.push('#');
        }

        let lines = break_line(&test_string, 80);

        assert_eq!(20, lines[0].len());
    }

    #[test]
    fn test_break_line_zwj() {
        let mut test_string = String::with_capacity(1100);
        for _ in 0..20 {
            test_string.push_str("👩🏻🔬");
        }

        let lines = break_line(&test_string, 80);

        let widths: Vec<usize> = lines
            .iter()
            .map(|s| UnicodeWidthStr::width(&s[..]))
            .collect();

        // Each 👩🏻🔬 is 6 character width it break line to the closest number to 80 => 6 * 13 = 78
        assert_eq!((78, 42), (widths[0], widths[1]));
    }
}
<commit_msg>Now stops at the last line first. Press down again to go to next file or quit<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Martin Kysel <code@martinkysel.com>
//  *
//  * For the full copyright and license information, please view the LICENSE file
//  * that was distributed with this source code.

// spell-checker:ignore (methods) isnt

#[macro_use]
extern crate uucore;

use std::{
    fs::File,
    io::{stdin, stdout, BufReader, Read, Stdout, Write},
    path::Path,
    time::Duration,
};

#[cfg(all(unix, not(target_os = "fuchsia")))]
extern crate nix;

use clap::{crate_version, App, Arg};
use crossterm::{
    event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
    execute, queue,
    style::Attribute,
    terminal,
};

use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;

const BELL: &str = "\x07";

pub mod options {
    pub const SILENT: &str = "silent";
    pub const LOGICAL: &str = "logical";
    pub const NO_PAUSE: &str = "no-pause";
    pub const PRINT_OVER: &str = "print-over";
    pub const CLEAN_PRINT: &str = "clean-print";
    pub const SQUEEZE: &str = "squeeze";
    pub const PLAIN: &str = "plain";
    pub const LINES: &str = "lines";
    pub const NUMBER: &str = "number";
    pub const PATTERN: &str = "pattern";
    pub const FROM_LINE: &str = "from-line";
    pub const FILES: &str = "files";
}

const MULTI_FILE_TOP_PROMPT: &str = "::::::::::::::\n{}\n::::::::::::::\n";

pub fn uumain(args: impl uucore::Args) -> i32 {
    let matches = App::new(executable!())
        .about("A file perusal filter for CRT viewing.")
        .version(crate_version!())
        .arg(
            Arg::with_name(options::SILENT)
                .short("d")
                .long(options::SILENT)
                .help("Display help instead of ringing bell"),
        )
        // The commented arguments below are unimplemented:
        /*
        .arg(
            Arg::with_name(options::LOGICAL)
                .short("f")
                .long(options::LOGICAL)
                .help("Count logical rather than screen lines"),
        )
        .arg(
            Arg::with_name(options::NO_PAUSE)
                .short("l")
                .long(options::NO_PAUSE)
                .help("Suppress pause after form feed"),
        )
        .arg(
            Arg::with_name(options::PRINT_OVER)
                .short("c")
                .long(options::PRINT_OVER)
                .help("Do not scroll, display text and clean line ends"),
        )
        .arg(
            Arg::with_name(options::CLEAN_PRINT)
                .short("p")
                .long(options::CLEAN_PRINT)
                .help("Do not scroll, clean screen and display text"),
        )
        .arg(
            Arg::with_name(options::SQUEEZE)
                .short("s")
                .long(options::SQUEEZE)
                .help("Squeeze multiple blank lines into one"),
        )
        .arg(
            Arg::with_name(options::PLAIN)
                .short("u")
                .long(options::PLAIN)
                .help("Suppress underlining and bold"),
        )
        .arg(
            Arg::with_name(options::LINES)
                .short("n")
                .long(options::LINES)
                .value_name("number")
                .takes_value(true)
                .help("The number of lines per screen full"),
        )
        .arg(
            Arg::with_name(options::NUMBER)
                .allow_hyphen_values(true)
                .long(options::NUMBER)
                .required(false)
                .takes_value(true)
                .help("Same as --lines"),
        )
        .arg(
            Arg::with_name(options::FROM_LINE)
                .short("F")
                .allow_hyphen_values(true)
                .required(false)
                .takes_value(true)
                .value_name("number")
                .help("Display file beginning from line number"),
        )
        .arg(
            Arg::with_name(options::PATTERN)
                .short("P")
                .allow_hyphen_values(true)
                .required(false)
                .takes_value(true)
                .help("Display file beginning from pattern match"),
        )
        */
        .arg(
            Arg::with_name(options::FILES)
                .required(false)
                .multiple(true)
                .help("Path to the files to be read"),
        )
        .get_matches_from(args);

    let mut buff = String::new();
    let silent = matches.is_present(options::SILENT);
    if let Some(files) = matches.values_of(options::FILES) {
        let mut stdout = setup_term();
        let length = files.len();

        let mut files_iter = files.peekable();
        while let (Some(file), next_file) = (files_iter.next(), files_iter.peek()) {
            let file = Path::new(file);
            if file.is_dir() {
                terminal::disable_raw_mode().unwrap();
                show_usage_error!("'{}' is a directory.", file.display());
                return 1;
            }
            if !file.exists() {
                terminal::disable_raw_mode().unwrap();
                show_error!("cannot open {}: No such file or directory", file.display());
                return 1;
            }
            if length > 1 {
                buff.push_str(&MULTI_FILE_TOP_PROMPT.replace("{}", file.to_str().unwrap()));
            }
            let mut reader = BufReader::new(File::open(file).unwrap());
            reader.read_to_string(&mut buff).unwrap();
            more(&buff, &mut stdout, next_file.copied(), silent);
            buff.clear();
        }
        reset_term(&mut stdout);
    } else if atty::isnt(atty::Stream::Stdin) {
        stdin().read_to_string(&mut buff).unwrap();
        let mut stdout = setup_term();
        more(&buff, &mut stdout, None, silent);
        reset_term(&mut stdout);
    } else {
        show_usage_error!("bad usage");
    }
    0
}

#[cfg(not(target_os = "fuchsia"))]
fn setup_term() -> std::io::Stdout {
    let stdout = stdout();
    terminal::enable_raw_mode().unwrap();
    stdout
}

#[cfg(target_os = "fuchsia")]
#[inline(always)]
fn setup_term() -> usize {
    0
}

#[cfg(not(target_os = "fuchsia"))]
fn reset_term(stdout: &mut std::io::Stdout) {
    terminal::disable_raw_mode().unwrap();
    // Clear the prompt
    queue!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
    // Move cursor to the beginning without printing new line
    print!("\r");
    stdout.flush().unwrap();
}

#[cfg(target_os = "fuchsia")]
#[inline(always)]
fn reset_term(_: &mut usize) {}

fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>, silent: bool) {
    let (cols, rows) = terminal::size().unwrap();
    let lines = break_buff(buff, usize::from(cols));

    let mut pager = Pager::new(rows as usize, lines, next_file, silent);
    pager.draw(stdout, false);
    if pager.should_close() {
        return;
    }

    loop {
        let mut wrong_key = false;
        if event::poll(Duration::from_millis(10)).unwrap() {
            match event::read().unwrap() {
                Event::Key(KeyEvent {
                    code: KeyCode::Char('q'),
                    modifiers: KeyModifiers::NONE,
                })
                | Event::Key(KeyEvent {
                    code: KeyCode::Char('c'),
                    modifiers: KeyModifiers::CONTROL,
                }) => {
                    reset_term(&mut stdout);
                    std::process::exit(0);
                }
                Event::Key(KeyEvent {
                    code: KeyCode::Down,
                    modifiers: KeyModifiers::NONE,
                })
                | Event::Key(KeyEvent {
                    code: KeyCode::Char(' '),
                    modifiers: KeyModifiers::NONE,
                }) => {
                    if pager.should_close() {
                        return;
                    } else {
                        pager.page_down();
                    }
                }
                Event::Key(KeyEvent {
                    code: KeyCode::Up,
                    modifiers: KeyModifiers::NONE,
                }) => {
                    pager.page_up();
                }
                _ => {
                    wrong_key = true;
                }
            }

            pager.draw(stdout, wrong_key);
        }
    }
}

struct Pager<'a> {
    // The current line at the top of the screen
    upper_mark: usize,
    // The number of rows that fit on the screen
    content_rows: usize,
    lines: Vec<String>,
    next_file: Option<&'a str>,
    line_count: usize,
    silent: bool,
}

impl<'a> Pager<'a> {
    fn new(rows: usize, lines: Vec<String>, next_file: Option<&'a str>, silent: bool) -> Self {
        let line_count = lines.len();
        Self {
            upper_mark: 0,
            content_rows: rows.saturating_sub(1),
            lines,
            next_file,
            line_count,
            silent,
        }
    }

    fn should_close(&mut self) -> bool {
        self.upper_mark
            .saturating_add(self.content_rows)
            .eq(&self.line_count)
    }

    fn page_down(&mut self) {
        self.upper_mark = self
            .upper_mark
            .saturating_add(self.content_rows)
            .min(self.line_count.saturating_sub(self.content_rows));
    }

    fn page_up(&mut self) {
        self.upper_mark = self.upper_mark.saturating_sub(self.content_rows);
    }

    fn draw(&self, stdout: &mut std::io::Stdout, wrong_key: bool) {
        let lower_mark = self.line_count.min(self.upper_mark + self.content_rows);
        self.draw_lines(stdout);
        self.draw_prompt(stdout, lower_mark, wrong_key);
        stdout.flush().unwrap();
    }

    fn draw_lines(&self, stdout: &mut std::io::Stdout) {
        execute!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
        let displayed_lines = self
            .lines
            .iter()
            .skip(self.upper_mark)
            .take(self.content_rows);

        for line in displayed_lines {
            stdout
                .write_all(format!("\r{}\n", line).as_bytes())
                .unwrap();
        }
    }

    fn draw_prompt(&self, stdout: &mut Stdout, lower_mark: usize, wrong_key: bool) {
        let status_inner = if lower_mark == self.line_count {
            format!("Next file: {}", self.next_file.unwrap_or_default())
        } else {
            format!(
                "{}%",
                (lower_mark as f64 / self.line_count as f64 * 100.0).round() as u16
            )
        };

        let status = format!("--More--({})", status_inner);

        let banner = match (self.silent, wrong_key) {
            (true, true) => "[Press 'h' for instructions. (unimplemented)]".to_string(),
            (true, false) => format!("{}[Press space to continue, 'q' to quit.]", status),
            (false, true) => format!("{}{}", status, BELL),
            (false, false) => status,
        };

        write!(
            stdout,
            "\r{}{}{}",
            Attribute::Reverse,
            banner,
            Attribute::Reset
        )
        .unwrap();
    }
}

// Break the lines on the cols of the terminal
fn break_buff(buff: &str, cols: usize) -> Vec<String> {
    let mut lines = Vec::with_capacity(buff.lines().count());

    for l in buff.lines() {
        lines.append(&mut break_line(l, cols));
    }
    lines
}

fn break_line(line: &str, cols: usize) -> Vec<String> {
    let width = UnicodeWidthStr::width(line);
    let mut lines = Vec::new();
    if width < cols {
        lines.push(line.to_string());
        return lines;
    }

    let gr_idx = UnicodeSegmentation::grapheme_indices(line, true);
    let mut last_index = 0;
    let mut total_width = 0;
    for (index, grapheme) in gr_idx {
        let width = UnicodeWidthStr::width(grapheme);
        total_width += width;

        if total_width > cols {
            lines.push(line[last_index..index].to_string());
            last_index = index;
            total_width = width;
        }
    }

    if last_index != line.len() {
        lines.push(line[last_index..].to_string());
    }
    lines
}

#[cfg(test)]
mod tests {
    use super::break_line;
    use unicode_width::UnicodeWidthStr;

    #[test]
    fn test_break_lines_long() {
        let mut test_string = String::with_capacity(100);
        for _ in 0..200 {
            test_string.push('#');
        }

        let lines = break_line(&test_string, 80);
        let widths: Vec<usize> = lines
            .iter()
            .map(|s| UnicodeWidthStr::width(&s[..]))
            .collect();

        assert_eq!((80, 80, 40), (widths[0], widths[1], widths[2]));
    }

    #[test]
    fn test_break_lines_short() {
        let mut test_string = String::with_capacity(100);
        for _ in 0..20 {
            test_string.push('#');
        }

        let lines = break_line(&test_string, 80);

        assert_eq!(20, lines[0].len());
    }

    #[test]
    fn test_break_line_zwj() {
        let mut test_string = String::with_capacity(1100);
        for _ in 0..20 {
            test_string.push_str("👩🏻🔬");
        }

        let lines = break_line(&test_string, 80);

        let widths: Vec<usize> = lines
            .iter()
            .map(|s| UnicodeWidthStr::width(&s[..]))
            .collect();

        // Each 👩🏻🔬 is 6 character width it break line to the closest number to 80 => 6 * 13 = 78
        assert_eq!((78, 42), (widths[0], widths[1]));
    }
}
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use super::DynamicValueType;
use sys;

#[allow(missing_docs)]
#[derive(Debug)]
pub struct SBVariablesOptions {
    /// The underlying raw `SBVariablesOptionsRef`.
    pub raw: sys::SBVariablesOptionsRef,
}

impl SBVariablesOptions {
    /// Construct a new `SBVariablesOptions`.
    pub fn wrap(raw: sys::SBVariablesOptionsRef) -> SBVariablesOptions {
        SBVariablesOptions { raw: raw }
    }

    /// Construct a new `Some(SBVariablesOptions)` or `None`.
    pub fn maybe_wrap(raw: sys::SBVariablesOptionsRef) -> Option<SBVariablesOptions> {
        if unsafe { sys::SBVariablesOptionsIsValid(raw) != 0 } {
            Some(SBVariablesOptions { raw: raw })
        } else {
            None
        }
    }

    /// Check whether or not this is a valid `SBVariablesOptions` value.
    pub fn is_valid(&self) -> bool {
        unsafe { sys::SBVariablesOptionsIsValid(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn include_arguments(&self) -> bool {
        unsafe { sys::SBVariablesOptionsGetIncludeArguments(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn set_include_arguments(&mut self, arguments: bool) {
        unsafe { sys::SBVariablesOptionsSetIncludeArguments(self.raw, arguments as u8) };
    }

    #[allow(missing_docs)]
    pub fn include_locals(&self) -> bool {
        unsafe { sys::SBVariablesOptionsGetIncludeLocals(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn set_include_locals(&mut self, locals: bool) {
        unsafe { sys::SBVariablesOptionsSetIncludeLocals(self.raw, locals as u8) };
    }

    #[allow(missing_docs)]
    pub fn include_statics(&self) -> bool {
        unsafe { sys::SBVariablesOptionsGetIncludeStatics(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn set_include_statics(&mut self, statics: bool) {
        unsafe { sys::SBVariablesOptionsSetIncludeStatics(self.raw, statics as u8) };
    }

    #[allow(missing_docs)]
    pub fn in_scope_only(&self) -> bool {
        unsafe { sys::SBVariablesOptionsGetInScopeOnly(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn set_in_scope_only(&mut self, in_scope_only: bool) {
        unsafe { sys::SBVariablesOptionsSetInScopeOnly(self.raw, in_scope_only as u8) };
    }

    #[allow(missing_docs)]
    pub fn include_runtime_support_values(&self) -> bool {
        unsafe { sys::SBVariablesOptionsGetIncludeRuntimeSupportValues(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn set_include_runtime_support_values(&mut self, include: bool) {
        unsafe { sys::SBVariablesOptionsSetIncludeRuntimeSupportValues(self.raw, include as u8) };
    }

    #[allow(missing_docs)]
    pub fn use_dynamic(&self) -> DynamicValueType {
        unsafe { sys::SBVariablesOptionsGetUseDynamic(self.raw) }
    }

    #[allow(missing_docs)]
    pub fn set_use_dynamic(&mut self, use_dynamic: DynamicValueType) {
        unsafe { sys::SBVariablesOptionsSetUseDynamic(self.raw, use_dynamic) };
    }
}

impl Drop for SBVariablesOptions {
    fn drop(&mut self) {
        unsafe { sys::DisposeSBVariablesOptions(self.raw) };
    }
}
<commit_msg>Allow constructing an empty SBVariablesOptions.<commit_after>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use super::DynamicValueType;
use sys;

#[allow(missing_docs)]
#[derive(Debug)]
pub struct SBVariablesOptions {
    /// The underlying raw `SBVariablesOptionsRef`.
    pub raw: sys::SBVariablesOptionsRef,
}

impl SBVariablesOptions {
    /// Construct a new `SBVariablesOptions`.
    pub fn new() -> SBVariablesOptions {
        SBVariablesOptions::wrap(unsafe { sys::CreateSBVariablesOptions() })
    }

    /// Construct a new `SBVariablesOptions`.
    pub fn wrap(raw: sys::SBVariablesOptionsRef) -> SBVariablesOptions {
        SBVariablesOptions { raw: raw }
    }

    /// Construct a new `Some(SBVariablesOptions)` or `None`.
    pub fn maybe_wrap(raw: sys::SBVariablesOptionsRef) -> Option<SBVariablesOptions> {
        if unsafe { sys::SBVariablesOptionsIsValid(raw) != 0 } {
            Some(SBVariablesOptions { raw: raw })
        } else {
            None
        }
    }

    /// Check whether or not this is a valid `SBVariablesOptions` value.
    pub fn is_valid(&self) -> bool {
        unsafe { sys::SBVariablesOptionsIsValid(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn include_arguments(&self) -> bool {
        unsafe { sys::SBVariablesOptionsGetIncludeArguments(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn set_include_arguments(&mut self, arguments: bool) {
        unsafe { sys::SBVariablesOptionsSetIncludeArguments(self.raw, arguments as u8) };
    }

    #[allow(missing_docs)]
    pub fn include_locals(&self) -> bool {
        unsafe { sys::SBVariablesOptionsGetIncludeLocals(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn set_include_locals(&mut self, locals: bool) {
        unsafe { sys::SBVariablesOptionsSetIncludeLocals(self.raw, locals as u8) };
    }

    #[allow(missing_docs)]
    pub fn include_statics(&self) -> bool {
        unsafe { sys::SBVariablesOptionsGetIncludeStatics(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn set_include_statics(&mut self, statics: bool) {
        unsafe { sys::SBVariablesOptionsSetIncludeStatics(self.raw, statics as u8) };
    }

    #[allow(missing_docs)]
    pub fn in_scope_only(&self) -> bool {
        unsafe { sys::SBVariablesOptionsGetInScopeOnly(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn set_in_scope_only(&mut self, in_scope_only: bool) {
        unsafe { sys::SBVariablesOptionsSetInScopeOnly(self.raw, in_scope_only as u8) };
    }

    #[allow(missing_docs)]
    pub fn include_runtime_support_values(&self) -> bool {
        unsafe { sys::SBVariablesOptionsGetIncludeRuntimeSupportValues(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn set_include_runtime_support_values(&mut self, include: bool) {
        unsafe { sys::SBVariablesOptionsSetIncludeRuntimeSupportValues(self.raw, include as u8) };
    }

    #[allow(missing_docs)]
    pub fn use_dynamic(&self) -> DynamicValueType {
        unsafe { sys::SBVariablesOptionsGetUseDynamic(self.raw) }
    }

    #[allow(missing_docs)]
    pub fn set_use_dynamic(&mut self, use_dynamic: DynamicValueType) {
        unsafe { sys::SBVariablesOptionsSetUseDynamic(self.raw, use_dynamic) };
    }
}

impl Drop for SBVariablesOptions {
    fn drop(&mut self) {
        unsafe { sys::DisposeSBVariablesOptions(self.raw) };
    }
}
<|endoftext|>


<commit_before>use structs::Point;
use traits::{HasValues, HasWalls};

/// A prism is a point with walls
#[derive(Debug)]
pub struct Prism {

  /// Strength of the east wall
  pub east: i32,

  /// Strength of the southeast wall
  pub southeast: i32,

  /// Strength of the southwest wall
  pub southwest: i32,

  /// Strength of the floor
  pub down: i32,

  /// The point surrounded by walls
  pub point: Point,

}

impl Prism {

  /// Factory function for making new prisms
  ///
  /// # Example
  ///
  /// ```
  /// use hex_math::{Point, Prism};
  ///
  /// let point: Point = Point::new(1, 2, 5);
  /// let prism: Prism = Prism::new(point, 1, 1, 1, 1);
  /// ```
  pub fn new(
    point: Point,
    east: i32,
    southeast: i32,
    southwest: i32,
    down: i32
  ) -> Prism {
    Prism {
      point: point,
      east: east,
      southeast: southeast,
      southwest: southwest,
      down: down,
    }
  }

}

/// Access the prism's coordinate values
///
/// # Example
///
/// ```
/// use hex_math::{Point, Prism, HasValues};
///
/// let point: Point = Point::new(1, 2, 5);
/// let prism: Prism = Prism::new(point, 1, 1, 1, 1);
///
/// assert_eq!((1, 2, 5), prism.values());
/// ```
impl HasValues for Prism {

  fn values(&self) -> (i32, i32, i32) {
    self.point.values()
  }

}

/// Access the prism's wall strength values
///
/// # Example
///
/// ```
/// use hex_math::{Point, Prism, HasWalls};
///
/// let point: Point = Point::new(1, 2, 5);
/// let prism: Prism = Prism::new(point, 1, 1, 1, 1);
///
/// assert_eq!((1, 1, 1, 1), prism.walls());
/// ```
impl HasWalls for Prism {

  fn walls(&self) -> (i32, i32, i32, i32) {
    (self.east, self.southeast, self.southwest, self.down)
  }

}

/// Convert from a point to a prism with zero walls
///
/// # Example
///
/// ```
/// use hex_math::{Point, Prism, HasValues, HasWalls};
///
/// let point: Point = Point::new(1, 2, 5);
/// let prism: Prism = Prism::from(&point);
///
/// assert_eq!((1, 2, 5), prism.values());
/// assert_eq!((0, 0, 0, 0), prism.walls());
/// ```
impl<'a> From<&'a Point> for Prism {

  fn from(point: &'a Point) -> Prism {
    Prism::new(Point::from(point.values()), 0, 0, 0, 0)
  }

}

/// Convert from tuples of values and wall strengths
///
/// # Example
///
/// ```
/// use hex_math::{Point, Prism, HasValues, HasWalls};
///
/// let point: Point = Point::new(1, 2, 5);
/// let values: (i32, i32, i32) = point.values();
/// let prism: Prism = Prism::new(point, 1, 1, 1, 1);
/// let other: Prism = Prism::from((values, prism.walls()));
///
/// assert_eq!((1, 2, 5), other.values());
/// assert_eq!((1, 1, 1, 1), other.walls());
/// ```
impl From<((i32, i32, i32), (i32, i32, i32, i32))> for Prism {

  fn from(
    (values, (e, se, sw, d)): ((i32, i32, i32), (i32, i32, i32, i32))
  ) -> Prism {
    Prism::new(Point::from(values), e, se, sw, d)
  }

}
<commit_msg>Issue #33: Refactor Prism<commit_after>use structs::Point;
use traits::{HasValues, HasWalls};

/// A prism is a point with walls
#[derive(Debug)]
pub struct Prism {

  /// Strength of the east wall
  pub east: i32,

  /// Strength of the southeast wall
  pub southeast: i32,

  /// Strength of the southwest wall
  pub southwest: i32,

  /// Strength of the floor
  pub down: i32,

  /// The point surrounded by walls
  pub point: Point,

}

impl Prism {

  /// Factory function for making new prisms
  pub fn new(
    point: Point,
    east: i32,
    southeast: i32,
    southwest: i32,
    down: i32
  ) -> Prism {
    Prism {
      point: point,
      east: east,
      southeast: southeast,
      southwest: southwest,
      down: down,
    }
  }

}

/// Access the prism's coordinate values
impl HasValues for Prism {

  fn values(&self) -> (i32, i32, i32) {
    self.point.values()
  }

}

/// Access the prism's wall strength values
impl HasWalls for Prism {

  fn walls(&self) -> (i32, i32, i32, i32) {
    (self.east, self.southeast, self.southwest, self.down)
  }

}

/// Convert from a point to a prism with zero walls
impl<'a> From<&'a Point> for Prism {

  fn from(point: &'a Point) -> Prism {
    Prism::new(Point::from(point.values()), 0, 0, 0, 0)
  }

}

/// Convert from tuples of values and wall strengths
impl From<((i32, i32, i32), (i32, i32, i32, i32))> for Prism {

  fn from(
    (values, (e, se, sw, d)): ((i32, i32, i32), (i32, i32, i32, i32))
  ) -> Prism {
    Prism::new(Point::from(values), e, se, sw, d)
  }

}

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

  #[test]
  fn new() {
    let point: Point = Point::new(1, 2, 5);
    let prism: Prism = Prism::new(point, 1, 1, 1, 1);

    assert!(1 == prism.point.q);
    assert!(2 == prism.point.r);
    assert!(5 == prism.point.t);
    assert!(1 == prism.east);
    assert!(1 == prism.southeast);
    assert!(1 == prism.southwest);
    assert!(1 == prism.down);
  }

  #[test]
  fn values() {
    let point: Point = Point::new(1, 2, 5);
    let (q, r, t) = Prism::new(point, 1, 1, 1, 1).values();

    assert!(1 == q);
    assert!(2 == r);
    assert!(5 == t);
  }

  #[test]
  fn walls() {
    let point: Point = Point::new(1, 2, 5);
    let (s, se, sw, d) = Prism::new(point, 1, 1, 1, 1).walls();

    assert!(1 == s);
    assert!(1 == se);
    assert!(1 == sw);
    assert!(1 == d);
  }

  #[test]
  fn from_point() {
    let point: Point = Point::new(1, 2, 5);
    let prism: Prism = Prism::from(&point);

    assert!(1 == prism.point.q);
    assert!(2 == prism.point.r);
    assert!(5 == prism.point.t);
    assert!(0 == prism.east);
    assert!(0 == prism.southeast);
    assert!(0 == prism.southwest);
    assert!(0 == prism.down);
  }

  #[test]
  fn from_i32_tuples() {
    let prism: Prism = Prism::from(((1, 2, 5), (1, 1, 1, 1)));

    assert!(1 == prism.point.q);
    assert!(2 == prism.point.r);
    assert!(5 == prism.point.t);
    assert!(1 == prism.east);
    assert!(1 == prism.southeast);
    assert!(1 == prism.southwest);
    assert!(1 == prism.down);
  }
}
<|endoftext|>


<commit_before>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # Symbol reader
//!
//! A symbol reader reads one symbol at a time from a source
//!

use std::slice::Iter;
use std::io::*;

///
/// A symbol reader reads one symbol at a time from a source
///
pub trait SymbolReader<Symbol> {
    ///
    /// Reads the next symbol from this reader
    ///
    fn next_symbol(&mut self) -> Option<Symbol>;
}

///
/// Trait that can be implemented by things that can construct a symbol reader
///
pub trait SymbolSource<'a, Symbol> {
    /// The type of symbol reader that will be generated by this call
    type SymbolReader: SymbolReader<Symbol>+'a;

    /// Returns a new object that can read the symbols from this one
    fn read_symbols(self) -> Self::SymbolReader;
}

impl<'a, Symbol: Clone+'a> SymbolSource<'a, Symbol> for &'a Vec<Symbol> {
    type SymbolReader = Iter<'a, Symbol>;

    fn read_symbols(self) -> Self::SymbolReader {
        self.iter()
    }
}

impl<'a, Symbol: Clone+'a> SymbolReader<Symbol> for Iter<'a, Symbol> {
    fn next_symbol(&mut self) -> Option<Symbol> {
        if let Some(sym) = self.next() {
            Some(sym.clone())
        } else {
            None
        }
    }
}

// Can read from streams

/*
impl<'a> SymbolSource<'a, u8> for Read {
    type SymbolReader = ByteReader<Self>;

    fn read_symbols(self) -> Self::SymbolReader {
        ByteReader::new(self.bytes())
    }
}
*/

pub struct ByteReader<Reader: Read> {
    bytes: Bytes<Reader>
}

impl<Reader: Read> ByteReader<Reader> {
    pub fn new(bytes: Bytes<Reader>) -> ByteReader<Reader> {
        ByteReader { bytes: bytes }
    }
}

impl<Reader: Read> SymbolReader<u8> for ByteReader<Reader> {
    fn next_symbol(&mut self) -> Option<u8> {
        if let Some(Ok(sym)) = self.bytes.next() {
            Some(sym.clone())
        } else {
            None
        }
    }
}

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

    #[test]
    fn can_read_from_vec() {
        let source      = vec![1, 2, 3];
        let mut reader  = source.read_symbols();

        assert!(reader.next_symbol() == Some(1));
        assert!(reader.next_symbol() == Some(2));
        assert!(reader.next_symbol() == Some(3));
        assert!(reader.next_symbol() == Nonecar);
    }
}
<commit_msg>Test the byte symbol reader<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # Symbol reader
//!
//! A symbol reader reads one symbol at a time from a source
//!

use std::slice::Iter;
use std::io::*;

///
/// A symbol reader reads one symbol at a time from a source
///
pub trait SymbolReader<Symbol> {
    ///
    /// Reads the next symbol from this reader
    ///
    fn next_symbol(&mut self) -> Option<Symbol>;
}

///
/// Trait that can be implemented by things that can construct a symbol reader
///
pub trait SymbolSource<'a, Symbol> {
    /// The type of symbol reader that will be generated by this call
    type SymbolReader: SymbolReader<Symbol>+'a;

    /// Returns a new object that can read the symbols from this one
    fn read_symbols(self) -> Self::SymbolReader;
}

impl<'a, Symbol: Clone+'a> SymbolSource<'a, Symbol> for &'a Vec<Symbol> {
    type SymbolReader = Iter<'a, Symbol>;

    fn read_symbols(self) -> Self::SymbolReader {
        self.iter()
    }
}

impl<'a, Symbol: Clone+'a> SymbolReader<Symbol> for Iter<'a, Symbol> {
    fn next_symbol(&mut self) -> Option<Symbol> {
        if let Some(sym) = self.next() {
            Some(sym.clone())
        } else {
            None
        }
    }
}

// Can read from streams

/*
impl<'a> SymbolSource<'a, u8> for Read {
    type SymbolReader = ByteReader<Self>;

    fn read_symbols(self) -> Self::SymbolReader {
        ByteReader::new(self.bytes())
    }
}
*/

///
/// The ByteReader turns a `std::io::Bytes` object into a symbol reader
///
pub struct ByteReader<Reader: Read> {
    bytes: Bytes<Reader>
}

impl<Reader: Read> ByteReader<Reader> {
    pub fn new(bytes: Bytes<Reader>) -> ByteReader<Reader> {
        ByteReader { bytes: bytes }
    }
}

impl<Reader: Read> SymbolReader<u8> for ByteReader<Reader> {
    fn next_symbol(&mut self) -> Option<u8> {
        if let Some(Ok(sym)) = self.bytes.next() {
            Some(sym.clone())
        } else {
            None
        }
    }
}

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

    #[test]
    fn can_read_from_vec() {
        let source      = vec![1, 2, 3];
        let mut reader  = source.read_symbols();

        assert!(reader.next_symbol() == Some(1));
        assert!(reader.next_symbol() == Some(2));
        assert!(reader.next_symbol() == Some(3));
        assert!(reader.next_symbol() == None);
    }

    #[test]
    fn can_read_from_bytes_reader() {
        let array: [u8; 3] = [1, 2, 3];
        let slice = &array[..];
        let bytes = slice.bytes();
        let mut reader = ByteReader::new(bytes);

        assert!(reader.next_symbol() == Some(1));
        assert!(reader.next_symbol() == Some(2));
        assert!(reader.next_symbol() == Some(3));
        assert!(reader.next_symbol() == None);
    }
}
<|endoftext|>