<commit_before>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the MIT License, <LICENSE or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed except according to those terms.

use bincode::{self, SizeLimit};
use bincode::serde::{deserialize_from, serialize_into};
use serde;
use std::io::{self, Read, Write};
use std::convert;
use std::sync::Arc;
use std::time::Duration;

mod client;
mod server;
mod packet;

pub use self::packet::Packet;
pub use self::client::{Client, Future};
pub use self::server::{Serve, ServeHandle};

/// Client errors that can occur during rpc calls
#[derive(Debug, Clone)]
pub enum Error {
    /// An IO-related error
    Io(Arc<io::Error>),
    /// The server hung up.
    ConnectionBroken,
}

impl convert::From<bincode::serde::SerializeError> for Error {
    fn from(err: bincode::serde::SerializeError) -> Error {
        match err {
            bincode::serde::SerializeError::IoError(err) => Error::Io(Arc::new(err)),
            err => panic!("Unexpected error during serialization: {:?}", err),
        }
    }
}

impl convert::From<bincode::serde::DeserializeError> for Error {
    fn from(err: bincode::serde::DeserializeError) -> Error {
        match err {
            bincode::serde::DeserializeError::IoError(ref err)
                if err.kind() == io::ErrorKind::ConnectionReset => Error::ConnectionBroken,
            bincode::serde::DeserializeError::EndOfStreamError => Error::ConnectionBroken,
            bincode::serde::DeserializeError::IoError(err) => Error::Io(Arc::new(err)),
            err => panic!("Unexpected error during deserialization: {:?}", err),
        }
    }
}

impl convert::From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(Arc::new(err))
    }
}

/// Configuration for client and server.
#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub struct Config {
    /// Request/Response timeout between packet delivery.
    pub timeout: Option<Duration>,
}

/// Return type of rpc calls: either the successful return value, or a client error.
pub type Result<T> = ::std::result::Result<T, Error>;

trait Deserialize: Read + Sized {
    fn deserialize<T: serde::Deserialize>(&mut self) -> Result<T> {
        deserialize_from(self, SizeLimit::Infinite).map_err(Error::from)
    }
}

impl<R: Read> Deserialize for R {}

trait Serialize: Write + Sized {
    fn serialize<T: serde::Serialize>(&mut self, value: &T) -> Result<()> {
        try!(serialize_into(self, value, SizeLimit::Infinite));
        try!(self.flush());
        Ok(())
    }
}

impl<W: Write> Serialize for W {}

#[cfg(test)]
mod test {
    extern crate env_logger;
    use super::{Client, Config, Serve};
    use scoped_pool::Pool;
    use std::sync::{Arc, Barrier, Mutex};
    use std::thread;
    use std::time::Duration;

    fn test_timeout() -> Option<Duration> {
        Some(Duration::from_secs(1))
    }

    struct Server {
        counter: Mutex<u64>,
    }

    impl Serve for Server {
        type Request = ();
        type Reply = u64;

        fn serve(&self, _: ()) -> u64 {
            let mut counter = self.counter.lock().unwrap();
            let reply = *counter;
            *counter += 1;
            reply
        }
    }

    impl Server {
        fn new() -> Server {
            Server { counter: Mutex::new(0) }
        }

        fn count(&self) -> u64 {
            *self.counter.lock().unwrap()
        }
    }

    #[test]
    fn handle() {
        let _ = env_logger::init();
        let server = Arc::new(Server::new());
        let serve_handle = server.spawn("localhost:0").unwrap();
        let client: Client<(), u64> = Client::new(serve_handle.local_addr()).unwrap();
        drop(client);
        serve_handle.shutdown();
    }

    #[test]
    fn simple() {
        let _ = env_logger::init();
        let server = Arc::new(Server::new());
        let serve_handle = server.clone().spawn("localhost:0").unwrap();
        let addr = serve_handle.local_addr().clone();
        // The explicit type is required so that it doesn't deserialize a u32 instead of u64
        let client: Client<(), u64> = Client::new(addr).unwrap();
        assert_eq!(0, client.rpc(()).unwrap());
        assert_eq!(1, server.count());
        assert_eq!(1, client.rpc(()).unwrap());
        assert_eq!(2, server.count());
        drop(client);
        serve_handle.shutdown();
    }

    struct BarrierServer {
        barrier: Barrier,
        inner: Server,
    }

    impl Serve for BarrierServer {
        type Request = ();
        type Reply = u64;
        fn serve(&self, request: ()) -> u64 {
            self.barrier.wait();
            self.inner.serve(request)
        }
    }

    impl BarrierServer {
        fn new(n: usize) -> BarrierServer {
            BarrierServer {
                barrier: Barrier::new(n),
                inner: Server::new(),
            }
        }

        fn count(&self) -> u64 {
            self.inner.count()
        }
    }

    #[test]
    fn force_shutdown() {
        let _ = env_logger::init();
        let server = Arc::new(Server::new());
        let serve_handle = server.spawn_with_config("localhost:0",
                                                    Config {
                                                        timeout: Some(Duration::new(0, 10))
                                                    })
                                 .unwrap();
        let addr = serve_handle.local_addr().clone();
        let client: Client<(), u64> = Client::new(addr).unwrap();
        let thread = thread::spawn(move || serve_handle.shutdown());
        info!("force_shutdown:: rpc1: {:?}", client.rpc(()));
        thread.join().unwrap();
    }

    #[test]
    fn client_failed_rpc() {
        let _ = env_logger::init();
        let server = Arc::new(Server::new());
        let serve_handle = server.spawn_with_config("localhost:0",
                                                    Config {
                                                        timeout: test_timeout(),
                                                    })
                                 .unwrap();
        let addr = serve_handle.local_addr().clone();
        let client: Arc<Client<(), u64>> = Arc::new(Client::new(addr).unwrap());
        client.rpc(()).unwrap();
        serve_handle.shutdown();
        match client.rpc(()) {
            Err(super::Error::ConnectionBroken) => {} // success
            otherwise => panic!("Expected Err(ConnectionBroken), got {:?}", otherwise),
        }
        let _ = client.rpc(()); // Test whether second failure hangs
    }

    #[test]
    fn concurrent() {
        let _ = env_logger::init();
        let concurrency = 10;
        let pool = Pool::new(concurrency);
        let server = Arc::new(BarrierServer::new(concurrency));
        let serve_handle = server.clone().spawn("localhost:0").unwrap();
        let addr = serve_handle.local_addr().clone();
        let client: Client<(), u64> = Client::new(addr).unwrap();
        pool.scoped(|scope| {
            for _ in 0..concurrency {
                let client = client.try_clone().unwrap();
                scope.execute(move || {
                    client.rpc(()).unwrap();
                });
            }
        });
        assert_eq!(concurrency as u64, server.count());
        drop(client);
        serve_handle.shutdown();
    }

    #[test]
    fn async() {
        let _ = env_logger::init();
        let server = Arc::new(Server::new());
        let serve_handle = server.spawn("localhost:0").unwrap();
        let addr = serve_handle.local_addr().clone();
        let client: Client<(), u64> = Client::new(addr).unwrap();

        // Drop future immediately; does the reader channel panic when sending?
        client.rpc_async(());
        // If the reader panicked, this won't succeed
        client.rpc_async(());

        drop(client);
        serve_handle.shutdown();
    }
}
<commit_msg>Remove a bunch of derived traits from Config for backwards compatibility reasons.<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the MIT License, <LICENSE or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed except according to those terms.

use bincode::{self, SizeLimit};
use bincode::serde::{deserialize_from, serialize_into};
use serde;
use std::io::{self, Read, Write};
use std::convert;
use std::sync::Arc;
use std::time::Duration;

mod client;
mod server;
mod packet;

pub use self::packet::Packet;
pub use self::client::{Client, Future};
pub use self::server::{Serve, ServeHandle};

/// Client errors that can occur during rpc calls
#[derive(Debug, Clone)]
pub enum Error {
    /// An IO-related error
    Io(Arc<io::Error>),
    /// The server hung up.
    ConnectionBroken,
}

impl convert::From<bincode::serde::SerializeError> for Error {
    fn from(err: bincode::serde::SerializeError) -> Error {
        match err {
            bincode::serde::SerializeError::IoError(err) => Error::Io(Arc::new(err)),
            err => panic!("Unexpected error during serialization: {:?}", err),
        }
    }
}

impl convert::From<bincode::serde::DeserializeError> for Error {
    fn from(err: bincode::serde::DeserializeError) -> Error {
        match err {
            bincode::serde::DeserializeError::IoError(ref err)
                if err.kind() == io::ErrorKind::ConnectionReset => Error::ConnectionBroken,
            bincode::serde::DeserializeError::EndOfStreamError => Error::ConnectionBroken,
            bincode::serde::DeserializeError::IoError(err) => Error::Io(Arc::new(err)),
            err => panic!("Unexpected error during deserialization: {:?}", err),
        }
    }
}

impl convert::From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(Arc::new(err))
    }
}

/// Configuration for client and server.
#[derive(Debug, Default)]
pub struct Config {
    /// Request/Response timeout between packet delivery.
    pub timeout: Option<Duration>,
}

/// Return type of rpc calls: either the successful return value, or a client error.
pub type Result<T> = ::std::result::Result<T, Error>;

trait Deserialize: Read + Sized {
    fn deserialize<T: serde::Deserialize>(&mut self) -> Result<T> {
        deserialize_from(self, SizeLimit::Infinite).map_err(Error::from)
    }
}

impl<R: Read> Deserialize for R {}

trait Serialize: Write + Sized {
    fn serialize<T: serde::Serialize>(&mut self, value: &T) -> Result<()> {
        try!(serialize_into(self, value, SizeLimit::Infinite));
        try!(self.flush());
        Ok(())
    }
}

impl<W: Write> Serialize for W {}

#[cfg(test)]
mod test {
    extern crate env_logger;
    use super::{Client, Config, Serve};
    use scoped_pool::Pool;
    use std::sync::{Arc, Barrier, Mutex};
    use std::thread;
    use std::time::Duration;

    fn test_timeout() -> Option<Duration> {
        Some(Duration::from_secs(1))
    }

    struct Server {
        counter: Mutex<u64>,
    }

    impl Serve for Server {
        type Request = ();
        type Reply = u64;

        fn serve(&self, _: ()) -> u64 {
            let mut counter = self.counter.lock().unwrap();
            let reply = *counter;
            *counter += 1;
            reply
        }
    }

    impl Server {
        fn new() -> Server {
            Server { counter: Mutex::new(0) }
        }

