<commit_before>// Copyright 2014-2015 Sam Doshi (sam@metal-fish.co.uk)
// Copyright 2013-2014 Philippe Delrieu (philippe.delrieu@free.fr)
//
// Licensed under either of
//          Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
//          MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT).
// This file may not be copied, modified, or distributed except according to those terms.
use std::ptr;
use std::os::raw::c_char;

mod ffi;
use ffi::MaybeError;
pub mod types;
pub use types::*;

// Global fns
// ----------
/// `initialize` initalizes the underlying PortMidi C library, call this
/// before using the library.
///
/// Once initialized, PortMidi will no longer pickup any new Midi devices that are
/// connected, i.e. it does not support hot plugging.
pub fn initialize() -> PortMidiResult<()> {
    PortMidiResult::from(unsafe { ffi::Pm_Initialize() })
}

/// `terminate` terminates the underlying PortMidi C library, call this
/// after using the library.
pub fn terminate() -> PortMidiResult<()> {
    PortMidiResult::from(unsafe { ffi::Pm_Terminate() })
}

/// Return the number of devices. This number will not change during the lifetime
/// of the program.
pub fn count_devices() -> PortMidiDeviceId {
    unsafe { ffi::Pm_CountDevices() }
}

/// Gets the `PortMidiDeviceId` for the default input, or `None` if
/// there isn't one
///
/// See the PortMidi documentation for details of how to set the default device
pub fn get_default_input_device_id() -> Option<PortMidiDeviceId> {
    let id = unsafe { ffi::Pm_GetDefaultInputDeviceID() };
    if id == ffi::PM_NO_DEVICE {
        None
    } else {
        Some(id)
    }
}

/// Gets the `PortMidiDeviceId` for the default output, or `None` if
/// there isn't one
///
/// See the PortMidi documentation for details of how to set the default device
pub fn get_default_output_device_id() -> Option<PortMidiDeviceId> {
    let id = unsafe { ffi::Pm_GetDefaultOutputDeviceID() };
    if id == ffi::PM_NO_DEVICE {
        None
    } else {
        Some(id)
    }
}

// DeviceInfo
// ----------
/// Represents what we know about a device
#[derive(Clone, Debug)]
pub struct DeviceInfo {
    /// The `PortMidiDeviceId` used with `OutputPort::new` and `InputPort::new`
    pub device_id: PortMidiDeviceId,
    /// The name of the device
    pub name: String,
    /// Is the device an input
    pub input: bool,
    /// Is the device an output
    pub output: bool,
}

impl DeviceInfo {
    fn wrap(device_id: PortMidiDeviceId, device_info: *const ffi::PmDeviceInfo) -> DeviceInfo {
        let name = unsafe {
            let bytes = std::ffi::CStr::from_ptr((*device_info).name).to_bytes();
            std::str::from_utf8_unchecked(bytes).to_string()
        };
        let input = unsafe { (*device_info).input };
        let output = unsafe { (*device_info).output };

        DeviceInfo {
            device_id: device_id,
            name: name,
            input: input > 0,
            output: output > 0,
        }
    }
}

/// Returns a `DeviceInfo` with information about a device, or `None` if
/// it does not exist
pub fn get_device_info(device_id: PortMidiDeviceId) -> Option<DeviceInfo> {
    let info = unsafe { ffi::Pm_GetDeviceInfo(device_id) };
    if info.is_null() {
        None
    } else {
        Some(DeviceInfo::wrap(device_id, info))
    }
}

// Midi events
// -----------
/// Represents a single midi message, see also `MidiEvent`
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct MidiMessage {
    pub status: u8,
    pub data1: u8,
    pub data2: u8,
}

impl MidiMessage {
    fn wrap(cmessage: ffi::PmMessage) -> MidiMessage {
        MidiMessage {
            status: ((cmessage) & 0xFF) as u8,
            data1: (((cmessage) >> 8) & 0xFF) as u8,
            data2: (((cmessage) >> 16) & 0xFF) as u8,
        }
    }

    fn unwrap(&self) -> ffi::PmMessage {
        ((((self.data2 as i32) << 16) & 0xFF0000) | (((self.data1 as i32) << 8) & 0xFF00) |
         ((self.status as i32) & 0xFF)) as i32
    }
}

/// Represents a time stamped midi event. See also `MidiMessage`
///
/// See the PortMidi documentation for how SysEx and midi realtime messages
/// are handled
///
/// TODO: what to do about the timestamp?
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct MidiEvent {
    pub message: MidiMessage,
    pub timestamp: ffi::PmTimestamp,
}

impl MidiEvent {
    fn wrap(event: ffi::PmEvent) -> MidiEvent {
        MidiEvent {
            message: MidiMessage::wrap(event.message),
            timestamp: event.timestamp,
        }
    }

    fn unwrap(&self) -> ffi::PmEvent {
        ffi::PmEvent {
            message: self.message.unwrap(),
            timestamp: self.timestamp,
        }
    }
}

// Input
// -----
/// Representation of an input midi port
#[allow(missing_copy_implementations)]
pub struct InputPort {
    stream: *const ffi::PortMidiStream,
    input_device: ffi::PmDeviceId,
    buffer_size: i32,
}

const EVENT_BUFFER_SIZE: usize = 128;
impl InputPort {
    /// Construct a new `InputPort` for `input_device`
    pub fn new(input_device: PortMidiDeviceId, buffer_size: i32) -> InputPort {
        InputPort {
            stream: ptr::null(),
            input_device: input_device,
            buffer_size: buffer_size,
        }
    }

    /// Open the port returning an error if there is a problem
    pub fn open(&mut self) -> PortMidiResult<()> {
        PortMidiResult::from(unsafe {
            ffi::Pm_OpenInput(&self.stream,
                              self.input_device,
                              ptr::null(),
                              self.buffer_size,
                              ptr::null(),
                              ptr::null())
        })
    }

    pub fn read_n(&mut self, cnt: usize) -> PortMidiResult<Option<Vec<MidiEvent>>> {
        let read_cnt = if cnt > EVENT_BUFFER_SIZE {
            EVENT_BUFFER_SIZE as i32
        } else {
            cnt as i32
        };
        let mut event_buffer = [ffi::PmEvent::default(); EVENT_BUFFER_SIZE];
        let res = unsafe {
            ffi::Pm_Read(self.stream,
                         event_buffer.as_mut_ptr(),
                         read_cnt)
        };
        if res < 0 {
            let err = ffi::PmError::try_from(res).unwrap();
            // TODO: Return the error
            println!("error: {:?}", err);
            return Ok(None);
        } else if res == 0 {
            Ok(None)
        } else {
            // remove mutability and replace return value
            let mut events = (0..res as usize)
                             .map(|i| MidiEvent::wrap(event_buffer[i].clone()))
                             .collect::<Vec<MidiEvent>>();
            Ok(Some(events))
        }
    }

    /// Reads a single `MidiEvent` if one is avaible
    ///
    /// A `Result` of `None` means no event was available.
    ///
    /// See the PortMidi documentation for information on how it deals with input
    /// overflows
    /// TODO: call `read_n`
    pub fn read(&mut self) -> PortMidiResult<Option<MidiEvent>> {
        match self.read_n(1) {
            Ok(Some(mut vec)) => Ok(vec.pop()),
            Ok(_) => Ok(None),
            Err(e) => Err(e),
        }
    }

    /// `poll` tests if there is input available, either returing a bool or an error
    pub fn poll(&self) -> PortMidiResult<bool> {
        let pm_error = unsafe { ffi::Pm_Poll(self.stream) };
        match pm_error {
            ffi::PmError::PmNoError => Ok(false),
            ffi::PmError::PmGotData => Ok(true),
            other => PortMidiResult::from(other).map(|_| false),
        }
    }

    /// Closes the input, flushing any pending buffers
    ///
    /// PortMidi attempts to close open streams when the application
    /// exits, but this can be difficult under Windows
    /// (according to the PortMidi documentation).
    pub fn close(&mut self) -> PortMidiResult<()> {
        PortMidiResult::from(unsafe { ffi::Pm_Close(self.stream) })
    }
}

// Output
// ------
/// Representation of an output midi port
#[allow(missing_copy_implementations)]
pub struct OutputPort {
    stream: *const ffi::PortMidiStream,
    output_device: ffi::PmDeviceId,
    buffer_size: i32,
}
impl OutputPort {
    /// Construct a new `InputPort` for `input_device`
    pub fn new(output_device: PortMidiDeviceId, buffer_size: i32) -> OutputPort {
        OutputPort {
            stream: ptr::null(),
            output_device: output_device,
            buffer_size: buffer_size,
        }
    }

    /// Open the port returning an error if there is a problem
    pub fn open(&mut self) -> PortMidiResult<()> {
        PortMidiResult::from(unsafe {
            ffi::Pm_OpenOutput(&self.stream,
                               self.output_device,
                               ptr::null(),
                               self.buffer_size,
                               ptr::null(),
                               ptr::null(),
                               0)
        })
    }

    /// Terminates outgoing messages immediately
    ///
    /// The caller should immediately close the output port, this may
    /// result in transmission of a partial midi message. Note, not all platforms
    /// support abort.
    pub fn abort(&mut self) -> PortMidiResult<()> {
        PortMidiResult::from(unsafe { ffi::Pm_Abort(self.stream) })
    }

    /// Closes the midi stream, flushing any pending buffers
    ///
    /// PortMidi attempts to close open streams when the application
    /// exits, but this can be difficult under Windows
    /// (according to the PortMidi documentation).
    pub fn close(&mut self) -> PortMidiResult<()> {
        PortMidiResult::from(unsafe { ffi::Pm_Close(self.stream) })
    }

    /// Write a single `MidiEvent`
    pub fn write_event(&mut self, midi_event: MidiEvent) -> PortMidiResult<()> {
        let event = midi_event.unwrap();
        PortMidiResult::from(unsafe { ffi::Pm_Write(self.stream, &event, 1) })
    }

    /// Write a single `MidiMessage` immediately
    pub fn write_message(&mut self, midi_message: MidiMessage) -> PortMidiResult<()> {
        let message = midi_message.unwrap();
        PortMidiResult::from(unsafe { ffi::Pm_WriteShort(self.stream, 0, message) })
    }
}

/**  Translate portmidi host error into human readable message.
     These strings are computed at run time, so client has to allocate storage.
     After this routine executes, the host error is cleared.
*/
pub fn get_host_error_text(msg: *const c_char, len: i32) {
    unsafe {
        ffi::Pm_GetHostErrorText(msg, len);
    }
}

pub const HDRLENGTH: i32 = 50;
pub const PM_HOST_ERROR_MSG_LEN: i32 = 256;
<commit_msg>Remove `get_host_error_text` because `get_host_error` is removed as well<commit_after>// Copyright 2014-2015 Sam Doshi (sam@metal-fish.co.uk)
// Copyright 2013-2014 Philippe Delrieu (philippe.delrieu@free.fr)
//
// Licensed under either of
//          Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
//          MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT).
// This file may not be copied, modified, or distributed except according to those terms.
use std::ptr;
use std::os::raw::c_char;

mod ffi;
use ffi::MaybeError;
pub mod types;
pub use types::*;

// Global fns
// ----------
/// `initialize` initalizes the underlying PortMidi C library, call this
/// before using the library.
///
/// Once initialized, PortMidi will no longer pickup any new Midi devices that are
/// connected, i.e. it does not support hot plugging.
pub fn initialize() -> PortMidiResult<()> {
    PortMidiResult::from(unsafe { ffi::Pm_Initialize() })
}

/// `terminate` terminates the underlying PortMidi C library, call this
/// after using the library.
pub fn terminate() -> PortMidiResult<()> {
    PortMidiResult::from(unsafe { ffi::Pm_Terminate() })
}

/// Return the number of devices. This number will not change during the lifetime
/// of the program.
pub fn count_devices() -> PortMidiDeviceId {
    unsafe { ffi::Pm_CountDevices() }
}

/// Gets the `PortMidiDeviceId` for the default input, or `None` if
/// there isn't one
///
/// See the PortMidi documentation for details of how to set the default device
pub fn get_default_input_device_id() -> Option<PortMidiDeviceId> {
    let id = unsafe { ffi::Pm_GetDefaultInputDeviceID() };
    if id == ffi::PM_NO_DEVICE {
        None
    } else {
        Some(id)
    }
}

/// Gets the `PortMidiDeviceId` for the default output, or `None` if
/// there isn't one
///
/// See the PortMidi documentation for details of how to set the default device
pub fn get_default_output_device_id() -> Option<PortMidiDeviceId> {
    let id = unsafe { ffi::Pm_GetDefaultOutputDeviceID() };
    if id == ffi::PM_NO_DEVICE {
        None
    } else {
        Some(id)
    }
}

// DeviceInfo
// ----------
/// Represents what we know about a device
#[derive(Clone, Debug)]
pub struct DeviceInfo {
    /// The `PortMidiDeviceId` used with `OutputPort::new` and `InputPort::new`
    pub device_id: PortMidiDeviceId,
    /// The name of the device
    pub name: String,
    /// Is the device an input
    pub input: bool,
    /// Is the device an output
    pub output: bool,
}

impl DeviceInfo {
    fn wrap(device_id: PortMidiDeviceId, device_info: *const ffi::PmDeviceInfo) -> DeviceInfo {
        let name = unsafe {
            let bytes = std::ffi::CStr::from_ptr((*device_info).name).to_bytes();
            std::str::from_utf8_unchecked(bytes).to_string()
        };
        let input = unsafe { (*device_info).input };
        let output = unsafe { (*device_info).output };

        DeviceInfo {
            device_id: device_id,
            name: name,
            input: input > 0,
            output: output > 0,
        }
    }
}

/// Returns a `DeviceInfo` with information about a device, or `None` if
/// it does not exist
pub fn get_device_info(device_id: PortMidiDeviceId) -> Option<DeviceInfo> {
    let info = unsafe { ffi::Pm_GetDeviceInfo(device_id) };
    if info.is_null() {
        None
    } else {
        Some(DeviceInfo::wrap(device_id, info))
    }
}

// Midi events
// -----------
/// Represents a single midi message, see also `MidiEvent`
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct MidiMessage {
    pub status: u8,
    pub data1: u8,
    pub data2: u8,
}

impl MidiMessage {
    fn wrap(cmessage: ffi::PmMessage) -> MidiMessage {
        MidiMessage {
            status: ((cmessage) & 0xFF) as u8,
            data1: (((cmessage) >> 8) & 0xFF) as u8,
            data2: (((cmessage) >> 16) & 0xFF) as u8,
        }
    }

    fn unwrap(&self) -> ffi::PmMessage {
        ((((self.data2 as i32) << 16) & 0xFF0000) | (((self.data1 as i32) << 8) & 0xFF00) |
         ((self.status as i32) & 0xFF)) as i32
    }
}

/// Represents a time stamped midi event. See also `MidiMessage`
///
/// See the PortMidi documentation for how SysEx and midi realtime messages
/// are handled
///
/// TODO: what to do about the timestamp?
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct MidiEvent {
    pub message: MidiMessage,
    pub timestamp: ffi::PmTimestamp,
}

impl MidiEvent {
    fn wrap(event: ffi::PmEvent) -> MidiEvent {
        MidiEvent {
            message: MidiMessage::wrap(event.message),
            timestamp: event.timestamp,
        }
    }

    fn unwrap(&self) -> ffi::PmEvent {
        ffi::PmEvent {
            message: self.message.unwrap(),
            timestamp: self.timestamp,
        }
    }
}

// Input
// -----
/// Representation of an input midi port
#[allow(missing_copy_implementations)]
pub struct InputPort {
    stream: *const ffi::PortMidiStream,
    input_device: ffi::PmDeviceId,
    buffer_size: i32,
}

const EVENT_BUFFER_SIZE: usize = 128;
impl InputPort {
    /// Construct a new `InputPort` for `input_device`
    pub fn new(input_device: PortMidiDeviceId, buffer_size: i32) -> InputPort {
        InputPort {
            stream: ptr::null(),
            input_device: input_device,
            buffer_size: buffer_size,
        }
    }

    /// Open the port returning an error if there is a problem
    pub fn open(&mut self) -> PortMidiResult<()> {
        PortMidiResult::from(unsafe {
            ffi::Pm_OpenInput(&self.stream,
                              self.input_device,
                              ptr::null(),
                              self.buffer_size,
                              ptr::null(),
                              ptr::null())
        })
    }

    pub fn read_n(&mut self, cnt: usize) -> PortMidiResult<Option<Vec<MidiEvent>>> {
        let read_cnt = if cnt > EVENT_BUFFER_SIZE {
            EVENT_BUFFER_SIZE as i32
        } else {
            cnt as i32
        };
        let mut event_buffer = [ffi::PmEvent::default(); EVENT_BUFFER_SIZE];
        let res = unsafe {
            ffi::Pm_Read(self.stream,
                         event_buffer.as_mut_ptr(),
                         read_cnt)
        };
        if res < 0 {
            let err = ffi::PmError::try_from(res).unwrap();
            // TODO: Return the error
            println!("error: {:?}", err);
            return Ok(None);
        } else if res == 0 {
            Ok(None)
        } else {
            // remove mutability and replace return value
            let mut events = (0..res as usize)
                             .map(|i| MidiEvent::wrap(event_buffer[i].clone()))
                             .collect::<Vec<MidiEvent>>();
            Ok(Some(events))
        }
    }

    /// Reads a single `MidiEvent` if one is avaible
    ///
    /// A `Result` of `None` means no event was available.
    ///
    /// See the PortMidi documentation for information on how it deals with input
    /// overflows
    /// TODO: call `read_n`
    pub fn read(&mut self) -> PortMidiResult<Option<MidiEvent>> {
        match self.read_n(1) {
            Ok(Some(mut vec)) => Ok(vec.pop()),
            Ok(_) => Ok(None),
            Err(e) => Err(e),
        }
    }

    /// `poll` tests if there is input available, either returing a bool or an error
    pub fn poll(&self) -> PortMidiResult<bool> {
        let pm_error = unsafe { ffi::Pm_Poll(self.stream) };
        match pm_error {
            ffi::PmError::PmNoError => Ok(false),
            ffi::PmError::PmGotData => Ok(true),
            other => PortMidiResult::from(other).map(|_| false),
        }
    }

    /// Closes the input, flushing any pending buffers
    ///
    /// PortMidi attempts to close open streams when the application
    /// exits, but this can be difficult under Windows
    /// (according to the PortMidi documentation).
    pub fn close(&mut self) -> PortMidiResult<()> {
        PortMidiResult::from(unsafe { ffi::Pm_Close(self.stream) })
    }
}

// Output
// ------
/// Representation of an output midi port
#[allow(missing_copy_implementations)]
pub struct OutputPort {
    stream: *const ffi::PortMidiStream,
    output_device: ffi::PmDeviceId,
    buffer_size: i32,
}
impl OutputPort {
    /// Construct a new `InputPort` for `input_device`
    pub fn new(output_device: PortMidiDeviceId, buffer_size: i32) -> OutputPort {
        OutputPort {
            stream: ptr::null(),
            output_device: output_device,
            buffer_size: buffer_size,
        }
    }

    /// Open the port returning an error if there is a problem
    pub fn open(&mut self) -> PortMidiResult<()> {
        PortMidiResult::from(unsafe {
            ffi::Pm_OpenOutput(&self.stream,
                               self.output_device,
                               ptr::null(),
                               self.buffer_size,
                               ptr::null(),
                               ptr::null(),
                               0)
        })
    }

    /// Terminates outgoing messages immediately
    ///
    /// The caller should immediately close the output port, this may
    /// result in transmission of a partial midi message. Note, not all platforms
    /// support abort.
    pub fn abort(&mut self) -> PortMidiResult<()> {
        PortMidiResult::from(unsafe { ffi::Pm_Abort(self.stream) })
    }

    /// Closes the midi stream, flushing any pending buffers
    ///
    /// PortMidi attempts to close open streams when the application
    /// exits, but this can be difficult under Windows
    /// (according to the PortMidi documentation).
    pub fn close(&mut self) -> PortMidiResult<()> {
        PortMidiResult::from(unsafe { ffi::Pm_Close(self.stream) })
    }

    /// Write a single `MidiEvent`
    pub fn write_event(&mut self, midi_event: MidiEvent) -> PortMidiResult<()> {
        let event = midi_event.unwrap();
        PortMidiResult::from(unsafe { ffi::Pm_Write(self.stream, &event, 1) })
    }

    /// Write a single `MidiMessage` immediately
    pub fn write_message(&mut self, midi_message: MidiMessage) -> PortMidiResult<()> {
        let message = midi_message.unwrap();
        PortMidiResult::from(unsafe { ffi::Pm_WriteShort(self.stream, 0, message) })
    }
}

pub const HDRLENGTH: i32 = 50;
pub const PM_HOST_ERROR_MSG_LEN: i32 = 256;
<|endoftext|>


<commit_before>//! Ethereum devp2p protocol implementation
//!
//! It is layered in the following way:
//! * `RLPxNode` which represents the whole pool of connected peers. It handles message routing and peer management.
//! * `MuxServer` which provides a request-response API to otherwise stateless P2P protocol.
//! * `EthIngressServer` which `MuxServer` calls into when new requests and gossip messages arrive.
//! * `MuxServer` itself implements `EthProtocol` which is a simple gateway to abstract Ethereum network.

#![allow(clippy::large_enum_variant)]

mod disc;
pub mod ecies;
mod errors;
mod mac;
mod mux;
mod node_filter;
mod peer;
mod rlpx;
mod types;
mod util;

pub use disc::*;
pub use mux::MuxServer;
pub use peer::{DisconnectReason, PeerStream};
pub use rlpx::{
    DiscoveryOptions, ListenOptions, Server as RLPxNode, ServerBuilder as RLPxNodeBuilder,
};
pub use types::{
    CapabilityId, CapabilityInfo, CapabilityName, CapabilityRegistrar, CapabilityServer,
    HandleError, IngressPeer, Message, NodeRecord, PeerConnectOutcome, PeerId, ReputationReport,
};
<commit_msg>expose util<commit_after>//! Ethereum devp2p protocol implementation
//!
//! It is layered in the following way:
//! * `RLPxNode` which represents the whole pool of connected peers. It handles message routing and peer management.
//! * `MuxServer` which provides a request-response API to otherwise stateless P2P protocol.
//! * `EthIngressServer` which `MuxServer` calls into when new requests and gossip messages arrive.
//! * `MuxServer` itself implements `EthProtocol` which is a simple gateway to abstract Ethereum network.