        fn count(&self) -> u64 {
            *self.counter.lock().unwrap()
        }
    }

    #[test]
    fn handle() {
        let _ = env_logger::init();
        let server = Arc::new(Server::new());
        let serve_handle = server.spawn("localhost:0").unwrap();
        let client: Client<(), u64> = Client::new(serve_handle.local_addr()).unwrap();
        drop(client);
        serve_handle.shutdown();
    }

    #[test]
    fn simple() {
        let _ = env_logger::init();
        let server = Arc::new(Server::new());
        let serve_handle = server.clone().spawn("localhost:0").unwrap();
        let addr = serve_handle.local_addr().clone();
        // The explicit type is required so that it doesn't deserialize a u32 instead of u64
        let client: Client<(), u64> = Client::new(addr).unwrap();
        assert_eq!(0, client.rpc(()).unwrap());
        assert_eq!(1, server.count());
        assert_eq!(1, client.rpc(()).unwrap());
        assert_eq!(2, server.count());
        drop(client);
        serve_handle.shutdown();
    }

    struct BarrierServer {
        barrier: Barrier,
        inner: Server,
    }

    impl Serve for BarrierServer {
        type Request = ();
        type Reply = u64;
        fn serve(&self, request: ()) -> u64 {
            self.barrier.wait();
            self.inner.serve(request)
        }
    }

    impl BarrierServer {
        fn new(n: usize) -> BarrierServer {
            BarrierServer {
                barrier: Barrier::new(n),
                inner: Server::new(),
            }
        }

        fn count(&self) -> u64 {
            self.inner.count()
        }
    }

    #[test]
    fn force_shutdown() {
        let _ = env_logger::init();
        let server = Arc::new(Server::new());
        let serve_handle = server.spawn_with_config("localhost:0",
                                                    Config {
                                                        timeout: Some(Duration::new(0, 10))
                                                    })
                                 .unwrap();
        let addr = serve_handle.local_addr().clone();
        let client: Client<(), u64> = Client::new(addr).unwrap();
        let thread = thread::spawn(move || serve_handle.shutdown());
        info!("force_shutdown:: rpc1: {:?}", client.rpc(()));
        thread.join().unwrap();
    }

    #[test]
    fn client_failed_rpc() {
        let _ = env_logger::init();
        let server = Arc::new(Server::new());
        let serve_handle = server.spawn_with_config("localhost:0",
                                                    Config {
                                                        timeout: test_timeout(),
                                                    })
                                 .unwrap();
        let addr = serve_handle.local_addr().clone();
        let client: Arc<Client<(), u64>> = Arc::new(Client::new(addr).unwrap());
        client.rpc(()).unwrap();
        serve_handle.shutdown();
        match client.rpc(()) {
            Err(super::Error::ConnectionBroken) => {} // success
            otherwise => panic!("Expected Err(ConnectionBroken), got {:?}", otherwise),
        }
        let _ = client.rpc(()); // Test whether second failure hangs
    }

    #[test]
    fn concurrent() {
        let _ = env_logger::init();
        let concurrency = 10;
        let pool = Pool::new(concurrency);
        let server = Arc::new(BarrierServer::new(concurrency));
        let serve_handle = server.clone().spawn("localhost:0").unwrap();
        let addr = serve_handle.local_addr().clone();
        let client: Client<(), u64> = Client::new(addr).unwrap();
        pool.scoped(|scope| {
            for _ in 0..concurrency {
                let client = client.try_clone().unwrap();
                scope.execute(move || {
                    client.rpc(()).unwrap();
                });
            }
        });
        assert_eq!(concurrency as u64, server.count());
        drop(client);
        serve_handle.shutdown();
    }

    #[test]
    fn async() {
        let _ = env_logger::init();
        let server = Arc::new(Server::new());
        let serve_handle = server.spawn("localhost:0").unwrap();
        let addr = serve_handle.local_addr().clone();
        let client: Client<(), u64> = Client::new(addr).unwrap();

        // Drop future immediately; does the reader channel panic when sending?
        client.rpc_async(());
        // If the reader panicked, this won't succeed
        client.rpc_async(());

        drop(client);
        serve_handle.shutdown();
    }
}
<|endoftext|>


<commit_before>//! `TypeFoldable` implementations for MIR types

use super::*;
use crate::ty;

CloneTypeFoldableAndLiftImpls! {
    BlockTailInfo,
    MirPhase,
    SourceInfo,
    FakeReadCause,
    RetagKind,
    SourceScope,
    SourceScopeLocalData,
    UserTypeAnnotationIndex,
}

impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        use crate::mir::TerminatorKind::*;

        let kind = match self.kind {
            Goto { target } => Goto { target },
            SwitchInt { ref discr, switch_ty, ref targets } => SwitchInt {
                discr: discr.fold_with(folder),
                switch_ty: switch_ty.fold_with(folder),
                targets: targets.clone(),
            },
            Drop { ref place, target, unwind } => {
                Drop { place: place.fold_with(folder), target, unwind }
            }
            DropAndReplace { ref place, ref value, target, unwind } => DropAndReplace {
                place: place.fold_with(folder),
                value: value.fold_with(folder),
                target,
                unwind,
            },
            Yield { ref value, resume, ref resume_arg, drop } => Yield {
                value: value.fold_with(folder),
                resume,
                resume_arg: resume_arg.fold_with(folder),
                drop,
            },
            Call { ref func, ref args, ref destination, cleanup, from_hir_call, fn_span } => {
                let dest =
                    destination.as_ref().map(|&(ref loc, dest)| (loc.fold_with(folder), dest));

                Call {
                    func: func.fold_with(folder),
                    args: args.fold_with(folder),
                    destination: dest,
                    cleanup,
                    from_hir_call,
                    fn_span,
                }
            }
            Assert { ref cond, expected, ref msg, target, cleanup } => {
                use AssertKind::*;
                let msg = match msg {
                    BoundsCheck { len, index } => {
                        BoundsCheck { len: len.fold_with(folder), index: index.fold_with(folder) }
                    }
                    Overflow(op, l, r) => Overflow(*op, l.fold_with(folder), r.fold_with(folder)),
                    OverflowNeg(op) => OverflowNeg(op.fold_with(folder)),
                    DivisionByZero(op) => DivisionByZero(op.fold_with(folder)),
                    RemainderByZero(op) => RemainderByZero(op.fold_with(folder)),
                    ResumedAfterReturn(_) | ResumedAfterPanic(_) => msg.clone(),
                };
                Assert { cond: cond.fold_with(folder), expected, msg, target, cleanup }
            }
            GeneratorDrop => GeneratorDrop,
            Resume => Resume,
            Abort => Abort,
            Return => Return,
            Unreachable => Unreachable,
            FalseEdge { real_target, imaginary_target } => {
                FalseEdge { real_target, imaginary_target }
            }
            FalseUnwind { real_target, unwind } => FalseUnwind { real_target, unwind },
            InlineAsm { template, ref operands, options, line_spans, destination } => InlineAsm {
                template,
                operands: operands.fold_with(folder),
                options,
                line_spans,
                destination,
            },
        };
        Terminator { source_info: self.source_info, kind }
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        use crate::mir::TerminatorKind::*;

        match self.kind {
            SwitchInt { ref discr, switch_ty, .. } => {
                discr.visit_with(visitor)?;
                switch_ty.visit_with(visitor)
            }
            Drop { ref place, .. } => place.visit_with(visitor),
            DropAndReplace { ref place, ref value, .. } => {
                place.visit_with(visitor)?;
                value.visit_with(visitor)
            }
            Yield { ref value, .. } => value.visit_with(visitor),
            Call { ref func, ref args, ref destination, .. } => {
                if let Some((ref loc, _)) = *destination {
                    loc.visit_with(visitor)?;
                };
                func.visit_with(visitor)?;
                args.visit_with(visitor)
            }
            Assert { ref cond, ref msg, .. } => {
                if cond.visit_with(visitor).is_break() {
                    use AssertKind::*;
                    match msg {
                        BoundsCheck { ref len, ref index } => {
                            len.visit_with(visitor)?;
                            index.visit_with(visitor)
                        }
                        Overflow(_, l, r) => {
                            l.visit_with(visitor)?;
                            r.visit_with(visitor)
                        }
                        OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => {
                            op.visit_with(visitor)
                        }
                        ResumedAfterReturn(_) | ResumedAfterPanic(_) => ControlFlow::CONTINUE,
                    }
                } else {
                    ControlFlow::CONTINUE
                }
            }
            InlineAsm { ref operands, .. } => operands.visit_with(visitor),
            Goto { .. }
            | Resume
            | Abort
            | Return
            | GeneratorDrop
            | Unreachable
            | FalseEdge { .. }
            | FalseUnwind { .. } => ControlFlow::CONTINUE,
        }
    }
}

impl<'tcx> TypeFoldable<'tcx> for GeneratorKind {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, _: &mut F) -> Self {
        *self
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<()> {
        ControlFlow::CONTINUE
    }
}

impl<'tcx> TypeFoldable<'tcx> for Place<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        Place { local: self.local.fold_with(folder), projection: self.projection.fold_with(folder) }
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        self.local.visit_with(visitor)?;
        self.projection.visit_with(visitor)
    }
}

impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<PlaceElem<'tcx>> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        let v = self.iter().map(|t| t.fold_with(folder)).collect::<Vec<_>>();
        folder.tcx().intern_place_elems(&v)
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        self.iter().try_for_each(|t| t.visit_with(visitor))
    }
}

impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        use crate::mir::Rvalue::*;
        match *self {
            Use(ref op) => Use(op.fold_with(folder)),
            Repeat(ref op, len) => Repeat(op.fold_with(folder), len.fold_with(folder)),
            ThreadLocalRef(did) => ThreadLocalRef(did.fold_with(folder)),
            Ref(region, bk, ref place) => {
                Ref(region.fold_with(folder), bk, place.fold_with(folder))
            }
            AddressOf(mutability, ref place) => AddressOf(mutability, place.fold_with(folder)),
            Len(ref place) => Len(place.fold_with(folder)),
            Cast(kind, ref op, ty) => Cast(kind, op.fold_with(folder), ty.fold_with(folder)),
            BinaryOp(op, ref rhs, ref lhs) => {
                BinaryOp(op, rhs.fold_with(folder), lhs.fold_with(folder))
            }
            CheckedBinaryOp(op, ref rhs, ref lhs) => {
                CheckedBinaryOp(op, rhs.fold_with(folder), lhs.fold_with(folder))
            }
            UnaryOp(op, ref val) => UnaryOp(op, val.fold_with(folder)),
            Discriminant(ref place) => Discriminant(place.fold_with(folder)),
            NullaryOp(op, ty) => NullaryOp(op, ty.fold_with(folder)),
            Aggregate(ref kind, ref fields) => {
                let kind = box match **kind {
                    AggregateKind::Array(ty) => AggregateKind::Array(ty.fold_with(folder)),
                    AggregateKind::Tuple => AggregateKind::Tuple,
                    AggregateKind::Adt(def, v, substs, user_ty, n) => AggregateKind::Adt(
                        def,
                        v,
                        substs.fold_with(folder),
                        user_ty.fold_with(folder),
                        n,
                    ),
                    AggregateKind::Closure(id, substs) => {
                        AggregateKind::Closure(id, substs.fold_with(folder))
                    }
                    AggregateKind::Generator(id, substs, movablity) => {
                        AggregateKind::Generator(id, substs.fold_with(folder), movablity)
                    }
                };
                Aggregate(kind, fields.fold_with(folder))
            }
        }
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        use crate::mir::Rvalue::*;
        match *self {
            Use(ref op) => op.visit_with(visitor),
            Repeat(ref op, _) => op.visit_with(visitor),
            ThreadLocalRef(did) => did.visit_with(visitor),
            Ref(region, _, ref place) => {
                region.visit_with(visitor)?;
                place.visit_with(visitor)
            }
            AddressOf(_, ref place) => place.visit_with(visitor),
            Len(ref place) => place.visit_with(visitor),
            Cast(_, ref op, ty) => {
                op.visit_with(visitor)?;
                ty.visit_with(visitor)
            }
            BinaryOp(_, ref rhs, ref lhs) | CheckedBinaryOp(_, ref rhs, ref lhs) => {
                rhs.visit_with(visitor)?;
                lhs.visit_with(visitor)
            }
            UnaryOp(_, ref val) => val.visit_with(visitor),
            Discriminant(ref place) => place.visit_with(visitor),
            NullaryOp(_, ty) => ty.visit_with(visitor),
            Aggregate(ref kind, ref fields) => {
                match **kind {
                    AggregateKind::Array(ty) => {
                        ty.visit_with(visitor)?;
                    }
                    AggregateKind::Tuple => {}
                    AggregateKind::Adt(_, _, substs, user_ty, _) => {
                        substs.visit_with(visitor)?;
                        user_ty.visit_with(visitor)?;
                    }
                    AggregateKind::Closure(_, substs) => {
                        substs.visit_with(visitor)?;
                    }
                    AggregateKind::Generator(_, substs, _) => {
                        substs.visit_with(visitor)?;
                    }
                }
                fields.visit_with(visitor)
            }
        }
    }
}

impl<'tcx> TypeFoldable<'tcx> for Operand<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        match *self {
            Operand::Copy(ref place) => Operand::Copy(place.fold_with(folder)),
            Operand::Move(ref place) => Operand::Move(place.fold_with(folder)),
            Operand::Constant(ref c) => Operand::Constant(c.fold_with(folder)),
        }
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        match *self {
            Operand::Copy(ref place) | Operand::Move(ref place) => place.visit_with(visitor),
            Operand::Constant(ref c) => c.visit_with(visitor),
        }
    }
}

impl<'tcx> TypeFoldable<'tcx> for PlaceElem<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        use crate::mir::ProjectionElem::*;

        match *self {
            Deref => Deref,
            Field(f, ty) => Field(f, ty.fold_with(folder)),
            Index(v) => Index(v.fold_with(folder)),
            Downcast(symbol, variantidx) => Downcast(symbol, variantidx),
            ConstantIndex { offset, min_length, from_end } => {
                ConstantIndex { offset, min_length, from_end }
            }
            Subslice { from, to, from_end } => Subslice { from, to, from_end },
        }
    }

    fn super_visit_with<Vs: TypeVisitor<'tcx>>(&self, visitor: &mut Vs) -> ControlFlow<()> {
        use crate::mir::ProjectionElem::*;

        match self {
            Field(_, ty) => ty.visit_with(visitor),
            Index(v) => v.visit_with(visitor),
            _ => ControlFlow::CONTINUE,
        }
    }
}

impl<'tcx> TypeFoldable<'tcx> for Field {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, _: &mut F) -> Self {
        *self
    }
    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<()> {
        ControlFlow::CONTINUE
    }
}

impl<'tcx> TypeFoldable<'tcx> for GeneratorSavedLocal {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, _: &mut F) -> Self {
        *self
    }
    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<()> {
        ControlFlow::CONTINUE
    }
}

impl<'tcx, R: Idx, C: Idx> TypeFoldable<'tcx> for BitMatrix<R, C> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, _: &mut F) -> Self {
        self.clone()
    }
    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<()> {
        ControlFlow::CONTINUE
    }
}

impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        Constant {
            span: self.span,
            user_ty: self.user_ty.fold_with(folder),
            literal: self.literal.fold_with(folder),
        }
    }
    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        self.literal.visit_with(visitor)
    }
}
<commit_msg>Rollup merge of #78830 - lcnr:mir-folder, r=oli-obk<commit_after>//! `TypeFoldable` implementations for MIR types

use super::*;
use crate::ty;

CloneTypeFoldableAndLiftImpls! {
    BlockTailInfo,
    MirPhase,
    SourceInfo,
    FakeReadCause,
    RetagKind,
    SourceScope,
    SourceScopeLocalData,
    UserTypeAnnotationIndex,
}

impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        use crate::mir::TerminatorKind::*;

        let kind = match self.kind {
            Goto { target } => Goto { target },
            SwitchInt { ref discr, switch_ty, ref targets } => SwitchInt {
                discr: discr.fold_with(folder),
                switch_ty: switch_ty.fold_with(folder),
                targets: targets.clone(),
            },
            Drop { ref place, target, unwind } => {
                Drop { place: place.fold_with(folder), target, unwind }
            }
            DropAndReplace { ref place, ref value, target, unwind } => DropAndReplace {
                place: place.fold_with(folder),
                value: value.fold_with(folder),
                target,
                unwind,
            },
            Yield { ref value, resume, ref resume_arg, drop } => Yield {
                value: value.fold_with(folder),
                resume,
                resume_arg: resume_arg.fold_with(folder),
                drop,
            },
            Call { ref func, ref args, ref destination, cleanup, from_hir_call, fn_span } => {
                let dest =
                    destination.as_ref().map(|&(ref loc, dest)| (loc.fold_with(folder), dest));

                Call {
                    func: func.fold_with(folder),
                    args: args.fold_with(folder),
                    destination: dest,
                    cleanup,
                    from_hir_call,
                    fn_span,
                }
            }
            Assert { ref cond, expected, ref msg, target, cleanup } => {
                use AssertKind::*;
                let msg = match msg {
                    BoundsCheck { len, index } => {
                        BoundsCheck { len: len.fold_with(folder), index: index.fold_with(folder) }
                    }
                    Overflow(op, l, r) => Overflow(*op, l.fold_with(folder), r.fold_with(folder)),
                    OverflowNeg(op) => OverflowNeg(op.fold_with(folder)),
                    DivisionByZero(op) => DivisionByZero(op.fold_with(folder)),
                    RemainderByZero(op) => RemainderByZero(op.fold_with(folder)),
                    ResumedAfterReturn(_) | ResumedAfterPanic(_) => msg.clone(),
                };
                Assert { cond: cond.fold_with(folder), expected, msg, target, cleanup }
            }
            GeneratorDrop => GeneratorDrop,
            Resume => Resume,
            Abort => Abort,
            Return => Return,
            Unreachable => Unreachable,
            FalseEdge { real_target, imaginary_target } => {
                FalseEdge { real_target, imaginary_target }
            }
            FalseUnwind { real_target, unwind } => FalseUnwind { real_target, unwind },
            InlineAsm { template, ref operands, options, line_spans, destination } => InlineAsm {
                template,
                operands: operands.fold_with(folder),
                options,
                line_spans,
                destination,
            },
        };
        Terminator { source_info: self.source_info, kind }
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        use crate::mir::TerminatorKind::*;

        match self.kind {
            SwitchInt { ref discr, switch_ty, .. } => {
                discr.visit_with(visitor)?;
                switch_ty.visit_with(visitor)
            }
            Drop { ref place, .. } => place.visit_with(visitor),
            DropAndReplace { ref place, ref value, .. } => {
                place.visit_with(visitor)?;
                value.visit_with(visitor)
            }
            Yield { ref value, .. } => value.visit_with(visitor),
            Call { ref func, ref args, ref destination, .. } => {
                if let Some((ref loc, _)) = *destination {
                    loc.visit_with(visitor)?;
                };
                func.visit_with(visitor)?;
                args.visit_with(visitor)
            }
            Assert { ref cond, ref msg, .. } => {
                cond.visit_with(visitor)?;
                use AssertKind::*;
                match msg {
                    BoundsCheck { ref len, ref index } => {
                        len.visit_with(visitor)?;
                        index.visit_with(visitor)
                    }
                    Overflow(_, l, r) => {
                        l.visit_with(visitor)?;
                        r.visit_with(visitor)
                    }
                    OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => {
                        op.visit_with(visitor)
                    }
                    ResumedAfterReturn(_) | ResumedAfterPanic(_) => ControlFlow::CONTINUE,
                }
            }
            InlineAsm { ref operands, .. } => operands.visit_with(visitor),
            Goto { .. }
            | Resume
            | Abort
            | Return
            | GeneratorDrop
            | Unreachable
            | FalseEdge { .. }
            | FalseUnwind { .. } => ControlFlow::CONTINUE,
        }
    }
}

impl<'tcx> TypeFoldable<'tcx> for GeneratorKind {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, _: &mut F) -> Self {
        *self
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<()> {
        ControlFlow::CONTINUE
    }
}

impl<'tcx> TypeFoldable<'tcx> for Place<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        Place { local: self.local.fold_with(folder), projection: self.projection.fold_with(folder) }
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        self.local.visit_with(visitor)?;
        self.projection.visit_with(visitor)
    }
}

impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<PlaceElem<'tcx>> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        let v = self.iter().map(|t| t.fold_with(folder)).collect::<Vec<_>>();
        folder.tcx().intern_place_elems(&v)
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        self.iter().try_for_each(|t| t.visit_with(visitor))
    }
}

impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        use crate::mir::Rvalue::*;
        match *self {
            Use(ref op) => Use(op.fold_with(folder)),
            Repeat(ref op, len) => Repeat(op.fold_with(folder), len.fold_with(folder)),
            ThreadLocalRef(did) => ThreadLocalRef(did.fold_with(folder)),
            Ref(region, bk, ref place) => {
                Ref(region.fold_with(folder), bk, place.fold_with(folder))
            }
            AddressOf(mutability, ref place) => AddressOf(mutability, place.fold_with(folder)),
            Len(ref place) => Len(place.fold_with(folder)),
            Cast(kind, ref op, ty) => Cast(kind, op.fold_with(folder), ty.fold_with(folder)),
            BinaryOp(op, ref rhs, ref lhs) => {
                BinaryOp(op, rhs.fold_with(folder), lhs.fold_with(folder))
            }
            CheckedBinaryOp(op, ref rhs, ref lhs) => {
                CheckedBinaryOp(op, rhs.fold_with(folder), lhs.fold_with(folder))
            }
            UnaryOp(op, ref val) => UnaryOp(op, val.fold_with(folder)),
            Discriminant(ref place) => Discriminant(place.fold_with(folder)),
            NullaryOp(op, ty) => NullaryOp(op, ty.fold_with(folder)),
            Aggregate(ref kind, ref fields) => {
                let kind = box match **kind {
                    AggregateKind::Array(ty) => AggregateKind::Array(ty.fold_with(folder)),
                    AggregateKind::Tuple => AggregateKind::Tuple,
                    AggregateKind::Adt(def, v, substs, user_ty, n) => AggregateKind::Adt(
                        def,
                        v,
                        substs.fold_with(folder),
                        user_ty.fold_with(folder),
                        n,
                    ),
                    AggregateKind::Closure(id, substs) => {
                        AggregateKind::Closure(id, substs.fold_with(folder))
                    }
                    AggregateKind::Generator(id, substs, movablity) => {
                        AggregateKind::Generator(id, substs.fold_with(folder), movablity)
                    }
                };
                Aggregate(kind, fields.fold_with(folder))
            }
        }
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        use crate::mir::Rvalue::*;
        match *self {
            Use(ref op) => op.visit_with(visitor),
            Repeat(ref op, _) => op.visit_with(visitor),
            ThreadLocalRef(did) => did.visit_with(visitor),
            Ref(region, _, ref place) => {
                region.visit_with(visitor)?;
                place.visit_with(visitor)
            }
            AddressOf(_, ref place) => place.visit_with(visitor),
            Len(ref place) => place.visit_with(visitor),
            Cast(_, ref op, ty) => {
                op.visit_with(visitor)?;
                ty.visit_with(visitor)
            }
            BinaryOp(_, ref rhs, ref lhs) | CheckedBinaryOp(_, ref rhs, ref lhs) => {
                rhs.visit_with(visitor)?;
                lhs.visit_with(visitor)
            }
            UnaryOp(_, ref val) => val.visit_with(visitor),
            Discriminant(ref place) => place.visit_with(visitor),
            NullaryOp(_, ty) => ty.visit_with(visitor),
            Aggregate(ref kind, ref fields) => {
                match **kind {
                    AggregateKind::Array(ty) => {
                        ty.visit_with(visitor)?;
                    }
                    AggregateKind::Tuple => {}
                    AggregateKind::Adt(_, _, substs, user_ty, _) => {
                        substs.visit_with(visitor)?;
                        user_ty.visit_with(visitor)?;
                    }
                    AggregateKind::Closure(_, substs) => {
                        substs.visit_with(visitor)?;
                    }
                    AggregateKind::Generator(_, substs, _) => {
                        substs.visit_with(visitor)?;
                    }
                }
                fields.visit_with(visitor)
            }
        }
    }
}

impl<'tcx> TypeFoldable<'tcx> for Operand<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        match *self {
            Operand::Copy(ref place) => Operand::Copy(place.fold_with(folder)),
            Operand::Move(ref place) => Operand::Move(place.fold_with(folder)),
            Operand::Constant(ref c) => Operand::Constant(c.fold_with(folder)),
        }
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        match *self {
            Operand::Copy(ref place) | Operand::Move(ref place) => place.visit_with(visitor),
            Operand::Constant(ref c) => c.visit_with(visitor),
        }
    }
}

impl<'tcx> TypeFoldable<'tcx> for PlaceElem<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        use crate::mir::ProjectionElem::*;

        match *self {
            Deref => Deref,
            Field(f, ty) => Field(f, ty.fold_with(folder)),
            Index(v) => Index(v.fold_with(folder)),
            Downcast(symbol, variantidx) => Downcast(symbol, variantidx),
            ConstantIndex { offset, min_length, from_end } => {
                ConstantIndex { offset, min_length, from_end }
            }
            Subslice { from, to, from_end } => Subslice { from, to, from_end },
        }
    }

    fn super_visit_with<Vs: TypeVisitor<'tcx>>(&self, visitor: &mut Vs) -> ControlFlow<()> {
        use crate::mir::ProjectionElem::*;

        match self {
            Field(_, ty) => ty.visit_with(visitor),
            Index(v) => v.visit_with(visitor),
            _ => ControlFlow::CONTINUE,
        }
    }
}

impl<'tcx> TypeFoldable<'tcx> for Field {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, _: &mut F) -> Self {
        *self
    }
    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<()> {
        ControlFlow::CONTINUE
    }
}

impl<'tcx> TypeFoldable<'tcx> for GeneratorSavedLocal {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, _: &mut F) -> Self {
        *self
    }
    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<()> {
        ControlFlow::CONTINUE
    }
}

impl<'tcx, R: Idx, C: Idx> TypeFoldable<'tcx> for BitMatrix<R, C> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, _: &mut F) -> Self {
        self.clone()
    }
    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<()> {
        ControlFlow::CONTINUE
    }
}

impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        Constant {
            span: self.span,
            user_ty: self.user_ty.fold_with(folder),
            literal: self.literal.fold_with(folder),
        }
    }
    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        self.literal.visit_with(visitor)
    }
}
<|endoftext|>


<commit_before>
use std::fs::File;
use std::io::{self, BufRead};
use regex::Regex;

#[macro_use]
extern crate lazy_static;

struct State<T>(Box<dyn Iterator<Item = T>>, Option<T>);
pub struct Merge {
    count: usize,
    states: Vec<State<u32>>
}

impl Merge {
    pub fn new(filename: &str) -> Self {
        let count = get_number(filename).unwrap();
        let mut states: Vec<State<u32>> = Vec::with_capacity(count);
        for i in 0..count {
            let collection = Box::new(get_collection(filename, i).unwrap());
            let state = State(collection, None);
            states.push(state);
        }
        Merge{count: count, states: states}
    }
}

impl Iterator for Merge  {
    type Item = u32;
    fn next(&mut self) -> Option<Self::Item> {
        let count = self.count;
        let states = &mut self.states;
        loop {
            for i in 0..count {
                let mut state = &mut states[i];
                if state.1.is_none() {
                    state.1 = state.0.next();
                }
            }
            let not_done = states.iter().any(|s| s.1.is_some());
            if !not_done {
                return None;
            }

            let mut min = u32::MAX;
            let mut min_index = usize::MIN;
            for i in 0..count {
                let state = &mut states[i];
                if let Some(val) = state.1 {
                    if val < min {
                        min = val;
                        min_index = i;
                    }
                }
            }
            states[min_index].1 = None;
            return Some(min);
        }
    }
}

pub fn get_number(filename: &str) -> io::Result<usize> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"NUMBER=(\d+)").unwrap();
    }
    let mut matches = match_lines(filename, &*RE)?;
    return Ok(matches.next().unwrap().parse().unwrap());
}

pub fn get_output(filename: &str) -> io::Result<impl Iterator<Item = u32>> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"OUTPUT:(.*)").unwrap();
    }
    get_numbers(filename, &*RE)
}

pub fn get_collection(filename: &str, idx: usize) -> io::Result<impl Iterator<Item = u32>> {
    let regex: Regex = {
        let r = format!("COLLECTION{}:(.*)", idx);
        Regex::new(&r).unwrap()
    };
    get_numbers(filename, &regex)
}

fn get_numbers(filename: &str, regex: &Regex) -> io::Result<impl Iterator<Item = u32>> {
    let matches = match_lines(filename, regex)?;
    let to_int = |v: &str| -> u32 {
        if let Ok(i) = v.parse::<u32>() {
            i
        } else {
            0
        }
    };
    let values = matches.map(move |s| {
        let numbers = s.split(',').map(|v| to_int(&v));
        numbers.collect::<Vec<_>>()
    }).flatten();
    Ok(values)
}

fn match_lines(filename: &str, regex: &Regex) -> io::Result<impl Iterator<Item = String>> {
    let read_lines = move || -> io::Result<_> {
        let file = File::open(filename)?;
        Ok(io::BufReader::new(file).lines().map(|line|
            line.unwrap()))
    };
    let lines = read_lines()?;
    let r = regex.clone(); // TODO
    let matches = lines.filter_map(move |line| {
        r.captures(&line).map(|captures|
            captures[1].to_string()
        )
    });
    Ok(matches)
}

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

    #[test]
    fn test_number() {
        assert_eq!(get_number("../data/test.txt").unwrap() , 3);
    }

    #[test]
    fn test_output() {
        let output = [1,1,2,2,3,4,4,6,7,9,9,20,21];
        assert_eq!(get_output("../data/test.txt").unwrap().collect::<Vec<_>>(), output);
    }

    #[test]
    fn test_collection() {
        let output = [2, 4, 6, 9, 20];
        assert_eq!(get_collection("../data/test.txt", 1).unwrap().collect::<Vec<_>>(), output);
    }

    fn merge(filename: &str) -> (Vec<u32>, Vec<u32>) {
        let result = Merge::new(filename).collect::<Vec<u32>>();
        let output = get_output(filename).unwrap().collect::<Vec<u32>>();
        (result, output)
    }

    #[test]
    fn test_merge() {
        let filename = "../data/test.txt";
        let (result, output) = merge(filename);
        assert_eq!(result, output);
    }

    #[test]
    #[should_panic]
    fn test_bad_merge() {
        let filename = "../data/bad.txt";
        let (_result, _output) = merge(filename);
    }

    #[test]
    fn test_zero_merge() {
        let filename = "../data/zero.txt";
        let (result, output) = merge(filename);
        assert_eq!(output.len(), 0);
        assert_eq!(result.len(), 0);
        assert_eq!(result, output);
    }
}
<commit_msg>fix parser to skip empty strings<commit_after>
use std::fs::File;
use std::io::{self, BufRead};
use regex::Regex;

#[macro_use]
extern crate lazy_static;