#![allow(clippy::large_enum_variant)]

mod disc;
pub mod ecies;
mod errors;
mod mac;
mod mux;
mod node_filter;
mod peer;
mod rlpx;
mod types;
pub mod util;

pub use disc::*;
pub use mux::MuxServer;
pub use peer::{DisconnectReason, PeerStream};
pub use rlpx::{
    DiscoveryOptions, ListenOptions, Server as RLPxNode, ServerBuilder as RLPxNodeBuilder,
};
pub use types::{
    CapabilityId, CapabilityInfo, CapabilityName, CapabilityRegistrar, CapabilityServer,
    HandleError, IngressPeer, Message, NodeRecord, PeerConnectOutcome, PeerId, ReputationReport,
};
<|endoftext|>


<commit_before>//! This library provides [`anyhow::Error`][Error], a trait object based error
//! type for easy idiomatic error handling in Rust applications.
//!
//!

//!
//! # Details
//!
//! - Use `Result<T, anyhow::Error>`, or equivalently `anyhow::Result<T>`, as
//!   the return type of any fallible function.
//!
//!   Within the function, use `?` to easily propagate any error that implements
//!   the `std::error::Error` trait.
//!
//!   ```
//!   # pub trait Deserialize {}
//!   #
//!   # mod serde_json {
//!   #     use super::Deserialize;
//!   #     use std::io;
//!   #
//!   #     pub fn from_str<T: Deserialize>(json: &str) -> io::Result<T> {
//!   #         unimplemented!()
//!   #     }
//!   # }
//!   #
//!   # struct ClusterMap;
//!   #
//!   # impl Deserialize for ClusterMap {}
//!   #
//!   use anyhow::Result;
//!
//!   fn get_cluster_info() -> Result<ClusterMap> {
//!       let config = std::fs::read_to_string("cluster.json")?;
//!       let map: ClusterMap = serde_json::from_str(&config)?;
//!       Ok(map)
//!   }
//!   #
//!   # fn main() {}
//!   ```
//!
//! - Attach context to help the person troubleshooting the error understand
//!   where things went wrong. A low-level error like "No such file or
//!   directory" can be annoying to debug without more context about what higher
//!   level step the application was in the middle of.
//!
//!   ```
//!   # struct It;
//!   #
//!   # impl It {
//!   #     fn detach(&self) -> Result<()> {
//!   #         unimplemented!()
//!   #     }
//!   # }
//!   #
//!   use anyhow::{Context, Result};
//!
//!   fn main() -> Result<()> {
//!       # return Ok(());
//!       #
//!       # const _: &str = stringify! {
//!       ...
//!       # };
//!       #
//!       # let it = It;
//!       # let path = "./path/to/instrs.jsox";
//!       #
//!       it.detach().context("failed to detach the important thing")?;
//!
//!       let content = std::fs::read(path)
//!           .with_context(|| format!("failed to read instrs from {}", path))?;
//!       #
//!       # const _: &str = stringify! {
//!       ...
//!       # };
//!       #
//!       # Ok(())
//!   }
//!   ```
//!
//!   ```console
//!   Error: failed to read instrs from ./path/to/instrs.jsox
//!
//!   Caused by:
//!       No such file or directory (os error 2)
//!   ```
//!
//! - Downcasting is supported and can be by value, by shared reference, or by
//!   mutable reference as needed.
//!
//!   ```
//!   # use anyhow::anyhow;
//!   # use std::fmt::{self, Display};
//!   # use std::task::Poll;
//!   #
//!   # #[derive(Debug)]
//!   # enum DataStoreError {
//!   #     Censored(()),
//!   # }
//!   #
//!   # impl Display for DataStoreError {
//!   #     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
//!   #         unimplemented!()
//!   #     }
//!   # }
//!   #
//!   # impl std::error::Error for DataStoreError {}
//!   #
//!   # const REDACTED_CONTENT: () = ();
//!   #
//!   # let error = anyhow!("...");
//!   # let root_cause = &error;
//!   #
//!   # let ret =
//!   // If the error was caused by redaction, then return a
//!   // tombstone instead of the content.
//!   match root_cause.downcast_ref::<DataStoreError>() {
//!       Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)),
//!       None => Err(error),
//!   }
//!   # ;
//!   ```
//!
//! - A backtrace is captured and printed with the error if the underlying error
//!   type does not already provide its own. In order to see backtraces, the
//!   `RUST_LIB_BACKTRACE=1` environment variable must be defined.
//!
//! - Anyhow works with any error type that has an impl of `std::error::Error`,
//!   including ones defined in your crate. We do not bundle a `derive(Error)`
//!   macro but you can write the impls yourself or use a standalone macro like
//!   [err-derive].
//!
//!   [err-derive]: https://crates.io/crates/err-derive
//!
//!   ```
//!   # const IGNORE: &str = stringify! {
//!   #[derive(Error, Debug)]
//!   pub enum FormatError {
//!       #[error(display = "invalid header (expected {:?}, got {:?})", expected, found)]
//!       InvalidHeader {
//!           expected: String,
//!           found: String,
//!       },
//!       #[error(display = "missing attribute: {}", _0)]
//!       MissingAttribute(String),
//!   }
//!   # };
//!   ```
//!
//! - One-off error messages can be constructed using the `anyhow!` macro, which
//!   supports string interpolation and produces an `anyhow::Error`.
//!
//!   ```
//!   # use anyhow::{anyhow, Result};
//!   #
//!   # fn demo() -> Result<()> {
//!   #     let missing = "...";
//!   return Err(anyhow!("missing attribute: {}", missing));
//!   #     Ok(())
//!   # }
//!   ```
//!
//!

//!
//! # Acknowledgements
//!
//! The implementation of the `anyhow::Error` type is forked from
//! `fehler::Exception` ([https://github.com/withoutboats/fehler][fehler]). This
//! library just exposes it under the more standard `Error` / `Result`
//! terminology rather than the `throw!` / `#[throws]` / `Exception` language of
//! exceptions.
//!
//! [fehler]: https://github.com/withoutboats/fehler

#![doc(html_root_url = "https://docs.rs/anyhow/1.0.0")]
#![cfg_attr(backtrace, feature(backtrace))]

mod context;
mod error;

#[cfg(not(feature = "std"))]
compile_error!("no_std support is not implemented yet");

pub use crate::context::Context;
pub use crate::error::{Chain, Error};

/// `Result<T, Error>`
///
/// This is a reasonable return type to use throughout your application but also
/// for `fn main`; if you do, failures will be printed along with any
/// [context][Context] and a backtrace if one was captured.
///
/// # Example
///
/// ```
/// # pub trait Deserialize {}
/// #
/// # mod serde_json {
/// #     use super::Deserialize;
/// #     use std::io;
/// #
/// #     pub fn from_str<T: Deserialize>(json: &str) -> io::Result<T> {
/// #         unimplemented!()
/// #     }
/// # }
/// #
/// # #[derive(Debug)]
/// # struct ClusterMap;
/// #
/// # impl Deserialize for ClusterMap {}
/// #
/// use anyhow::Result;
///
/// fn main() -> Result<()> {
///     # return Ok(());
///     let config = std::fs::read_to_string("cluster.json")?;
///     let map: ClusterMap = serde_json::from_str(&config)?;
///     println!("cluster info: {:#?}", map);
///     Ok(())
/// }
/// ```
pub type Result<T> = std::result::Result<T, Error>;

/// Return early with an error.
///
/// This macro is equivalent to `return Err(From::from($err))`.
///
/// # Example
///
/// ```
/// # use anyhow::{bail, Result};
/// #
/// # fn has_permission(user: usize, resource: usize) -> bool {
/// #     true
/// # }
/// #
/// # fn main() -> Result<()> {
/// #     let user = 0;
/// #     let resource = 0;
/// #
/// if !has_permission(user, resource) {
///     bail!("permission denied for accessing {}", resource);
/// }
/// #     Ok(())
/// # }
/// ```
///
/// ```
/// # use anyhow::{bail, Result};
/// # use std::fmt::{self, Display};
/// #
/// # #[derive(Debug)]
/// # enum ScienceError {
/// #     RecursionLimitExceeded,
/// # }
/// #
/// # impl std::error::Error for ScienceError {}
/// #
/// # impl Display for ScienceError {
/// #     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
/// #         unimplemented!()
/// #     }
/// # }
/// #
/// # const MAX_DEPTH: usize = 1;
/// #
/// # const IGNORE: &str = stringify! {
/// #[derive(Error, Debug)]
/// enum ScienceError {
///     #[error(display = "recursion limit exceeded")]
///     RecursionLimitExceeded,
///     ...
/// }
/// # };
///
/// # fn main() -> Result<()> {
/// #     let depth = 0;
/// #
/// if depth > MAX_DEPTH {
///     bail!(ScienceError::RecursionLimitExceeded);
/// }
/// #     Ok(())
/// # }
/// ```
#[macro_export]
macro_rules! bail {
    ($err:expr $(,)?) => {
        return std::result::Result::Err(std::convert::From::from($err));
    };
    ($fmt:expr, $($arg:tt)*) => {
        return std::result::Result::Err($crate::anyhow!($fmt, $($arg)*));
    };
}

/// Construct an ad-hoc error from a string.
///
/// This evaluates to an `Error`. It can take either just a string, or a format
/// string with arguments. It also can take any custom type which implements
/// `Debug` and `Display`.
///
/// # Example
///
/// ```
/// # type V = ();
/// #
/// use anyhow::{anyhow, Result};
///
/// fn lookup(key: &str) -> Result<V> {
///     if key.len() != 16 {
///         return Err(anyhow!("key length must be 16 characters, got {:?}", key));
///     }
///
///     // ...
///     # Ok(())
/// }
/// ```
#[macro_export]
macro_rules! anyhow {
    ($msg:expr $(,)?) => {
        $crate::private::new_adhoc($msg)
    };
    ($fmt:expr, $($arg:tt)*) => {
        $crate::private::new_adhoc(format!($fmt, $($arg)*))
    };
}

// Not public API.
#[doc(hidden)]
pub mod private {
    use crate::Error;
    use std::fmt::{Debug, Display};

    #[cfg(backtrace)]
    use std::backtrace::Backtrace;

    pub fn new_adhoc<M>(message: M) -> Error
    where
        M: Display + Debug + Send + Sync + 'static,
    {
        #[cfg(backtrace)]
        let backtrace = Some(Backtrace::capture());

        #[cfg(not(backtrace))]
        let backtrace = None;

        Error::new_adhoc(message, backtrace)
    }
}
<commit_msg>Add ensure macro. Fix #1.<commit_after>//! This library provides [`anyhow::Error`][Error], a trait object based error
//! type for easy idiomatic error handling in Rust applications.
//!
//!