struct State<T>(Box<dyn Iterator<Item = T>>, Option<T>);
pub struct Merge {
    count: usize,
    states: Vec<State<u32>>
}

impl Merge {
    pub fn new(filename: &str) -> Self {
        let count = get_number(filename).unwrap();
        let mut states: Vec<State<u32>> = Vec::with_capacity(count);
        for i in 0..count {
            let collection = Box::new(get_collection(filename, i).unwrap());
            let state = State(collection, None);
            states.push(state);
        }
        Merge{count: count, states: states}
    }
}

impl Iterator for Merge  {
    type Item = u32;
    fn next(&mut self) -> Option<Self::Item> {
        let count = self.count;
        let states = &mut self.states;
        loop {
            for i in 0..count {
                let mut state = &mut states[i];
                if state.1.is_none() {
                    state.1 = state.0.next();
                }
            }
            let not_done = states.iter().any(|s| s.1.is_some());
            if !not_done {
                return None;
            }

            let mut min = u32::MAX;
            let mut min_index = usize::MIN;
            for i in 0..count {
                let state = &mut states[i];
                if let Some(val) = state.1 {
                    if val < min {
                        min = val;
                        min_index = i;
                    }
                }
            }
            states[min_index].1 = None;
            return Some(min);
        }
    }
}

pub fn get_number(filename: &str) -> io::Result<usize> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"NUMBER=(\d+)").unwrap();
    }
    let mut matches = match_lines(filename, &*RE)?;
    return Ok(matches.next().unwrap().parse().unwrap());
}

pub fn get_output(filename: &str) -> io::Result<impl Iterator<Item = u32>> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"OUTPUT:(.*)").unwrap();
    }
    get_numbers(filename, &*RE)
}

pub fn get_collection(filename: &str, idx: usize) -> io::Result<impl Iterator<Item = u32>> {
    let regex: Regex = {
        let r = format!("COLLECTION{}:(.*)", idx);
        Regex::new(&r).unwrap()
    };
    get_numbers(filename, &regex)
}

fn get_numbers(filename: &str, regex: &Regex) -> io::Result<impl Iterator<Item = u32>> {
    let matches = match_lines(filename, regex)?;
    let to_int = |v: &str| -> Option<u32> {
        if let Ok(i) = v.parse::<u32>() {
            Some(i)
        } else {
            None
        }
    };
    let values = matches.map(move |s| {
        let numbers = s.split(',').filter_map(|v| to_int(&v));
        numbers.collect::<Vec<_>>()
    }).flatten();
    Ok(values)
}

fn match_lines(filename: &str, regex: &Regex) -> io::Result<impl Iterator<Item = String>> {
    let read_lines = move || -> io::Result<_> {
        let file = File::open(filename)?;
        Ok(io::BufReader::new(file).lines().map(|line|
            line.unwrap()))
    };
    let lines = read_lines()?;
    let r = regex.clone(); // TODO
    let matches = lines.filter_map(move |line| {
        r.captures(&line).map(|captures|
            captures[1].to_string()
        )
    });
    Ok(matches)
}

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

    #[test]
    fn test_number() {
        assert_eq!(get_number("../data/test.txt").unwrap() , 3);
    }

    #[test]
    fn test_output() {
        let output = [1,1,2,2,3,4,4,6,7,9,9,20,21];
        assert_eq!(get_output("../data/test.txt").unwrap().collect::<Vec<_>>(), output);
    }

    #[test]
    fn test_collection() {
        let output = [2, 4, 6, 9, 20];
        assert_eq!(get_collection("../data/test.txt", 1).unwrap().collect::<Vec<_>>(), output);
    }

    fn merge(filename: &str) -> (Vec<u32>, Vec<u32>) {
        let result = Merge::new(filename).collect::<Vec<u32>>();
        let output = get_output(filename).unwrap().collect::<Vec<u32>>();
        (result, output)
    }

    #[test]
    fn test_merge() {
        let filename = "../data/test.txt";
        let (result, output) = merge(filename);
        assert_eq!(result, output);
    }

    #[test]
    #[should_panic]
    fn test_bad_merge() {
        let filename = "../data/bad.txt";
        let (_result, _output) = merge(filename);
    }

    #[test]
    fn test_zero_merge() {
        let filename = "../data/zero.txt";
        let (result, output) = merge(filename);
        assert_eq!(output.len(), 0);
        assert_eq!(result.len(), 0);
        assert_eq!(result, output);
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::cmp::Ordering;
use std::io;

pub type Asn1LenNum = u64;

#[derive(PartialEq, Debug)]
/// An enum representing the length of an ASN.1 element.
pub enum Asn1Len {
  /// A Definite length element.
  Def(u64),
  /// An Indefinite length element, not known before decoding.
  Indef,
}

impl From<Option<Asn1LenNum>> for Asn1Len {
  fn from(len: Option<Asn1LenNum>) -> Self {
    match len {
      None => Asn1Len::Indef,
      Some(l) => Asn1Len::Def(l),
    }
  }
}

impl From<Asn1Len> for Option<Asn1LenNum> {
  fn from(len: Asn1Len) -> Self {
    match len {
      Asn1Len::Def(l) => Some(l),
      Asn1Len::Indef => None,
    }
  }
}

impl PartialOrd<Asn1Len> for Asn1Len {
  fn partial_cmp(&self, other: &Asn1Len) -> Option<Ordering> {
    match (self, other) {
      (&Asn1Len::Def(ref l),
        &Asn1Len::Def(ref r)) => Some(l.cmp(r)),
      _ => None,
    }
  }
}

impl PartialEq<Asn1LenNum> for Asn1Len {
  fn eq(&self, other: &Asn1LenNum) -> bool {
    match *self {
      Asn1Len::Def(ref l) => l.eq(other),
      Asn1Len::Indef => false,
    }
  }
}

impl PartialOrd<Asn1LenNum> for Asn1Len {
  fn partial_cmp(&self, other: &Asn1LenNum) -> Option<Ordering> {
    match *self {
      Asn1Len::Def(ref l) => Some(l.cmp(other)),
      Asn1Len::Indef => None,
    }
  }
}

impl fmt::Display for Asn1Len {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match *self {
      Asn1Len::Def(ref l) => write!(f, "{}", l),
      Asn1Len::Indef => write!(f, "Indefinite Length"),
    }
  }
}

/// An ASN.1 tag number.
pub type Asn1TagNum = u64;

#[derive(PartialEq, Debug)]
/// An ASN.1 Class.
pub enum Asn1Class {
  /// Universal class.
  Universal,
  /// Application class.
  Application,
  /// Context-specific class.
  ContextSpecific,
  /// Private class.
  Private,
}

impl From<u8> for Asn1Class {
  fn from(len: u8) -> Self {
    match len {
      0 => Asn1Class::Universal,
      1 => Asn1Class::Application,
      2 => Asn1Class::ContextSpecific,
      3 => Asn1Class::Private,
      _ => unreachable!()
    }
  }
}

impl fmt::Display for Asn1Class {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", match *self {
      Asn1Class::Universal => "Universal",
      Asn1Class::Application => "Application",
      Asn1Class::ContextSpecific => "Context-specific",
      Asn1Class::Private => "Private",
    })
  }
}

#[derive(Debug)]
/// A struct representing an ASN.1 element.
pub struct Asn1Tag {
  /// The class of the ASN.1 element.
  pub class: Asn1Class,
  /// The tag number of the ASN.1 element.
  pub tagnum: Asn1TagNum,
  /// The length of the ASN.1 element.
  pub len: Asn1Len,
  /// A flag indicating whether an element is constructed.
  pub constructed: bool,
}

impl Asn1Tag {
  /// Returns true when this is a structured type.
  pub fn is_structured(&self) -> bool {
    if self.class == Asn1Class::Universal {
      match self.tagnum {
        // SEQUENCE (OF)
        16 => true,
        // SET (OF)
        17 => true,
        _ => false,
      }
    } else {
      false
    }
  }

  /// Decode an ASN.1 tag from a stream.
  pub fn decode_tag<R: io::Read>(reader: &mut R) -> Result<(Self, Asn1LenNum), Asn1DecodeError> {
    let mut bytes = ByteReader::new(reader);

    // Decode tag byte, which includes class, constructed flag, and tag number.
    let tag_byte = try!(bytes.read());
    let class_num = (tag_byte & 0xc0) >> 6;
    let constructed = tag_byte & 0x20 == 0x20;
    // If tag is 0x1F, use extended decode format.
    let tag = if (tag_byte & 0x1f) == 0x1f {
      let mut tag: Asn1TagNum = 0;
      loop {
        // Incrementatlly read bytes, adding base-128 to tag.
        let tag_more = try!(bytes.read());
        tag = (tag << 7) + (tag_more & 0x7f) as Asn1TagNum;
        // Stop looping when 0x80 bit is set.
        if tag_more & 0x80 == 0x00 {
          break;
        }
      }
      tag
    // Otherwise it's just bits 5-1.
    } else {
      (tag_byte & 0x1f) as Asn1TagNum
    };

    // Decode len byte.
    let len_byte = try!(bytes.read());
    let len = match len_byte {
      // When byte is 0x80, this is the start of indefinite length encoding.
      0x80 => Asn1Len::Indef,
      // If 0x80 is set, then other bits indicate the number of len bytes.
      l => if (l & 0x80) == 0x80 {
          let mut len: Asn1LenNum = 0;
          let byte_count = l & 0x7f;
          // Loop through number of len bytes.
          for _ in 0..byte_count {
            let len_more = try!(bytes.read());
            // Add up each byte base-256.
            len = (len << 8) + len_more as Asn1TagNum;
          }
          Asn1Len::Def(len)
        // If 0x80 bit is not set, just decode the value.
        } else {
          Asn1Len::Def(l as Asn1LenNum)
        },
    };

    Ok((Asn1Tag {
      class: Asn1Class::from(class_num),
      tagnum: tag,
      len: len,
      constructed: constructed,
    }, bytes.count))
  }
}

type Asn1Type = String;

trait Asn1Data {
  fn get_asn1_type() -> Asn1Type;

  // /// Create ASN.1 data from this struct.
  // FIXME: Should this use &self?
  // fn into_asn1(&self) -> Result<Asn1Data, Asn1Error>;

  // /// Create this struct from ASN.1 data.
  // fn from_asn1(slice: Asn1Slice) -> Result<Self, Asn1Error>;
}