//!
//! # Details
//!
//! - Use `Result<T, anyhow::Error>`, or equivalently `anyhow::Result<T>`, as
//!   the return type of any fallible function.
//!
//!   Within the function, use `?` to easily propagate any error that implements
//!   the `std::error::Error` trait.
//!
//!   ```
//!   # pub trait Deserialize {}
//!   #
//!   # mod serde_json {
//!   #     use super::Deserialize;
//!   #     use std::io;
//!   #
//!   #     pub fn from_str<T: Deserialize>(json: &str) -> io::Result<T> {
//!   #         unimplemented!()
//!   #     }
//!   # }
//!   #
//!   # struct ClusterMap;
//!   #
//!   # impl Deserialize for ClusterMap {}
//!   #
//!   use anyhow::Result;
//!
//!   fn get_cluster_info() -> Result<ClusterMap> {
//!       let config = std::fs::read_to_string("cluster.json")?;
//!       let map: ClusterMap = serde_json::from_str(&config)?;
//!       Ok(map)
//!   }
//!   #
//!   # fn main() {}
//!   ```
//!
//! - Attach context to help the person troubleshooting the error understand
//!   where things went wrong. A low-level error like "No such file or
//!   directory" can be annoying to debug without more context about what higher
//!   level step the application was in the middle of.
//!
//!   ```
//!   # struct It;
//!   #
//!   # impl It {
//!   #     fn detach(&self) -> Result<()> {
//!   #         unimplemented!()
//!   #     }
//!   # }
//!   #
//!   use anyhow::{Context, Result};
//!
//!   fn main() -> Result<()> {
//!       # return Ok(());
//!       #
//!       # const _: &str = stringify! {
//!       ...
//!       # };
//!       #
//!       # let it = It;
//!       # let path = "./path/to/instrs.jsox";
//!       #
//!       it.detach().context("failed to detach the important thing")?;
//!
//!       let content = std::fs::read(path)
//!           .with_context(|| format!("failed to read instrs from {}", path))?;
//!       #
//!       # const _: &str = stringify! {
//!       ...
//!       # };
//!       #
//!       # Ok(())
//!   }
//!   ```
//!
//!   ```console
//!   Error: failed to read instrs from ./path/to/instrs.jsox
//!
//!   Caused by:
//!       No such file or directory (os error 2)
//!   ```
//!
//! - Downcasting is supported and can be by value, by shared reference, or by
//!   mutable reference as needed.
//!
//!   ```
//!   # use anyhow::anyhow;
//!   # use std::fmt::{self, Display};
//!   # use std::task::Poll;
//!   #
//!   # #[derive(Debug)]
//!   # enum DataStoreError {
//!   #     Censored(()),
//!   # }
//!   #
//!   # impl Display for DataStoreError {
//!   #     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
//!   #         unimplemented!()
//!   #     }
//!   # }
//!   #
//!   # impl std::error::Error for DataStoreError {}
//!   #
//!   # const REDACTED_CONTENT: () = ();
//!   #
//!   # let error = anyhow!("...");
//!   # let root_cause = &error;
//!   #
//!   # let ret =
//!   // If the error was caused by redaction, then return a
//!   // tombstone instead of the content.
//!   match root_cause.downcast_ref::<DataStoreError>() {
//!       Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)),
//!       None => Err(error),
//!   }
//!   # ;
//!   ```
//!
//! - A backtrace is captured and printed with the error if the underlying error
//!   type does not already provide its own. In order to see backtraces, the
//!   `RUST_LIB_BACKTRACE=1` environment variable must be defined.
//!
//! - Anyhow works with any error type that has an impl of `std::error::Error`,
//!   including ones defined in your crate. We do not bundle a `derive(Error)`
//!   macro but you can write the impls yourself or use a standalone macro like
//!   [err-derive].
//!
//!   [err-derive]: https://crates.io/crates/err-derive
//!
//!   ```
//!   # const IGNORE: &str = stringify! {
//!   #[derive(Error, Debug)]
//!   pub enum FormatError {
//!       #[error(display = "invalid header (expected {:?}, got {:?})", expected, found)]
//!       InvalidHeader {
//!           expected: String,
//!           found: String,
//!       },
//!       #[error(display = "missing attribute: {}", _0)]
//!       MissingAttribute(String),
//!   }
//!   # };
//!   ```
//!
//! - One-off error messages can be constructed using the `anyhow!` macro, which
//!   supports string interpolation and produces an `anyhow::Error`.
//!
//!   ```
//!   # use anyhow::{anyhow, Result};
//!   #
//!   # fn demo() -> Result<()> {
//!   #     let missing = "...";
//!   return Err(anyhow!("missing attribute: {}", missing));
//!   #     Ok(())
//!   # }
//!   ```
//!
//!

//!
//! # Acknowledgements
//!
//! The implementation of the `anyhow::Error` type is forked from
//! `fehler::Exception` ([https://github.com/withoutboats/fehler][fehler]). This
//! library just exposes it under the more standard `Error` / `Result`
//! terminology rather than the `throw!` / `#[throws]` / `Exception` language of
//! exceptions.
//!
//! [fehler]: https://github.com/withoutboats/fehler

#![doc(html_root_url = "https://docs.rs/anyhow/1.0.0")]
#![cfg_attr(backtrace, feature(backtrace))]

mod context;
mod error;

#[cfg(not(feature = "std"))]
compile_error!("no_std support is not implemented yet");

pub use crate::context::Context;
pub use crate::error::{Chain, Error};

/// `Result<T, Error>`
///
/// This is a reasonable return type to use throughout your application but also
/// for `fn main`; if you do, failures will be printed along with any
/// [context][Context] and a backtrace if one was captured.
///
/// # Example
///
/// ```
/// # pub trait Deserialize {}
/// #
/// # mod serde_json {
/// #     use super::Deserialize;
/// #     use std::io;
/// #
/// #     pub fn from_str<T: Deserialize>(json: &str) -> io::Result<T> {
/// #         unimplemented!()
/// #     }
/// # }
/// #
/// # #[derive(Debug)]
/// # struct ClusterMap;
/// #
/// # impl Deserialize for ClusterMap {}
/// #
/// use anyhow::Result;
///
/// fn main() -> Result<()> {
///     # return Ok(());
///     let config = std::fs::read_to_string("cluster.json")?;
///     let map: ClusterMap = serde_json::from_str(&config)?;
///     println!("cluster info: {:#?}", map);
///     Ok(())
/// }
/// ```
pub type Result<T> = std::result::Result<T, Error>;

/// Return early with an error.
///
/// This macro is equivalent to `return Err(From::from($err))`.
///
/// # Example
///
/// ```
/// # use anyhow::{bail, Result};
/// #
/// # fn has_permission(user: usize, resource: usize) -> bool {
/// #     true
/// # }
/// #
/// # fn main() -> Result<()> {
/// #     let user = 0;
/// #     let resource = 0;
/// #
/// if !has_permission(user, resource) {
///     bail!("permission denied for accessing {}", resource);
/// }
/// #     Ok(())
/// # }
/// ```
///
/// ```
/// # use anyhow::{bail, Result};
/// # use std::fmt::{self, Display};
/// #
/// # #[derive(Debug)]
/// # enum ScienceError {
/// #     RecursionLimitExceeded,
/// # }
/// #
/// # impl std::error::Error for ScienceError {}
/// #
/// # impl Display for ScienceError {
/// #     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
/// #         unimplemented!()
/// #     }
/// # }
/// #
/// # const MAX_DEPTH: usize = 1;
/// #
/// # const IGNORE: &str = stringify! {
/// #[derive(Error, Debug)]
/// enum ScienceError {
///     #[error(display = "recursion limit exceeded")]
///     RecursionLimitExceeded,
///     ...
/// }
/// # };
///
/// # fn main() -> Result<()> {
/// #     let depth = 0;
/// #
/// if depth > MAX_DEPTH {
///     bail!(ScienceError::RecursionLimitExceeded);
/// }
/// #     Ok(())
/// # }
/// ```
#[macro_export]
macro_rules! bail {
    ($err:expr $(,)?) => {
        return std::result::Result::Err(std::convert::From::from($err));
    };
    ($fmt:expr, $($arg:tt)*) => {
        return std::result::Result::Err($crate::anyhow!($fmt, $($arg)*));
    };
}

/// Return early with an error if a condition is not satisfied.
///
/// This macro is equivalent to `if !condition { return Err(From::from($err)) }`.
///
/// Similar to `assert!`, `ensure!` takes a condition and exits the function
/// if the condition fails. Unlike `assert!`, `ensure!` returns an `Error`,
/// it does not panic.
///
/// # Example
///
/// ```
/// # use anyhow::{ensure, Result};
/// #
/// #
/// # fn main() -> Result<()> {
/// #     let user = 0;
/// #     let resource = 0;
/// #
/// ensure!(user == 0, "only user 0 is allowed");
/// #     Ok(())
/// # }
/// ```
///
/// ```
/// # use anyhow::{ensure, Result};
/// # use std::fmt::{self, Display};
/// #
/// # #[derive(Debug)]
/// # enum ScienceError {
/// #     RecursionLimitExceeded,
/// # }
/// #
/// # impl std::error::Error for ScienceError {}
/// #
/// # impl Display for ScienceError {
/// #     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
/// #         unimplemented!()
/// #     }
/// # }
/// #
/// # const MAX_DEPTH: usize = 1;
/// #
/// # const IGNORE: &str = stringify! {
/// #[derive(Error, Debug)]
/// enum ScienceError {
///     #[error(display = "recursion limit exceeded")]
///     RecursionLimitExceeded,
///     ...
/// }
/// # };
///
/// # fn main() -> Result<()> {
/// #     let depth = 0;
/// #
/// ensure!(depth <= MAX_DEPTH, ScienceError::RecursionLimitExceeded);
/// #     Ok(())
/// # }
/// ```
#[macro_export]
macro_rules! ensure {
    ($cond:expr, $err:expr $(,)?) => {
        if !($cond) {
            return std::result::Result::Err($crate::anyhow!($err));
        }
    };
    ($cond:expr, $fmt:expr, $($arg:tt)*) => {
        if !($cond) {
            return std::result::Result::Err($crate::anyhow!($fmt, $($arg)*));
        }
    };
}

/// Construct an ad-hoc error from a string.
///
/// This evaluates to an `Error`. It can take either just a string, or a format
/// string with arguments. It also can take any custom type which implements
/// `Debug` and `Display`.
///
/// # Example
///
/// ```
/// # type V = ();
/// #
/// use anyhow::{anyhow, Result};
///
/// fn lookup(key: &str) -> Result<V> {
///     if key.len() != 16 {
///         return Err(anyhow!("key length must be 16 characters, got {:?}", key));
///     }
///
///     // ...
///     # Ok(())
/// }
/// ```
#[macro_export]
macro_rules! anyhow {
    ($msg:expr $(,)?) => {
        $crate::private::new_adhoc($msg)
    };
    ($fmt:expr, $($arg:tt)*) => {
        $crate::private::new_adhoc(format!($fmt, $($arg)*))
    };
}

// Not public API.
#[doc(hidden)]
pub mod private {
    use crate::Error;
    use std::fmt::{Debug, Display};