/// A macro to generate a generic Asn1Data trait implementation for a struct.
macro_rules! asn1_impl {
  ( $impl_type:ident, $asn1_type:expr,
    $( $name:ident, $rusttype:ident, $asn1type:expr ),*
  ) =>
(

impl Asn1Data for $impl_type {
  fn get_asn1_type() -> Asn1Type {
    $asn1_type
  }

  fn into_asn1(&self) -> Result<Asn1Data, Asn1Error> {
    Err(Asn1Error::EncodingError)
  }

  fn from_asn1(slice: Asn1Slice) -> Result<Self, Asn1Error> {
    Err(Asn1Error::InvalidAsn1)
  }
}

)
}

/// A reader to easily read a byte from a reader.
struct ByteReader<'a, R: io::Read + 'a> {
  reader: &'a mut R,
  pub count: u64,
}

impl<'a, R: io::Read + 'a> ByteReader<'a, R> {
  fn new(reader: &'a mut R) -> ByteReader<'a, R> {
    ByteReader {
      reader: reader,
      count: 0
    }
  }

  /// Read a byte from a reader.
  fn read(&mut self) -> io::Result<u8> {
    let mut buf = [0u8; 1];
    // FIXME: Should retry on the Interrupted Error, and perhaps another error.
    match try!(self.reader.read(&mut buf)) {
      0 => Err(io::Error::new(io::ErrorKind::Other, "Read zero bytes")),
      1 => {
        self.count += 1;
        Ok(buf[0])
      },
      _ => Err(io::Error::new(io::ErrorKind::Other, "Read more than one byte")),
    }
  }
}

/// A list of errors that can occur decoding or encoding Asn1 data.
enum Asn1Error {
  /// Invalid Asn1 data.
  InvalidAsn1,
  /// An error occured while encoding Asn1 data.
  EncodingError,
  /// An invalid tag was decoded
  InvalidTag(Asn1Tag),
}

#[derive(Debug)]
/// Errors that can occur reading an ASN.1 element.
pub enum Asn1DecodeError {
  /// Generic IO Error.
  IO(io::Error),
  /// Child element(s) decoded to greater length than the parent's tag.
  GreaterLen,
  /// Primitive value encoded with an indefinite length.
  PrimIndef,
}

impl From<io::Error> for Asn1DecodeError {
  fn from(err: io::Error) -> Self {
    Asn1DecodeError::IO(err)
  }
}
<commit_msg>Add a simple encoder<commit_after>use std::fmt;
use std::cmp::Ordering;
use std::io;

pub type Asn1LenNum = u64;

#[derive(PartialEq, Debug)]
/// An enum representing the length of an ASN.1 element.
pub enum Asn1Len {
  /// A Definite length element.
  Def(u64),
  /// An Indefinite length element, not known before decoding.
  Indef,
}

impl From<Option<Asn1LenNum>> for Asn1Len {
  fn from(len: Option<Asn1LenNum>) -> Self {
    match len {
      None => Asn1Len::Indef,
      Some(l) => Asn1Len::Def(l),
    }
  }
}

impl From<Asn1Len> for Option<Asn1LenNum> {
  fn from(len: Asn1Len) -> Self {
    match len {
      Asn1Len::Def(l) => Some(l),
      Asn1Len::Indef => None,
    }
  }
}

impl PartialOrd<Asn1Len> for Asn1Len {
  fn partial_cmp(&self, other: &Asn1Len) -> Option<Ordering> {
    match (self, other) {
      (&Asn1Len::Def(ref l),
        &Asn1Len::Def(ref r)) => Some(l.cmp(r)),
      _ => None,
    }
  }
}

impl PartialEq<Asn1LenNum> for Asn1Len {
  fn eq(&self, other: &Asn1LenNum) -> bool {
    match *self {
      Asn1Len::Def(ref l) => l.eq(other),
      Asn1Len::Indef => false,
    }
  }
}

impl PartialOrd<Asn1LenNum> for Asn1Len {
  fn partial_cmp(&self, other: &Asn1LenNum) -> Option<Ordering> {
    match *self {
      Asn1Len::Def(ref l) => Some(l.cmp(other)),
      Asn1Len::Indef => None,
    }
  }
}

impl fmt::Display for Asn1Len {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match *self {
      Asn1Len::Def(ref l) => write!(f, "{}", l),
      Asn1Len::Indef => write!(f, "Indefinite Length"),
    }
  }
}

/// An ASN.1 tag number.
pub type Asn1TagNum = u64;

#[derive(PartialEq, Debug)]
/// An ASN.1 Class.
pub enum Asn1Class {
  /// Universal class.
  Universal,
  /// Application class.
  Application,
  /// Context-specific class.
  ContextSpecific,
  /// Private class.
  Private,
}

impl From<u8> for Asn1Class {
  fn from(class: u8) -> Self {
    match class {
      0 => Asn1Class::Universal,
      1 => Asn1Class::Application,
      2 => Asn1Class::ContextSpecific,
      3 => Asn1Class::Private,
      _ => unreachable!()
    }
  }
}

impl From<Asn1Class> for u8 {
  fn from(class: Asn1Class) -> Self {
    match class {
      Asn1Class::Universal => 0,
      Asn1Class::Application => 1,
      Asn1Class::ContextSpecific => 2,
      Asn1Class::Private => 3,
    }
  }
}

impl fmt::Display for Asn1Class {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", match *self {
      Asn1Class::Universal => "Universal",
      Asn1Class::Application => "Application",
      Asn1Class::ContextSpecific => "Context-specific",
      Asn1Class::Private => "Private",
    })
  }
}

#[derive(Debug)]
/// A struct representing an ASN.1 element.
pub struct Asn1Tag {
  /// The class of the ASN.1 element.
  pub class: Asn1Class,
  /// The tag number of the ASN.1 element.
  pub tagnum: Asn1TagNum,
  /// The length of the ASN.1 element.
  pub len: Asn1Len,
  /// A flag indicating whether an element is constructed.
  pub constructed: bool,
}

impl Asn1Tag {
  /// Returns true when this is a structured type.
  pub fn is_structured(&self) -> bool {
    if self.class == Asn1Class::Universal {
      match self.tagnum {
        // SEQUENCE (OF)
        16 => true,
        // SET (OF)
        17 => true,
        _ => false,
      }
    } else {
      false
    }
  }

  /// Decode an ASN.1 tag from a stream.
  pub fn decode_tag<R: io::Read>(reader: &mut R) -> Result<(Self, Asn1LenNum), Asn1DecodeError> {
    let mut bytes = ByteReader::new(reader);

    // Decode tag byte, which includes class, constructed flag, and tag number.
    let tag_byte = try!(bytes.read());
    let class_num = (tag_byte & 0xc0) >> 6;
    let constructed = tag_byte & 0x20 == 0x20;
    // If tag is 0x1F, use extended decode format.
    let tag = if (tag_byte & 0x1f) == 0x1f {
      let mut tag: Asn1TagNum = 0;
      loop {
        // Incrementatlly read bytes, adding base-128 to tag.
        let tag_more = try!(bytes.read());
        tag = (tag << 7) + (tag_more & 0x7f) as Asn1TagNum;
        // Stop looping when 0x80 bit is set.
        if tag_more & 0x80 == 0x00 {
          break;
        }
      }
      tag
    // Otherwise it's just bits 5-1.
    } else {
      (tag_byte & 0x1f) as Asn1TagNum
    };

    // Decode len byte.
    let len_byte = try!(bytes.read());
    let len = match len_byte {
      // When byte is 0x80, this is the start of indefinite length encoding.
      0x80 => Asn1Len::Indef,
      // If 0x80 is set, then other bits indicate the number of len bytes.
      l => if (l & 0x80) == 0x80 {
          let mut len: Asn1LenNum = 0;
          let byte_count = l & 0x7f;
          // Loop through number of len bytes.
          for _ in 0..byte_count {
            let len_more = try!(bytes.read());
            // Add up each byte base-256.
            len = (len << 8) + len_more as Asn1TagNum;
          }
          Asn1Len::Def(len)
        // If 0x80 bit is not set, just decode the value.
        } else {
          Asn1Len::Def(l as Asn1LenNum)
        },
    };

    Ok((Asn1Tag {
      class: Asn1Class::from(class_num),
      tagnum: tag,
      len: len,
      constructed: constructed,
    }, bytes.count))
  }

  /// Encode an ASN.1 stream from a tag.
  pub fn encode_tag<W: io::Write>(self, writer: &mut W) -> Result<Asn1LenNum, Asn1EncodeError> {
    let mut bytes = ByteWriter::new(writer);
    let (class, tagnum, len, constructed) =
      (self.class, self.tagnum, self.len, self.constructed);

    // Create first tag_byte from class, constructed and tag number.
    let mut tag_byte = u8::from(class) << 6;
    if constructed {
      tag_byte += 0x20;
    }
    // If tag number is <31, add to single byte.
    if tagnum < 31 {
      tag_byte += tagnum as u8 & 0x1f;
      try!(bytes.write(tag_byte));
    // Otherwise build additional tag bytes.
    } else {
      tag_byte += 0x1f;
      try!(bytes.write(tag_byte));
    }

    Ok(bytes.count)
  }
}

type Asn1Type = String;

trait Asn1Data {
  fn get_asn1_type() -> Asn1Type;

  // /// Create ASN.1 data from this struct.
  // FIXME: Should this use &self?
  // fn into_asn1(&self) -> Result<Asn1Data, Asn1Error>;

  // /// Create this struct from ASN.1 data.
  // fn from_asn1(slice: Asn1Slice) -> Result<Self, Asn1Error>;
}

/// A macro to generate a generic Asn1Data trait implementation for a struct.
macro_rules! asn1_impl {
  ( $impl_type:ident, $asn1_type:expr,
    $( $name:ident, $rusttype:ident, $asn1type:expr ),*
  ) =>
(

impl Asn1Data for $impl_type {
  fn get_asn1_type() -> Asn1Type {
    $asn1_type
  }

  fn into_asn1(&self) -> Result<Asn1Data, Asn1Error> {
    Err(Asn1Error::EncodingError)
  }

  fn from_asn1(slice: Asn1Slice) -> Result<Self, Asn1Error> {
    Err(Asn1Error::InvalidAsn1)
  }
}

)
}

/// A reader to easily read a byte from a reader.
struct ByteReader<'a, R: io::Read + 'a> {
  reader: &'a mut R,
  pub count: u64,
}