    #[cfg(backtrace)]
    use std::backtrace::Backtrace;

    pub fn new_adhoc<M>(message: M) -> Error
    where
        M: Display + Debug + Send + Sync + 'static,
    {
        #[cfg(backtrace)]
        let backtrace = Some(Backtrace::capture());

        #[cfg(not(backtrace))]
        let backtrace = None;

        Error::new_adhoc(message, backtrace)
    }
}

#[cfg(test)]
mod test {
    #[test]
    fn ensure() {
        let f = || {
            ensure!(1 + 1 == 2, "This is correct");
            Ok(())
        };
        assert!(f().is_ok());

        let v = 1;
        let f = || {
            ensure!(v + v == 2, "This is correct, v: {}", v);
            Ok(())
        };
        assert!(f().is_ok());

        let f = || {
            ensure!(v + v == 1, "This is not correct, v: {}", v);
            Ok(())
        };
        assert!(f().is_err());
    }
}
<|endoftext|>


<commit_before>mod raft_log;
mod storage;
mod progress;
mod errors;
mod log_unstable;
<commit_msg>raft: export necessary unit for outer use.<commit_after>mod raft_log;
mod progress;
mod log_unstable;

pub mod storage;
pub mod errors;

pub use self::storage::{RaftState, Storage};

pub use self::errors::{Result, Error, StorageError};
<|endoftext|>


<commit_before>use std::iter::IntoIterator;

use syntax::ast;
use syntax::codemap::{DUMMY_SP, Span, Spanned, respan};
use syntax::ptr::P;

use invoke::{Invoke, Identity};

use expr::ExprBuilder;
use ident::ToIdent;
use path::PathBuilder;

//////////////////////////////////////////////////////////////////////////////

pub struct PatBuilder<F=Identity> {
    callback: F,
    span: Span,
}

impl PatBuilder {
    pub fn new() -> Self {
        PatBuilder::with_callback(Identity)
    }
}

impl<F> PatBuilder<F>
    where F: Invoke<P<ast::Pat>>,
{
    pub fn with_callback(callback: F) -> Self {
        PatBuilder {
            callback: callback,
            span: DUMMY_SP,
        }
    }

    pub fn span(mut self, span: Span) -> Self {
        self.span = span;
        self
    }

    pub fn build(self, pat: P<ast::Pat>) -> F::Result {
        self.callback.invoke(pat)
    }

    pub fn build_pat_(self, pat_: ast::Pat_) -> F::Result {
        let span = self.span;
        self.build(P(ast::Pat {
            id: ast::DUMMY_NODE_ID,
            node: pat_,
            span: span,
        }))
    }

    pub fn wild(self) -> F::Result {
        self.build_pat_(ast::Pat_::PatWild)
    }

    pub fn build_id<I>(self, mode: ast::BindingMode, id: I, sub: Option<P<ast::Pat>>) -> F::Result
        where I: ToIdent,
    {
        let id = respan(self.span, id.to_ident());

        self.build_pat_(ast::Pat_::PatIdent(mode, id, sub))
    }

    pub fn id<I>(self, id: I) -> F::Result
        where I: ToIdent
    {
        let mode = ast::BindingMode::ByValue(ast::Mutability::MutImmutable);
        self.build_id(mode, id, None)
    }

    pub fn mut_id<I>(self, id: I) -> F::Result
        where I: ToIdent
    {
        let mode = ast::BindingMode::ByValue(ast::Mutability::MutMutable);
        self.build_id(mode, id, None)
    }

    pub fn ref_id<I>(self, id: I) -> F::Result
        where I: ToIdent
    {
        let mode = ast::BindingMode::ByRef(ast::Mutability::MutImmutable);
        self.build_id(mode, id, None)
    }

    pub fn ref_mut_id<I>(self, id: I) -> F::Result
        where I: ToIdent
    {
        let mode = ast::BindingMode::ByRef(ast::Mutability::MutMutable);
        self.build_id(mode, id, None)
    }

    pub fn enum_(self) -> PathBuilder<PatEnumBuilder<F>> {
        PathBuilder::with_callback(PatEnumBuilder(self))
    }

    pub fn struct_(self) -> PathBuilder<PatStructBuilder<F>> {
        PathBuilder::with_callback(PatStructBuilder(self))
    }

    pub fn expr(self) -> ExprBuilder<PatExprBuilder<F>> {
        ExprBuilder::with_callback(PatExprBuilder(self))
    }

    pub fn tuple(self) -> PatTupleBuilder<F> {
        PatTupleBuilder {
            builder: self,
            pats: Vec::new(),
        }
    }
}

//////////////////////////////////////////////////////////////////////////////

pub struct PatEnumBuilder<F>(PatBuilder<F>);

impl<F> Invoke<ast::Path> for PatEnumBuilder<F> {
    type Result = PatEnumPathBuilder<F>;

    fn invoke(self, path: ast::Path) -> PatEnumPathBuilder<F> {
        PatEnumPathBuilder {
            builder: self.0,
            path: path,
            pats: Vec::new(),
        }
    }
}

//////////////////////////////////////////////////////////////////////////////

pub struct PatEnumPathBuilder<F> {
    builder: PatBuilder<F>,
    path: ast::Path,
    pats: Vec<P<ast::Pat>>,
}

impl<F> PatEnumPathBuilder<F>
    where F: Invoke<P<ast::Pat>>,
{
    pub fn with_pats<I>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=P<ast::Pat>>,
    {
        self.pats.extend(iter);
        self
    }

    pub fn pat(self) -> PatBuilder<Self> {
        PatBuilder::with_callback(self)
    }

    pub fn with_ids<I, T>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=T>,
              T: ToIdent,
    {
        for id in iter {
            self = self.id(id);
        }
        self
    }

    pub fn id<I>(self, id: I) -> Self
        where I: ToIdent
    {
        self.pat().id(id)
    }

    pub fn build(self) -> F::Result {
        self.builder.build_pat_(ast::Pat_::PatEnum(self.path, Some(self.pats)))
    }
}

impl<F> Invoke<P<ast::Pat>> for PatEnumPathBuilder<F>
    where F: Invoke<P<ast::Pat>>,
{
    type Result = Self;

    fn invoke(mut self, pat: P<ast::Pat>) -> Self {
        self.pats.push(pat);
        self
    }
}

//////////////////////////////////////////////////////////////////////////////

pub struct PatStructBuilder<F>(PatBuilder<F>);

impl<F> Invoke<ast::Path> for PatStructBuilder<F> {
    type Result = PatStructPathBuilder<F>;

    fn invoke(self, path: ast::Path) -> PatStructPathBuilder<F> {
        PatStructPathBuilder {
            builder: self.0,
            path: path,
            pats: Vec::new(),
        }
    }
}

//////////////////////////////////////////////////////////////////////////////

pub struct PatStructPathBuilder<F> {
    builder: PatBuilder<F>,
    path: ast::Path,
    pats: Vec<Spanned<ast::FieldPat>>,
}

impl<F> PatStructPathBuilder<F>
    where F: Invoke<P<ast::Pat>>,
{
    pub fn with_field_pat(mut self, pat: ast::FieldPat) -> Self {
        self.pats.push(respan(self.builder.span, pat));
        self
    }

    pub fn with_pats<I, T>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=(T, P<ast::Pat>)>,
              T: ToIdent,
    {
        for (id, pat) in iter {
            self = self.pat(id).build(pat);
        }
        self
    }

    pub fn pat<I>(self, id: I) -> PatBuilder<PatStructFieldBuilder<F>>
        where I: ToIdent,
    {
        PatBuilder::with_callback(PatStructFieldBuilder {
            builder: self,
            id: id.to_ident(),
        })
    }

    pub fn with_ids<I, T>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=T>,
              T: ToIdent,
    {
        for id in iter {
            self = self.id(id);
        }
        self
    }

    pub fn id<I>(self, id: I) -> Self
        where I: ToIdent,
    {
        let id = id.to_ident();
        let span = self.builder.span;
        let pat = PatBuilder::new().span(span).id(id);

        self.with_field_pat(ast::FieldPat {
            ident: id,
            pat: pat,
            is_shorthand: true,
        })
    }

    pub fn etc(self) -> F::Result {
        self.builder.build_pat_(ast::Pat_::PatStruct(self.path, self.pats, true))
    }

    pub fn build(self) -> F::Result {
        self.builder.build_pat_(ast::Pat_::PatStruct(self.path, self.pats, false))
    }
}

//////////////////////////////////////////////////////////////////////////////

pub struct PatStructFieldBuilder<F> {
    builder: PatStructPathBuilder<F>,
    id: ast::Ident,
}

impl<F> Invoke<P<ast::Pat>> for PatStructFieldBuilder<F>
    where F: Invoke<P<ast::Pat>>,
{
    type Result = PatStructPathBuilder<F>;

    fn invoke(self, pat: P<ast::Pat>) -> PatStructPathBuilder<F> {
        self.builder.with_field_pat(ast::FieldPat {
            ident: self.id,
            pat: pat,
            is_shorthand: false,
        })
    }
}

//////////////////////////////////////////////////////////////////////////////

pub struct PatExprBuilder<F>(PatBuilder<F>);

impl<F> Invoke<P<ast::Expr>> for PatExprBuilder<F>
    where F: Invoke<P<ast::Pat>>,
{
    type Result = F::Result;

    fn invoke(self, expr: P<ast::Expr>) -> F::Result {
        self.0.build_pat_(ast::Pat_::PatLit(expr))
    }
}

//////////////////////////////////////////////////////////////////////////////

pub struct PatTupleBuilder<F> {
    builder: PatBuilder<F>,
    pats: Vec<P<ast::Pat>>,
}

impl<F: Invoke<P<ast::Pat>>> PatTupleBuilder<F>
    where F: Invoke<P<ast::Pat>>
{
    pub fn with_pats<I>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=P<ast::Pat>>,
    {
        self.pats.extend(iter);
        self
    }

    pub fn with_pat(mut self, pat: P<ast::Pat>) -> Self {
        self.pats.push(pat);
        self
    }

    pub fn pat(self) -> PatBuilder<Self> {
        PatBuilder::with_callback(self)
    }

    pub fn build(self) -> F::Result {
        self.builder.build_pat_(ast::PatTup(self.pats))
    }
}

impl<F> Invoke<P<ast::Pat>> for PatTupleBuilder<F>
    where F: Invoke<P<ast::Pat>>
{
    type Result = PatTupleBuilder<F>;

    fn invoke(self, pat: P<ast::Pat>) -> Self {
        self.with_pat(pat)
    }
}
<commit_msg>feat(pat): Add support for mutable pat ids<commit_after>use std::iter::IntoIterator;

use syntax::ast;
use syntax::codemap::{DUMMY_SP, Span, Spanned, respan};
use syntax::ptr::P;

use invoke::{Invoke, Identity};

use expr::ExprBuilder;
use ident::ToIdent;
use path::PathBuilder;

//////////////////////////////////////////////////////////////////////////////

pub struct PatBuilder<F=Identity> {
    callback: F,
    span: Span,
}

impl PatBuilder {
    pub fn new() -> Self {
        PatBuilder::with_callback(Identity)
    }
}