impl<'a, R: io::Read + 'a> ByteReader<'a, R> {
  fn new(reader: &'a mut R) -> ByteReader<'a, R> {
    ByteReader {
      reader: reader,
      count: 0
    }
  }

  /// Read a byte from a reader.
  fn read(&mut self) -> io::Result<u8> {
    let mut buf = [0u8; 1];
    // FIXME: Should retry on the Interrupted Error, and perhaps another error.
    match try!(self.reader.read(&mut buf)) {
      0 => Err(io::Error::new(io::ErrorKind::Other, "Read zero bytes")),
      1 => {
        self.count += 1;
        Ok(buf[0])
      },
      _ => Err(io::Error::new(io::ErrorKind::Other, "Read more than one byte")),
    }
  }
}

/// A writer to easily write a byte to a writer.
struct ByteWriter<'a, W: io::Write + 'a> {
  writer: &'a mut W,
  pub count: u64,
}

impl<'a, W: io::Write + 'a> ByteWriter<'a, W> {
  fn new(writer: &'a mut W) -> ByteWriter<'a, W> {
    ByteWriter {
      writer: writer,
      count: 0
    }
  }

  /// Write a byte to a writer.
  fn write(&mut self, byte: u8) -> io::Result<()> {
    let buf = [byte];
    match try!(self.writer.write(&buf)) {
      0 => Err(io::Error::new(io::ErrorKind::Other, "Wrote zero bytes")),
      1 => {
        self.count += 1;
        Ok(())
      },
      _ => Err(io::Error::new(io::ErrorKind::Other, "Wrote more than one byte")),
    }
  }
}

/// A list of errors that can occur decoding or encoding Asn1 data.
enum Asn1Error {
  /// Invalid Asn1 data.
  InvalidAsn1,
  /// An error occured while encoding Asn1 data.
  EncodingError,
  /// An invalid tag was decoded
  InvalidTag(Asn1Tag),
}

#[derive(Debug)]
/// Errors that can occur while decoding an ASN.1 element.
pub enum Asn1DecodeError {
  /// Generic IO Error.
  IO(io::Error),
  /// Child element(s) decoded to greater length than the parent's tag.
  GreaterLen,
  /// Primitive value encoded with an indefinite length.
  PrimIndef,
}

impl From<io::Error> for Asn1DecodeError {
  fn from(err: io::Error) -> Self {
    Asn1DecodeError::IO(err)
  }
}

#[derive(Debug)]
/// Errors that can occur while encoding an ASN.1 element.
pub enum Asn1EncodeError {
  /// Generic IO Error.
  IO(io::Error),
}

impl From<io::Error> for Asn1EncodeError {
  fn from(err: io::Error) -> Self {
    Asn1EncodeError::IO(err)
  }
}
<|endoftext|>


<commit_before>#![deny(warnings)]
extern crate coreutils;
extern crate extra;

use std::io::{self, Write};
use std::env;
use std::process;
use coreutils::ArgParser;
use extra::option::OptionalExt;

const MAN_PAGE: &'static str = /* @MANSTART{basename} */ r#"
NAME
    basename - strip directory and suffix from filenames

SYNOPSIS
    basename [-z --zero] [-s | --suffix] SUFFIX [-a | --multiple] NAME...

DESCRIPTION
    Print NAME with any leading directory components removed. If a SUFFIX is provided, the suffix
    will be removed from NAME.

OPTIONS
    -a
    --multiple
        Support multiple arguments and treat each as a NAME

    -s
    --suffix=SUFFIX
        remove a trailing SUFFIX; implies -a

    -z
    --zero
        end each output line with NUL, not newline

    -h
    --help
        display this help and exit

EXAMPLE
    basename dir/filename.ext
        > filename.ext
    basename dir/filename.ext .ext
        > filename
    basename -a -s .ext one.ext two.ext three.ext
        > one two three

AUTHOR
    Written by Michael Murphy.
"#; /* @MANEND */

const HELP_INFO:       &'static str = "Try ‘basename --help’ for more information.\n";
const MISSING_OPERAND: &'static str = "missing operand\n";
const REQUIRES_OPTION: &'static str = "option requires an argument -- ‘s’\n";

fn main() {
    let stdout          = io::stdout();
    let mut stdout      = stdout.lock();
    let mut stderr      = io::stderr();
    let mut parser = ArgParser::new(1)
        .add_opt("s", "suffix")
        .add_flag("a", "multiple")
        .add_flag("z", "zero")
        .add_flag("h", "help");
    parser.initialize(env::args());

    if parser.flagged(&'h') || parser.flagged("help") {
        stdout.write_all(MAN_PAGE.as_bytes()).try(&mut stderr);
        stdout.flush().try(&mut stderr);
        process::exit(0);
    }
    if parser.flagged(&'s') || parser.flagged("suffix") {
        if parser.get_opt(&'s').is_none() && parser.get_opt("suffix").is_none() {
            stderr.write_all(REQUIRES_OPTION.as_bytes()).try(&mut stderr);
            stdout.write_all(HELP_INFO.as_bytes()).try(&mut stderr);
            stderr.flush().try(&mut stderr);
            process::exit(1);
        }
        parser.set_flag(&'a', true);
        parser.set_flag("multiple", true);
    }
    if parser.args.is_empty() {
        stdout.write_all(MISSING_OPERAND.as_bytes()).try(&mut stderr);
        stdout.write_all(HELP_INFO.as_bytes()).try(&mut stderr);
        process::exit(1);
    }
    // TODO: Implement mismatched() for ArgParser
    //if parser.mismatched() {
    //    stderr.write_all("invalid option -- ‘".as_bytes()).try(&mut stderr);
    //    stderr.write_all(argument.as_bytes()).try(&mut stderr);
    //    stderr.write_all("’\n".as_bytes()).try(&mut stderr);
    //    stdout.write_all(HELP_INFO.as_bytes()).try(&mut stderr);
    //    stderr.flush().try(&mut stderr);
    //    process::exit(1);
    //}

    if parser.flagged(&'a') || parser.flagged("multiple") {
        for path in &parser.args {
            basename(&path, &parser, &mut stdout, &mut stderr);
        }
    } else {
        // If there is an additional variable, set this variable as the suffix to remove
        if let Some(potential_suffix) = parser.args.get(1).map(|s| (*s).clone()) {
            parser.set_opt(&'s', Some(potential_suffix.clone()));
            parser.set_opt("suffix", Some(potential_suffix));
            // If there is an extra variable after that, print an error about an extra operand
            if let Some(extra_operand) = parser.args.get(2) {
                stderr.write_all("extra operand ‘".as_bytes()).try(&mut stderr);
                stderr.write_all(extra_operand.as_bytes()).try(&mut stderr);
                stderr.write_all("’\n".as_bytes()).try(&mut stderr);
                stdout.write_all(HELP_INFO.as_bytes()).try(&mut stderr);
                stderr.flush().try(&mut stderr);
                process::exit(1);
            }
        }
        basename(&parser.args[0], &parser, &mut stdout, &mut stderr);
    }
}

/// Takes a file path as input and returns the basename of that path. If `zero` is set to true,
/// the name will be printed without a newline. If a suffix is set and the path contains that
/// suffix, the suffix will be removed.
fn basename(path: &str, parser: &ArgParser, stdout: &mut io::StdoutLock, stderr: &mut io::Stderr) {
    match get_basename(path, &parser.get_opt("suffix").fail("No arguments. Use --help to see the usage.", stderr)) {
        Ok(filename) => stdout.write_all(filename.as_bytes()).try(stderr),
        Err(why) => {
            // An error occurred, so this will select the correct message to print
            let err_message = match why {
                BaseError::Path    => "invalid path ‘".as_bytes(),
                BaseError::Unicode => "invalid unicode in path ‘".as_bytes()
            };
            stderr.write_all(err_message).try(stderr);
            stderr.write_all(path.as_bytes()).try(stderr);
            stderr.write_all("’\n".as_bytes()).try(stderr);
            stderr.flush().try(stderr);
            process::exit(1);
        }
    }

    if parser.flagged("z") || parser.flagged("zero") {
        stdout.write_all(b"\n").try(stderr);
    }
}

#[derive(Debug, PartialEq)]
enum BaseError { Path, Unicode }

/// Either return the basename as a byte slice or return a `BaseError`
fn get_basename<'a>(input: &'a str, suffix: &str) -> Result<&'a str, BaseError> {
    // Remove the suffix from the path and attempt to collect the file name in that path
    std::path::Path::new({
            let (prefix, input_suffix) = input.split_at(input.len() - suffix.len());
            if input_suffix == suffix { prefix } else { input }
    })
        .file_name()
        // If there was an error in obtaining the filename from the path, return an error
        .map_or(Err(BaseError::Path), |base| {
            // Convert the filename back into a string, else return a unicode error.
            base.to_str().map_or(Err(BaseError::Unicode), |filename| Ok(filename))
        })
}

#[test]
fn test_basename() {
    assert_eq!(Ok("a"), get_basename("a.b", ".b"));
}
<commit_msg>Update invalid parameter use in basename.rs<commit_after>#![deny(warnings)]
extern crate coreutils;
extern crate extra;

use std::io::{self, Write};
use std::env;
use std::process;
use coreutils::ArgParser;
use extra::option::OptionalExt;

const MAN_PAGE: &'static str = /* @MANSTART{basename} */ r#"
NAME
    basename - strip directory and suffix from filenames

SYNOPSIS
    basename [-z --zero] [-s | --suffix] SUFFIX [-a | --multiple] NAME...

DESCRIPTION
    Print NAME with any leading directory components removed. If a SUFFIX is provided, the suffix
    will be removed from NAME.

OPTIONS
    -a
    --multiple
        Support multiple arguments and treat each as a NAME

    -s
    --suffix=SUFFIX
        remove a trailing SUFFIX; implies -a

    -z
    --zero
        end each output line with NUL, not newline

    -h
    --help
        display this help and exit

EXAMPLE
    basename dir/filename.ext
        > filename.ext
    basename dir/filename.ext .ext
        > filename
    basename -a -s .ext one.ext two.ext three.ext
        > one two three

AUTHOR
    Written by Michael Murphy.
"#; /* @MANEND */

const HELP_INFO:       &'static str = "Try ‘basename --help’ for more information.\n";
const MISSING_OPERAND: &'static str = "missing operand\n";
const REQUIRES_OPTION: &'static str = "option requires an argument -- ‘s’\n";

fn main() {
    let stdout          = io::stdout();
    let mut stdout      = stdout.lock();
    let mut stderr      = io::stderr();
    let mut parser = ArgParser::new(1)
        .add_opt("s", "suffix")
        .add_flag("a", "multiple")
        .add_flag("z", "zero")
        .add_flag("h", "help");
    parser.initialize(env::args());

    if parser.flagged(&'h') || parser.flagged("help") {
        stdout.write_all(MAN_PAGE.as_bytes()).try(&mut stderr);
        stdout.flush().try(&mut stderr);
        process::exit(0);
    }
    if parser.flagged(&'s') || parser.flagged("suffix") {
        if parser.get_opt(&'s').is_none() && parser.get_opt("suffix").is_none() {
            stderr.write_all(REQUIRES_OPTION.as_bytes()).try(&mut stderr);
            stdout.write_all(HELP_INFO.as_bytes()).try(&mut stderr);
            stderr.flush().try(&mut stderr);
            process::exit(1);
        }
        parser.set_flag(&'a', true);
        parser.set_flag("multiple", true);
    }
    if parser.args.is_empty() {
        stdout.write_all(MISSING_OPERAND.as_bytes()).try(&mut stderr);
        stdout.write_all(HELP_INFO.as_bytes()).try(&mut stderr);
        process::exit(1);
    }
    if let Err(err) = parser.flagged_invalid() {
        stderr.write_all(err.as_bytes()).try(&mut stderr);
        stdout.write_all(HELP_INFO.as_bytes()).try(&mut stderr);
        stderr.flush().try(&mut stderr);
        process::exit(1);
    }

    if parser.flagged(&'a') || parser.flagged("multiple") {
        for path in &parser.args {
            basename(&path, &parser, &mut stdout, &mut stderr);
        }
    } else {
        // If there is an additional variable, set this variable as the suffix to remove
        if let Some(potential_suffix) = parser.args.get(1).map(|s| (*s).clone()) {
            parser.set_opt(&'s', Some(potential_suffix.clone()));
            parser.set_opt("suffix", Some(potential_suffix));
            // If there is an extra variable after that, print an error about an extra operand
            if let Some(extra_operand) = parser.args.get(2) {
                stderr.write_all("extra operand ‘".as_bytes()).try(&mut stderr);
                stderr.write_all(extra_operand.as_bytes()).try(&mut stderr);
                stderr.write_all("’\n".as_bytes()).try(&mut stderr);
                stdout.write_all(HELP_INFO.as_bytes()).try(&mut stderr);
                stderr.flush().try(&mut stderr);
                process::exit(1);
            }
        }
        basename(&parser.args[0], &parser, &mut stdout, &mut stderr);
    }
}

/// Takes a file path as input and returns the basename of that path. If `zero` is set to true,
/// the name will be printed without a newline. If a suffix is set and the path contains that
/// suffix, the suffix will be removed.
fn basename(path: &str, parser: &ArgParser, stdout: &mut io::StdoutLock, stderr: &mut io::Stderr) {
    match get_basename(path, &parser.get_opt("suffix").fail("No arguments. Use --help to see the usage.", stderr)) {
        Ok(filename) => stdout.write_all(filename.as_bytes()).try(stderr),
        Err(why) => {
            // An error occurred, so this will select the correct message to print
            let err_message = match why {
                BaseError::Path    => "invalid path ‘".as_bytes(),
                BaseError::Unicode => "invalid unicode in path ‘".as_bytes()
            };
            stderr.write_all(err_message).try(stderr);
            stderr.write_all(path.as_bytes()).try(stderr);
            stderr.write_all("’\n".as_bytes()).try(stderr);
            stderr.flush().try(stderr);
            process::exit(1);
        }
    }

    if parser.flagged("z") || parser.flagged("zero") {
        stdout.write_all(b"\n").try(stderr);
    }
}

#[derive(Debug, PartialEq)]
enum BaseError { Path, Unicode }

/// Either return the basename as a byte slice or return a `BaseError`
fn get_basename<'a>(input: &'a str, suffix: &str) -> Result<&'a str, BaseError> {
    // Remove the suffix from the path and attempt to collect the file name in that path
    std::path::Path::new({
            let (prefix, input_suffix) = input.split_at(input.len() - suffix.len());
            if input_suffix == suffix { prefix } else { input }
    })
        .file_name()
        // If there was an error in obtaining the filename from the path, return an error
        .map_or(Err(BaseError::Path), |base| {
            // Convert the filename back into a string, else return a unicode error.
            base.to_str().map_or(Err(BaseError::Unicode), |filename| Ok(filename))
        })
}

#[test]
fn test_basename() {
    assert_eq!(Ok("a"), get_basename("a.b", ".b"));
}
<|endoftext|>


<commit_before>use clap::{Arg, App, SubCommand};

pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
    app
        .arg(Arg::with_name("id")
            .long("id")
            .short("i")
            .takes_value(true)
            .required(true)
            .help("View this entry at this store path"))

        .arg(Arg::with_name("version")
            .long("version")
            .short("V")
            .takes_value(true)
            .required(false)
            .help("View this version (youngest if not specified)"))

        .arg(Arg::with_name("versions")
            .long("versions")
            .takes_value(false)
            .required(false)
            .help("Only print available versions for this file"))

        .arg(Arg::with_name("view-header")
            .long("header")
            .short("h")
            .takes_value(false)
            .required(false)
            .help("View header"))
        .arg(Arg::with_name("view-content")
            .long("content")
            .short("C")
            .takes_value(false)
            .required(false)
            .help("View content"))

        .arg(Arg::with_name("view-copy")
            .long("copy")
            .takes_value(false)
            .required(false)
            .help("Copy before opening (copies to /tmp/) and removes the file after viewing."))

        .arg(Arg::with_name("keep-copy")
            .long("keep-copy")
            .short("k")
            .takes_value(false)
            .required(false)
            .help("If --copy was passed, keep the copy after viewing."))

        .subcommand(SubCommand::with_name("view-in")
                   .about("View the entry in ...")
                   .version("0.1")

                   .arg(Arg::with_name("view-in-stdout")
                        .long("stdout")
                        .short("s")
                        .takes_value(false)
                        .required(false)
                        .help("View by printing to stdout"))

                   .arg(Arg::with_name("view-in-ui")
                        .long("ui")
                        .short("u")
                        .takes_value(false)
                        .required(false)
                        .help("View by opening own curses-like UI (default)"))

                   .arg(Arg::with_name("view-in-browser")
                        .long("browser")
                        .short("b")
                        .takes_value(true) // optional, which browser
                        .required(false)
                        .help("View content in $BROWSER (fails if no env variable $BROWSER)"))

                   .arg(Arg::with_name("view-in-texteditor")
                        .long("editor")
                        .short("e")
                        .takes_value(true) // optional, which editor
                        .required(false)
                        .help("View content in $EDITOR"))

                   .arg(Arg::with_name("view-in-custom")
                        .long("custom")
                        .short("c")
                        .takes_value(true) // non-optional, call-string
                        .required(false)
                        .help("View content in custom program, for example 'libreoffice %e', replace '%e' with entry path"))
                   )

        .subcommand(SubCommand::with_name("compile")
                   .about("Compile content to other format for viewing, implies that the entry gets copied to /tmp")
                   .version("0.1")
                   .arg(Arg::with_name("from")
                        .long("from")
                        .short("f")
                        .takes_value(true) // "markdown" or "textile" or "restructuredtex"
                        .required(true)
                        .help("Compile from"))

                   .arg(Arg::with_name("to")
                        .long("to")
                        .short("t")
                        .takes_value(true) // "html" or "HTML" or ... json maybe?
                        .required(true)
                        .help("Compile to"))
                   )
}

<commit_msg>Group Args, so we enforce that only _one_ of them can be specified<commit_after>use clap::{Arg, App, ArgGroup, SubCommand};

pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
    app
        .arg(Arg::with_name("id")
            .long("id")
            .short("i")
            .takes_value(true)
            .required(true)
            .help("View this entry at this store path"))

        .arg(Arg::with_name("version")
            .long("version")
            .short("V")
            .takes_value(true)
            .required(false)
            .help("View this version (youngest if not specified)"))

        .arg(Arg::with_name("versions")
            .long("versions")
            .takes_value(false)
            .required(false)
            .help("Only print available versions for this file"))

        .arg(Arg::with_name("view-header")
            .long("header")
            .short("h")
            .takes_value(false)
            .required(false)
            .help("View header"))
        .arg(Arg::with_name("view-content")
            .long("content")
            .short("C")
            .takes_value(false)
            .required(false)
            .help("View content"))

        .arg(Arg::with_name("view-copy")
            .long("copy")
            .takes_value(false)
            .required(false)
            .help("Copy before opening (copies to /tmp/) and removes the file after viewing."))

        .arg(Arg::with_name("keep-copy")
            .long("keep-copy")
            .short("k")
            .takes_value(false)
            .required(false)
            .help("If --copy was passed, keep the copy after viewing."))

        .subcommand(SubCommand::with_name("view-in")
                   .about("View the entry in ...")
                   .version("0.1")

                   .arg(Arg::with_name("view-in-stdout")
                        .long("stdout")
                        .short("s")
                        .takes_value(false)
                        .required(false)
                        .help("View by printing to stdout"))

                   .arg(Arg::with_name("view-in-ui")
                        .long("ui")
                        .short("u")
                        .takes_value(false)
                        .required(false)
                        .help("View by opening own curses-like UI (default)"))

                   .arg(Arg::with_name("view-in-browser")
                        .long("browser")
                        .short("b")
                        .takes_value(true) // optional, which browser
                        .required(false)
                        .help("View content in $BROWSER (fails if no env variable $BROWSER)"))

                   .arg(Arg::with_name("view-in-texteditor")
                        .long("editor")
                        .short("e")
                        .takes_value(true) // optional, which editor
                        .required(false)
                        .help("View content in $EDITOR"))

                   .arg(Arg::with_name("view-in-custom")
                        .long("custom")
                        .short("c")
                        .takes_value(true) // non-optional, call-string
                        .required(false)
                        .help("View content in custom program, for example 'libreoffice %e', replace '%e' with entry path"))

                   .group(ArgGroup::with_name("viewer")
                          .args(&["view-in-stdout",
                                  "view-in-ui",
                                  "view-in-browser",
                                  "view-in-texteditor",
                                  "view-in-custom",
                          ])
                          .required(false))
                   )

        .subcommand(SubCommand::with_name("compile")
                   .about("Compile content to other format for viewing, implies that the entry gets copied to /tmp")
                   .version("0.1")
                   .arg(Arg::with_name("from")
                        .long("from")
                        .short("f")
                        .takes_value(true) // "markdown" or "textile" or "restructuredtex"
                        .required(true)
                        .help("Compile from"))

                   .arg(Arg::with_name("to")
                        .long("to")
                        .short("t")
                        .takes_value(true) // "html" or "HTML" or ... json maybe?
                        .required(true)
                        .help("Compile to"))
                   )
}

<|endoftext|>