impl<F> PatBuilder<F>
    where F: Invoke<P<ast::Pat>>,
{
    pub fn with_callback(callback: F) -> Self {
        PatBuilder {
            callback: callback,
            span: DUMMY_SP,
        }
    }

    pub fn span(mut self, span: Span) -> Self {
        self.span = span;
        self
    }

    pub fn build(self, pat: P<ast::Pat>) -> F::Result {
        self.callback.invoke(pat)
    }

    pub fn build_pat_(self, pat_: ast::Pat_) -> F::Result {
        let span = self.span;
        self.build(P(ast::Pat {
            id: ast::DUMMY_NODE_ID,
            node: pat_,
            span: span,
        }))
    }

    pub fn wild(self) -> F::Result {
        self.build_pat_(ast::Pat_::PatWild)
    }

    pub fn build_id<I>(self, mode: ast::BindingMode, id: I, sub: Option<P<ast::Pat>>) -> F::Result
        where I: ToIdent,
    {
        let id = respan(self.span, id.to_ident());

        self.build_pat_(ast::Pat_::PatIdent(mode, id, sub))
    }

    pub fn id<I>(self, id: I) -> F::Result
        where I: ToIdent
    {
        let mode = ast::BindingMode::ByValue(ast::Mutability::MutImmutable);
        self.build_id(mode, id, None)
    }

    pub fn mut_id<I>(self, id: I) -> F::Result
        where I: ToIdent
    {
        let mode = ast::BindingMode::ByValue(ast::Mutability::MutMutable);
        self.build_id(mode, id, None)
    }

    pub fn ref_id<I>(self, id: I) -> F::Result
        where I: ToIdent
    {
        let mode = ast::BindingMode::ByRef(ast::Mutability::MutImmutable);
        self.build_id(mode, id, None)
    }

    pub fn ref_mut_id<I>(self, id: I) -> F::Result
        where I: ToIdent
    {
        let mode = ast::BindingMode::ByRef(ast::Mutability::MutMutable);
        self.build_id(mode, id, None)
    }

    pub fn enum_(self) -> PathBuilder<PatEnumBuilder<F>> {
        PathBuilder::with_callback(PatEnumBuilder(self))
    }

    pub fn struct_(self) -> PathBuilder<PatStructBuilder<F>> {
        PathBuilder::with_callback(PatStructBuilder(self))
    }

    pub fn expr(self) -> ExprBuilder<PatExprBuilder<F>> {
        ExprBuilder::with_callback(PatExprBuilder(self))
    }

    pub fn tuple(self) -> PatTupleBuilder<F> {
        PatTupleBuilder {
            builder: self,
            pats: Vec::new(),
        }
    }
}

//////////////////////////////////////////////////////////////////////////////

pub struct PatEnumBuilder<F>(PatBuilder<F>);

impl<F> Invoke<ast::Path> for PatEnumBuilder<F> {
    type Result = PatEnumPathBuilder<F>;

    fn invoke(self, path: ast::Path) -> PatEnumPathBuilder<F> {
        PatEnumPathBuilder {
            builder: self.0,
            path: path,
            pats: Vec::new(),
        }
    }
}

//////////////////////////////////////////////////////////////////////////////

pub struct PatEnumPathBuilder<F> {
    builder: PatBuilder<F>,
    path: ast::Path,
    pats: Vec<P<ast::Pat>>,
}

impl<F> PatEnumPathBuilder<F>
    where F: Invoke<P<ast::Pat>>,
{
    pub fn with_pats<I>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=P<ast::Pat>>,
    {
        self.pats.extend(iter);
        self
    }

    pub fn pat(self) -> PatBuilder<Self> {
        PatBuilder::with_callback(self)
    }

    pub fn with_ids<I, T>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=T>,
              T: ToIdent,
    {
        for id in iter {
            self = self.id(id);
        }
        self
    }

    pub fn id<I>(self, id: I) -> Self
        where I: ToIdent
    {
        self.pat().id(id)
    }

    pub fn build(self) -> F::Result {
        self.builder.build_pat_(ast::Pat_::PatEnum(self.path, Some(self.pats)))
    }
}

impl<F> Invoke<P<ast::Pat>> for PatEnumPathBuilder<F>
    where F: Invoke<P<ast::Pat>>,
{
    type Result = Self;

    fn invoke(mut self, pat: P<ast::Pat>) -> Self {
        self.pats.push(pat);
        self
    }
}

//////////////////////////////////////////////////////////////////////////////

pub struct PatStructBuilder<F>(PatBuilder<F>);

impl<F> Invoke<ast::Path> for PatStructBuilder<F> {
    type Result = PatStructPathBuilder<F>;

    fn invoke(self, path: ast::Path) -> PatStructPathBuilder<F> {
        PatStructPathBuilder {
            builder: self.0,
            path: path,
            pats: Vec::new(),
        }
    }
}

//////////////////////////////////////////////////////////////////////////////

pub struct PatStructPathBuilder<F> {
    builder: PatBuilder<F>,
    path: ast::Path,
    pats: Vec<Spanned<ast::FieldPat>>,
}

impl<F> PatStructPathBuilder<F>
    where F: Invoke<P<ast::Pat>>,
{
    pub fn with_field_pat(mut self, pat: ast::FieldPat) -> Self {
        self.pats.push(respan(self.builder.span, pat));
        self
    }

    pub fn with_pats<I, T>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=(T, P<ast::Pat>)>,
              T: ToIdent,
    {
        for (id, pat) in iter {
            self = self.pat(id).build(pat);
        }
        self
    }

    pub fn pat<I>(self, id: I) -> PatBuilder<PatStructFieldBuilder<F>>
        where I: ToIdent,
    {
        PatBuilder::with_callback(PatStructFieldBuilder {
            builder: self,
            id: id.to_ident(),
        })
    }

    pub fn with_ids<I, T>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=T>,
              T: ToIdent,
    {
        for id in iter {
            self = self.id(id);
        }
        self
    }

    pub fn mut_id<I>(self, id: I) -> Self
        where I: ToIdent,
    {
        let id = id.to_ident();
        let span = self.builder.span;
        let pat = PatBuilder::new().span(span).mut_id(id);

        self.with_field_pat(ast::FieldPat {
            ident: id,
            pat: pat,
            is_shorthand: true,
        })
    }

    pub fn id<I>(self, id: I) -> Self
        where I: ToIdent,
    {
        let id = id.to_ident();
        let span = self.builder.span;
        let pat = PatBuilder::new().span(span).id(id);

        self.with_field_pat(ast::FieldPat {
            ident: id,
            pat: pat,
            is_shorthand: true,
        })
    }

    pub fn etc(self) -> F::Result {
        self.builder.build_pat_(ast::Pat_::PatStruct(self.path, self.pats, true))
    }

    pub fn build(self) -> F::Result {
        self.builder.build_pat_(ast::Pat_::PatStruct(self.path, self.pats, false))
    }
}

//////////////////////////////////////////////////////////////////////////////

pub struct PatStructFieldBuilder<F> {
    builder: PatStructPathBuilder<F>,
    id: ast::Ident,
}

impl<F> Invoke<P<ast::Pat>> for PatStructFieldBuilder<F>
    where F: Invoke<P<ast::Pat>>,
{
    type Result = PatStructPathBuilder<F>;

    fn invoke(self, pat: P<ast::Pat>) -> PatStructPathBuilder<F> {
        self.builder.with_field_pat(ast::FieldPat {
            ident: self.id,
            pat: pat,
            is_shorthand: false,
        })
    }
}

//////////////////////////////////////////////////////////////////////////////

pub struct PatExprBuilder<F>(PatBuilder<F>);

impl<F> Invoke<P<ast::Expr>> for PatExprBuilder<F>
    where F: Invoke<P<ast::Pat>>,
{
    type Result = F::Result;

    fn invoke(self, expr: P<ast::Expr>) -> F::Result {
        self.0.build_pat_(ast::Pat_::PatLit(expr))
    }
}

//////////////////////////////////////////////////////////////////////////////

pub struct PatTupleBuilder<F> {
    builder: PatBuilder<F>,
    pats: Vec<P<ast::Pat>>,
}

impl<F: Invoke<P<ast::Pat>>> PatTupleBuilder<F>
    where F: Invoke<P<ast::Pat>>
{
    pub fn with_pats<I>(mut self, iter: I) -> Self
        where I: IntoIterator<Item=P<ast::Pat>>,
    {
        self.pats.extend(iter);
        self
    }

    pub fn with_pat(mut self, pat: P<ast::Pat>) -> Self {
        self.pats.push(pat);
        self
    }

    pub fn pat(self) -> PatBuilder<Self> {
        PatBuilder::with_callback(self)
    }

    pub fn build(self) -> F::Result {
        self.builder.build_pat_(ast::PatTup(self.pats))
    }
}

impl<F> Invoke<P<ast::Pat>> for PatTupleBuilder<F>
    where F: Invoke<P<ast::Pat>>
{
    type Result = PatTupleBuilder<F>;

    fn invoke(self, pat: P<ast::Pat>) -> Self {
        self.with_pat(pat)
    }
}
<|endoftext|>


<commit_before>use std::sync::Arc;
use std::path::{self, PathBuf};
use std::fs;
use std::fmt::{self, Debug};
use std::io::{self, Read, Write, Seek};

use {GameResult, GameError};

pub type Path = str;

pub trait VFile: Read + Write + Seek + Debug {}

impl<T> VFile for T where T: Read + Write + Seek + Debug {}

/// Options for opening files
///
/// We need our own version of this structure because the one in
/// std annoyingly doesn't let you get data out of it.
#[derive(Debug, Default)]
pub struct OpenOptions {
    read: bool,
    write: bool,
    create: bool,
    append: bool,
    truncate: bool,
}

impl OpenOptions {
    /// Create a new instance
    pub fn new() -> OpenOptions {
        Default::default()
    }

    /// Open for reading
    pub fn read(&mut self, read: bool) -> &mut OpenOptions {
        self.read = read;
        self
    }

    /// Open for writing
    pub fn write(&mut self, write: bool) -> &mut OpenOptions {
        self.write = write;
        self
    }

    /// Create the file if it does not exist yet
    pub fn create(&mut self, create: bool) -> &mut OpenOptions {
        self.create = create;
        self
    }

    /// Append at the end of the file
    pub fn append(&mut self, append: bool) -> &mut OpenOptions {
        self.append = append;
        self
    }

    /// Truncate the file to 0 bytes after opening
    pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
        self.truncate = truncate;
        self
    }

    fn to_fs_openoptions(&self) -> fs::OpenOptions {
        let mut opt = fs::OpenOptions::new();
        opt.read(self.read)
            .write(self.write)
            .create(self.create)
            .append(self.append)
            .truncate(self.truncate)
            .create(self.create);
        opt
    }
}

pub trait VFS: Debug {
    /// Open the file at this path with the given options
    fn open_with_options(&self, path: &Path, openOptions: &OpenOptions) -> GameResult<Box<VFile>>;
    /// Open the file at this path for reading
    fn open(&self, path: &Path) -> GameResult<Box<VFile>> {
        self.open_with_options(path, OpenOptions::new().read(true))
    }
    /// Open the file at this path for writing, truncating it if it exists already
    fn create(&self, path: &Path) -> GameResult<Box<VFile>> {
        self.open_with_options(path, OpenOptions::new().write(true).create(true).truncate(true))
    }
    /// Open the file at this path for appending, creating it if necessary
    fn append(&self, path: &Path) -> GameResult<Box<VFile>> {
        self.open_with_options(path, OpenOptions::new().write(true).create(true).append(true))
    }
    /// Create a directory at the location by this path
    fn mkdir(&self, path: &Path) -> GameResult<()>;

    /// Remove a file or an empty directory.
    fn rm(&self, path: &Path) -> GameResult<()>;

    /// Remove a file or directory and all its contents
    fn rmrf(&self, path: &Path) -> GameResult<()>;

    /// Check if the file exists
    fn exists(&self, path: &Path) -> bool;

    /// Get the file's metadata
    fn metadata(&self, path: &Path) -> GameResult<Box<VMetadata>>;

    /// Retrieve the path entries in this path
    fn read_dir(&self, path: &Path) -> GameResult<Box<Iterator<Item = GameResult<Box<Path>>>>>;
}

pub trait VMetadata {}

/// A VFS that points to a directory and uses it as the root of its
/// file heirarchy.
pub struct PhysicalFS {
    root: Arc<PathBuf>,
    readonly: bool,
}

pub struct PhysicalMetadata(fs::Metadata);

impl VMetadata for PhysicalMetadata {}

/// Helper function to turn a path::Component into an Option<String> iff the Component
/// is a normal portion.
///
/// Basically this is to help turn a canonicalized absolute path into a relative path.
fn component_filter(comp: path::Component) -> Option<String> {
    match comp {
        path::Component::Normal(osstr) => Some(osstr.to_string_lossy().into_owned()),
        _ => None
    }
}

impl PhysicalFS {
    fn new(root: &str, readonly: bool) -> Self {
        PhysicalFS {
            root: Arc::new(root.into()),
            readonly: readonly,
        }
    }

    /// Takes a given path (&str) and returns
    /// a new PathBuf containing the canonical
    /// absolute path you get when appending it
    /// to this filesystem's root.
    fn get_absolute(&self, p: &str) -> GameResult<PathBuf> {
        let pathbuf = PathBuf::from(p);
        let relative_path = pathbuf.components().filter_map(component_filter);
        let mut full_path = (*self.root).clone();
        full_path.extend(relative_path);
        full_path.canonicalize()?;
        if !full_path.starts_with(&*self.root) {
            panic!("Tried to create an AltPath that exits the AltrootFS's root dir");
        }
        Ok(full_path)
    }
}

impl Debug for PhysicalFS {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "<PhysicalFS root: {}>", self.root.display())
    }
}

impl VFS for PhysicalFS {
    /// Open the file at this path with the given options
    fn open_with_options(&self, path: &Path, openOptions: &OpenOptions) -> GameResult<Box<VFile>> {
        if self.readonly {
            if openOptions.write || openOptions.create || openOptions.append || openOptions.truncate {
                let msg = format!("Cannot alter file {:?} in root {:?}, filesystem read-only", path, self);
                return Err(GameError::FilesystemError(msg));
            }
        }
        let p = self.get_absolute(path)?;
        openOptions.to_fs_openoptions().open(p)
            .map(|x| Box::new(x) as Box<VFile>)
            .map_err(GameError::from)
    }

    /// Create a directory at the location by this path
    fn mkdir(&self, path: &Path) -> GameResult<()> {
        if self.readonly {
            return Err(GameError::FilesystemError("Tried to make directory {} but FS is read-only".to_string()));
        }
        let p = self.get_absolute(path)?;
        fs::DirBuilder::new()
            .recursive(true)
            .create(p)
            .map_err(GameError::from)
    }

    /// Remove a file
    fn rm(&self, path: &Path) -> GameResult<()> {
        if self.readonly {
            return Err(GameError::FilesystemError("Tried to remove file {} but FS is read-only".to_string()));
        }

        let p = self.get_absolute(path)?;
        if p.is_dir() {
            fs::remove_dir(p)
                .map_err(GameError::from)
        } else {
            fs::remove_file(p)
                .map_err(GameError::from)
        }
    }

    /// Remove a file or directory and all its contents
    fn rmrf(&self, path: &Path) -> GameResult<()> {
        if self.readonly {
            return Err(GameError::FilesystemError("Tried to remove file/dir {} but FS is read-only".to_string()));
        }

        let p = self.get_absolute(path)?;
        if p.is_dir() {
            fs::remove_dir_all(p)
                .map_err(GameError::from)
        } else {
            fs::remove_file(p)
                .map_err(GameError::from)
        }
    }

    /// Check if the file exists
    fn exists(&self, path: &Path) -> bool {
        match self.get_absolute(path) {
            Ok(p) => p.exists(),
            _ => false
        }
    }

    /// Get the file's metadata
    fn metadata(&self, path: &Path) -> GameResult<Box<VMetadata>> {
        let p = self.get_absolute(path)?;
        p.metadata()
            .map(|m| Box::new(PhysicalMetadata(m)) as Box<VMetadata>)
            .map_err(GameError::from)
    }

    /// Retrieve the path entries in this path
    fn read_dir(&self, path: &Path) -> GameResult<Box<Iterator<Item = GameResult<Box<Path>>>>> {
        // TODO
        Err(GameError::FilesystemError("Foo!".to_string()))
    }
}

/// A structure that joins several VFS's together in order.
/// VecDeque instead of Vec?
#[derive(Debug)]
struct OverlayFS {
    roots: Vec<Box<VFS>>,
}

impl OverlayFS {
    fn new() -> Self {
        Self {
            roots: Vec::new()
        }
    }

    /// Adds a new VFS to the end of the list.
    fn push(&mut self, fs: Box<VFS>) {
        &self.roots.push(fs);
    }
}

impl VFS for OverlayFS {
    /// Open the file at this path with the given options
    fn open_with_options(&self, path: &Path, openOptions: &OpenOptions) -> GameResult<Box<VFile>> {
        for vfs in &self.roots {
            match vfs.open_with_options(path, openOptions) {
                Err(_) => (),
                f => return f,
            }
        }
        Err(GameError::FilesystemError(format!("File {} not found", path)))
    }

    /// Create a directory at the location by this path
    fn mkdir(&self, path: &Path) -> GameResult<()> {
        for vfs in &self.roots {
            match vfs.mkdir(path) {
                Err(_) => (),
                f => return f,
            }
        }
        Err(GameError::FilesystemError(format!("Could not find anywhere writeable to make dir {}", path)))
    }

    /// Remove a file
    fn rm(&self, path: &Path) -> GameResult<()> {
        for vfs in &self.roots {
            match vfs.rm(path) {
                Err(_) => (),
                f => return f,
            }
        }
        Err(GameError::FilesystemError(format!("Could not remove file {}", path)))
    }

    /// Remove a file or directory and all its contents
    fn rmrf(&self, path: &Path) -> GameResult<()> {
        for vfs in &self.roots {
            match vfs.rmrf(path) {
                Err(_) => (),
                f => return f,
            }
        }
        Err(GameError::FilesystemError(format!("Could not remove file/dir {}", path)))
    }

    /// Check if the file exists
    fn exists(&self, path: &Path) -> bool {
        for vfs in &self.roots {
            if vfs.exists(path) {
                return true;
            }
        }

        false
    }

    /// Get the file's metadata
    fn metadata(&self, path: &Path) -> GameResult<Box<VMetadata>> {
        for vfs in &self.roots {
            match vfs.metadata(path) {
                Err(_) => (),
                f => return f,
            }
        }
        Err(GameError::FilesystemError(format!("Could not remove file/dir {}", path)))
    }

    /// Retrieve the path entries in this path
    fn read_dir(&self, path: &Path) -> GameResult<Box<Iterator<Item = GameResult<Box<Path>>>>> {
        // TODO: This is tricky 'cause we have to actually merge iterators together...
        Err(GameError::FilesystemError("Foo!".to_string()))
    }
}

#[cfg(test)]
mod tests {
    use std::io::{self, BufRead};
    use super::*;
    #[test]
    fn test_read() {
        let fs = PhysicalFS::new(env!("CARGO_MANIFEST_DIR"), true);
        let f = fs.open("Cargo.toml").unwrap();
        let mut bf = io::BufReader::new(f);
        let mut s = String::new();
        bf.read_line(&mut s).unwrap();
        assert_eq!(&s, "[package]\n");
    }

    #[test]
    fn test_read_overlay() {
        let fs1 = PhysicalFS::new(env!("CARGO_MANIFEST_DIR"), true);
        let mut f2path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        f2path.push("src");
        let fs2 = PhysicalFS::new(f2path.to_str().unwrap(), true);
        let mut ofs = OverlayFS::new();
        ofs.push(Box::new(fs1));
        ofs.push(Box::new(fs2));

        assert!(ofs.exists("Cargo.toml"));
        assert!(ofs.exists("lib.rs"));
        assert!(!ofs.exists("foobaz.rs"));
    }

}
<commit_msg>Making path mongling for vfs less ad-hoc.<commit_after>use std::sync::Arc;
use std::path::{self, PathBuf};
use std::fs;
use std::fmt::{self, Debug};
use std::io::{self, Read, Write, Seek};

use {GameResult, GameError};

pub type Path = str;

pub trait VFile: Read + Write + Seek + Debug {}

impl<T> VFile for T where T: Read + Write + Seek + Debug {}

/// Options for opening files
///
/// We need our own version of this structure because the one in
/// std annoyingly doesn't let you get data out of it.
#[derive(Debug, Default)]
pub struct OpenOptions {
    read: bool,
    write: bool,
    create: bool,
    append: bool,
    truncate: bool,
}

impl OpenOptions {
    /// Create a new instance
    pub fn new() -> OpenOptions {
        Default::default()
    }

    /// Open for reading
    pub fn read(&mut self, read: bool) -> &mut OpenOptions {
        self.read = read;
        self
    }

    /// Open for writing
    pub fn write(&mut self, write: bool) -> &mut OpenOptions {
        self.write = write;
        self
    }

    /// Create the file if it does not exist yet
    pub fn create(&mut self, create: bool) -> &mut OpenOptions {
        self.create = create;
        self
    }

    /// Append at the end of the file
    pub fn append(&mut self, append: bool) -> &mut OpenOptions {
        self.append = append;
        self
    }

    /// Truncate the file to 0 bytes after opening
    pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
        self.truncate = truncate;
        self
    }

    fn to_fs_openoptions(&self) -> fs::OpenOptions {
        let mut opt = fs::OpenOptions::new();
        opt.read(self.read)
            .write(self.write)
            .create(self.create)
            .append(self.append)
            .truncate(self.truncate)
            .create(self.create);
        opt
    }
}

pub trait VFS: Debug {
    /// Open the file at this path with the given options
    fn open_with_options(&self, path: &Path, open_options: &OpenOptions) -> GameResult<Box<VFile>>;
    /// Open the file at this path for reading
    fn open(&self, path: &Path) -> GameResult<Box<VFile>> {
        self.open_with_options(path, OpenOptions::new().read(true))
    }
    /// Open the file at this path for writing, truncating it if it exists already
    fn create(&self, path: &Path) -> GameResult<Box<VFile>> {
        self.open_with_options(path, OpenOptions::new().write(true).create(true).truncate(true))
    }
    /// Open the file at this path for appending, creating it if necessary
    fn append(&self, path: &Path) -> GameResult<Box<VFile>> {
        self.open_with_options(path, OpenOptions::new().write(true).create(true).append(true))
    }
    /// Create a directory at the location by this path
    fn mkdir(&self, path: &Path) -> GameResult<()>;

    /// Remove a file or an empty directory.
    fn rm(&self, path: &Path) -> GameResult<()>;

    /// Remove a file or directory and all its contents
    fn rmrf(&self, path: &Path) -> GameResult<()>;

    /// Check if the file exists
    fn exists(&self, path: &Path) -> bool;

    /// Get the file's metadata
    fn metadata(&self, path: &Path) -> GameResult<Box<VMetadata>>;

    /// Retrieve the path entries in this path
    fn read_dir(&self, path: &Path) -> GameResult<Box<Iterator<Item = GameResult<Box<Path>>>>>;
}

pub trait VMetadata {}

/// A VFS that points to a directory and uses it as the root of its
/// file heirarchy.
pub struct PhysicalFS {
    root: Arc<PathBuf>,
    readonly: bool,
}

pub struct PhysicalMetadata(fs::Metadata);

impl VMetadata for PhysicalMetadata {}

/// Helper function to turn a path::Component into an Option<String> iff the Component
/// is a normal portion.
///
/// Basically this is to help turn a canonicalized absolute path into a relative path.
fn component_filter(comp: path::Component) -> Option<String> {
    match comp {
        path::Component::Normal(osstr) => Some(osstr.to_string_lossy().into_owned()),
        _ => None
    }
}

/// This takes a path and returns whether not it is what we want.
///
/// What we want is an absolute path with no `..`'s in it, so, something
/// like "/foo" or "/foo/bar.txt".  This means a path with components
/// starting with a RootDir, and zero or more Normal components.
fn path_is_valid(path: &path::Path) -> bool {
    let mut c = path.components();
    match c.next() {
        Some(path::Component::RootDir) => (),
        _ => return false,
    }

    fn is_normal_component(comp: path::Component) -> bool {
        match comp {
            path::Component::Normal(_) => true,
            _ => false,
        }
    }

    c.all(is_normal_component)
}

impl PhysicalFS {
    fn new(root: &str, readonly: bool) -> Self {
        PhysicalFS {
            root: Arc::new(root.into()),
            readonly: readonly,
        }
    }

    /// Takes a given path (&str) and returns
    /// a new PathBuf containing the canonical
    /// absolute path you get when appending it
    /// to this filesystem's root.
    fn get_absolute(&self, p: &str) -> GameResult<PathBuf> {
        let pathbuf = PathBuf::from(p);
        let relative_path = pathbuf.components().filter_map(component_filter);
        let mut full_path = (*self.root).clone();
        full_path.extend(relative_path);
        //
        //full_path.canonicalize()?;
        if !full_path.starts_with(&*self.root) {
            panic!("Tried to create an AltPath that exits the AltrootFS's root dir");
        }
        Ok(full_path)
    }
}

impl Debug for PhysicalFS {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "<PhysicalFS root: {}>", self.root.display())
    }
}

impl VFS for PhysicalFS {
    /// Open the file at this path with the given options
    fn open_with_options(&self, path: &Path, open_options: &OpenOptions) -> GameResult<Box<VFile>> {
        if self.readonly {
            if open_options.write || open_options.create || open_options.append || open_options.truncate {
                let msg = format!("Cannot alter file {:?} in root {:?}, filesystem read-only", path, self);
                return Err(GameError::FilesystemError(msg));
            }
        }
        let p = self.get_absolute(path)?;
        open_options.to_fs_openoptions().open(p)
            .map(|x| Box::new(x) as Box<VFile>)
            .map_err(GameError::from)
    }

    /// Create a directory at the location by this path
    fn mkdir(&self, path: &Path) -> GameResult<()> {
        if self.readonly {
            return Err(GameError::FilesystemError("Tried to make directory {} but FS is read-only".to_string()));
        }
        let p = self.get_absolute(path)?;
        println!("Creating {:?}", p);
        panic!("fdsfdsafs");
        fs::DirBuilder::new()
            .recursive(true)
            .create(p)
            .map_err(GameError::from)
    }

    /// Remove a file
    fn rm(&self, path: &Path) -> GameResult<()> {
        if self.readonly {
            return Err(GameError::FilesystemError("Tried to remove file {} but FS is read-only".to_string()));
        }

        let p = self.get_absolute(path)?;
        if p.is_dir() {
            fs::remove_dir(p)
                .map_err(GameError::from)
        } else {
            fs::remove_file(p)
                .map_err(GameError::from)
        }
    }

    /// Remove a file or directory and all its contents
    fn rmrf(&self, path: &Path) -> GameResult<()> {
        if self.readonly {
            return Err(GameError::FilesystemError("Tried to remove file/dir {} but FS is read-only".to_string()));
        }

        let p = self.get_absolute(path)?;
        if p.is_dir() {
            fs::remove_dir_all(p)
                .map_err(GameError::from)
        } else {
            fs::remove_file(p)
                .map_err(GameError::from)
        }
    }

    /// Check if the file exists
    fn exists(&self, path: &Path) -> bool {
        match self.get_absolute(path) {
            Ok(p) => p.exists(),
            _ => false
        }
    }

    /// Get the file's metadata
    fn metadata(&self, path: &Path) -> GameResult<Box<VMetadata>> {
        let p = self.get_absolute(path)?;
        p.metadata()
            .map(|m| Box::new(PhysicalMetadata(m)) as Box<VMetadata>)
            .map_err(GameError::from)
    }

    /// Retrieve the path entries in this path
    fn read_dir(&self, path: &Path) -> GameResult<Box<Iterator<Item = GameResult<Box<Path>>>>> {
        // TODO
        Err(GameError::FilesystemError("Foo!".to_string()))
    }
}

/// A structure that joins several VFS's together in order.
/// VecDeque instead of Vec?
#[derive(Debug)]
struct OverlayFS {
    roots: Vec<Box<VFS>>,
}

impl OverlayFS {
    fn new() -> Self {
        Self {
            roots: Vec::new()
        }
    }

    /// Adds a new VFS to the end of the list.
    fn push(&mut self, fs: Box<VFS>) {
        &self.roots.push(fs);
    }
}

impl VFS for OverlayFS {
    /// Open the file at this path with the given options
    fn open_with_options(&self, path: &Path, open_options: &OpenOptions) -> GameResult<Box<VFile>> {
        for vfs in &self.roots {
            match vfs.open_with_options(path, open_options) {
                Err(_) => (),
                f => return f,
            }
        }
        Err(GameError::FilesystemError(format!("File {} not found", path)))
    }

    /// Create a directory at the location by this path
    fn mkdir(&self, path: &Path) -> GameResult<()> {
        for vfs in &self.roots {
            match vfs.mkdir(path) {
                Err(_) => (),
                f => return f,
            }
        }
        Err(GameError::FilesystemError(format!("Could not find anywhere writeable to make dir {}", path)))
    }

    /// Remove a file
    fn rm(&self, path: &Path) -> GameResult<()> {
        for vfs in &self.roots {
            match vfs.rm(path) {
                Err(_) => (),
                f => return f,
            }
        }
        Err(GameError::FilesystemError(format!("Could not remove file {}", path)))
    }

    /// Remove a file or directory and all its contents
    fn rmrf(&self, path: &Path) -> GameResult<()> {
        for vfs in &self.roots {
            match vfs.rmrf(path) {
                Err(_) => (),
                f => return f,
            }
        }
        Err(GameError::FilesystemError(format!("Could not remove file/dir {}", path)))
    }

    /// Check if the file exists
    fn exists(&self, path: &Path) -> bool {
        for vfs in &self.roots {
            if vfs.exists(path) {
                return true;
            }
        }

        false
    }

    /// Get the file's metadata
    fn metadata(&self, path: &Path) -> GameResult<Box<VMetadata>> {
        for vfs in &self.roots {
            match vfs.metadata(path) {
                Err(_) => (),
                f => return f,
            }
        }
        Err(GameError::FilesystemError(format!("Could not remove file/dir {}", path)))
    }

    /// Retrieve the path entries in this path
    fn read_dir(&self, path: &Path) -> GameResult<Box<Iterator<Item = GameResult<Box<Path>>>>> {
        // TODO: This is tricky 'cause we have to actually merge iterators together...
        Err(GameError::FilesystemError("Foo!".to_string()))
    }
}

#[cfg(test)]
mod tests {
    use std::io::{self, BufRead};
    use super::*;

    fn test_path_filtering() {
        let p = path::Path::new("/foo");
        assert!(path_is_valid(p));

        let p = path::Path::new("/foo/");
        assert!(path_is_valid(p));

        let p = path::Path::new("/foo/bar.txt");
        assert!(path_is_valid(p));

        let p = path::Path::new("/");
        assert!(path_is_valid(p));

        let p = path::Path::new("../foo");
        assert!(!path_is_valid(p));

        let p = path::Path::new("foo");
        assert!(!path_is_valid(p));

        let p = path::Path::new("/foo/../../");
        assert!(!path_is_valid(p));

        let p = path::Path::new("/foo/../bop");
        assert!(!path_is_valid(p));

        let p = path::Path::new("/../bar");
        assert!(!path_is_valid(p));

        let p = path::Path::new("");
        assert!(!path_is_valid(p));
    }

    #[test]
    fn test_read() {
        let fs = PhysicalFS::new(env!("CARGO_MANIFEST_DIR"), true);
        let f = fs.open("Cargo.toml").unwrap();
        let mut bf = io::BufReader::new(f);
        let mut s = String::new();
        bf.read_line(&mut s).unwrap();
        assert_eq!(&s, "[package]\n");
    }

    #[test]
    fn test_read_overlay() {
        let fs1 = PhysicalFS::new(env!("CARGO_MANIFEST_DIR"), true);
        let mut f2path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        f2path.push("src");
        let fs2 = PhysicalFS::new(f2path.to_str().unwrap(), true);
        let mut ofs = OverlayFS::new();
        ofs.push(Box::new(fs1));
        ofs.push(Box::new(fs2));

        assert!(ofs.exists("Cargo.toml"));
        assert!(ofs.exists("lib.rs"));
        assert!(!ofs.exists("foobaz.rs"));
    }

    #[test]
    fn test_physical_all() {
        let fs = PhysicalFS::new(env!("CARGO_MANIFEST_DIR"), false);
        let testdir = "testdir";
        let f1 = "testdir/file1.txt";
        let f2 = "testdir/file2.txt";

        // Create and delete test dir
        //assert!(!fs.exists(testdir));
        //fs.mkdir(testdir).unwrap();
        //assert!(fs.exists(testdir));
        //fs.rm(testdir).unwrap();
        //assert!(!fs.exists(testdir));

        //fs.create(testdir);
    }

}
<|endoftext|>