<commit_before>use std::collections::HashMap;
use std::convert::TryFrom;
use std::fs::{remove_file, File, OpenOptions};
use std::io::{Read, Write};

use rustc::ty::layout::Size;

use crate::stacked_borrows::Tag;
use crate::*;

#[derive(Debug)]
pub struct FileHandle {
    file: File,
}

pub struct FileHandler {
    handles: HashMap<i32, FileHandle>,
    low: i32,
}

impl Default for FileHandler {
    fn default() -> Self {
        FileHandler {
            handles: Default::default(),
            // 0, 1 and 2 are reserved for stdin, stdout and stderr.
            low: 3,
        }
    }
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn open(
        &mut self,
        path_op: OpTy<'tcx, Tag>,
        flag_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("open")?;

        let flag = this.read_scalar(flag_op)?.to_i32()?;

        let mut options = OpenOptions::new();

        let o_rdonly = this.eval_libc_i32("O_RDONLY")?;
        let o_wronly = this.eval_libc_i32("O_WRONLY")?;
        let o_rdwr = this.eval_libc_i32("O_RDWR")?;
        // The first two bits of the flag correspond to the access mode in linux, macOS and
        // windows. We need to check that in fact the access mode flags for the current platform
        // only use these two bits, otherwise we are in an unsupported platform and should error.
        if (o_rdonly | o_wronly | o_rdwr) & !0b11 != 0 {
            throw_unsup_format!("Access mode flags on this platform are unsupported");
        }
        // Now we check the access mode
        let access_mode = flag & 0b11;

        if access_mode == o_rdonly {
            options.read(true);
        } else if access_mode == o_wronly {
            options.write(true);
        } else if access_mode == o_rdwr {
            options.read(true).write(true);
        } else {
            throw_unsup_format!("Unsupported access mode {:#x}", access_mode);
        }
        // We need to check that there aren't unsupported options in `flag`. For this we try to
        // reproduce the content of `flag` in the `mirror` variable using only the supported
        // options.
        let mut mirror = access_mode;

        let o_append = this.eval_libc_i32("O_APPEND")?;
        if flag & o_append != 0 {
            options.append(true);
            mirror |= o_append;
        }
        let o_trunc = this.eval_libc_i32("O_TRUNC")?;
        if flag & o_trunc != 0 {
            options.truncate(true);
            mirror |= o_trunc;
        }
        let o_creat = this.eval_libc_i32("O_CREAT")?;
        if flag & o_creat != 0 {
            options.create(true);
            mirror |= o_creat;
        }
        let o_cloexec = this.eval_libc_i32("O_CLOEXEC")?;
        if flag & o_cloexec != 0 {
            // We do not need to do anything for this flag because `std` already sets it.
            // (Technically we do not support *not* setting this flag, but we ignore that.)
            mirror |= o_cloexec;
        }
        // If `flag` is not equal to `mirror`, there is an unsupported option enabled in `flag`,
        // then we throw an error.
        if flag != mirror {
            throw_unsup_format!("unsupported flags {:#x}", flag & !mirror);
        }

        let path = this.read_os_string_from_c_string(this.read_scalar(path_op)?.not_undef()?)?;

        let fd = options.open(path).map(|file| {
            let mut fh = &mut this.machine.file_handler;
            fh.low += 1;
            fh.handles.insert(fh.low, FileHandle { file }).unwrap_none();
            fh.low
        });

        this.try_unwrap_io_result(fd)
    }

    fn fcntl(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        cmd_op: OpTy<'tcx, Tag>,
        _arg1_op: Option<OpTy<'tcx, Tag>>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("fcntl")?;

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let cmd = this.read_scalar(cmd_op)?.to_i32()?;
        // We only support getting the flags for a descriptor.
        if cmd == this.eval_libc_i32("F_GETFD")? {
            // Currently this is the only flag that `F_GETFD` returns. It is OK to just return the
            // `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
            // always sets this flag when opening a file. However we still need to check that the
            // file itself is open.
            if this.machine.file_handler.handles.contains_key(&fd) {
                Ok(this.eval_libc_i32("FD_CLOEXEC")?)
            } else {
                this.handle_not_found()
            }
        } else {
            throw_unsup_format!("The {:#x} command is not supported for `fcntl`)", cmd);
        }
    }

    fn close(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("close")?;

        let fd = this.read_scalar(fd_op)?.to_i32()?;

        if let Some(handle) = this.machine.file_handler.handles.remove(&fd) {
            // `File::sync_all` does the checks that are done when closing a file. We do this to
            // to handle possible errors correctly.
            let result = this.try_unwrap_io_result(handle.file.sync_all().map(|_| 0i32));
            // Now we actually close the file.
            drop(handle);
            // And return the result.
            result
        } else {
            this.handle_not_found()
        }
    }

    fn read(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        this.check_no_isolation("read")?;

        let ptr_size = this.pointer_size().bits();

        // We cap the number of read bytes to the largest value that we are able to fit in both the
        // host's and target's `isize`.
        let count = this
            .read_scalar(count_op)?
            .to_machine_usize(&*this.tcx)?
            .min((1 << (ptr_size - 1)) - 1) // max value of target `isize`
            .min(isize::max_value() as u64);

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.read_scalar(buf_op)?.not_undef()?;

        if let Some(handle) = this.machine.file_handler.handles.get_mut(&fd) {
            // This can never fail because `count` was capped to be smaller than
            // `isize::max_value()`.
            let count = isize::try_from(count).unwrap();
            // We want to read at most `count` bytes. We are sure that `count` is not negative
            // because it was a target's `usize`. Also we are sure that its smaller than
            // `usize::max_value()` because it is a host's `isize`.
            let mut bytes = vec![0; count as usize];
            let result = handle
                .file
                .read(&mut bytes)
                // `File::read` never returns a value larger than `count`, so this cannot fail.
                .map(|c| i64::try_from(c).unwrap());

            match result {
                Ok(read_bytes) => {
                    // If reading to `bytes` did not fail, we write those bytes to the buffer.
                    this.memory.write_bytes(buf, bytes)?;
                    Ok(read_bytes)
                }
                Err(e) => {
                    this.set_last_error_from_io_error(e)?;
                    Ok(-1)
                }
            }
        } else {
            this.handle_not_found()
        }
    }

    fn write(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        this.check_no_isolation("write")?;

        let ptr_size = this.pointer_size().bits();

        // We cap the number of read bytes to the largest value that we are able to fit in both the
        // host's and target's `isize`.
        let count = this
            .read_scalar(count_op)?
            .to_machine_usize(&*this.tcx)?
            .min((1 << (ptr_size - 1)) - 1) // max value of target `isize`
            .min(isize::max_value() as u64);

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.read_scalar(buf_op)?.not_undef()?;

        if let Some(handle) = this.machine.file_handler.handles.get_mut(&fd) {
            let bytes = this.memory.read_bytes(buf, Size::from_bytes(count))?;
            let result = handle.file.write(&bytes).map(|c| i64::try_from(c).unwrap());
            this.try_unwrap_io_result(result)
        } else {
            this.handle_not_found()
        }
    }

    fn unlink(&mut self, path_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("unlink")?;

        let path = this.read_os_string_from_c_string(this.read_scalar(path_op)?.not_undef()?)?;

        let result = remove_file(path).map(|_| 0);

        this.try_unwrap_io_result(result)
    }

    /// Function used when a handle is not found inside `FileHandler`. It returns `Ok(-1)`and sets
    /// the last OS error to `libc::EBADF` (invalid file descriptor). This function uses
    /// `T: From<i32>` instead of `i32` directly because some fs functions return different integer
    /// types (like `read`, that returns an `i64`).
    fn handle_not_found<T: From<i32>>(&mut self) -> InterpResult<'tcx, T> {
        let this = self.eval_context_mut();
        let ebadf = this.eval_libc("EBADF")?;
        this.set_last_error(ebadf)?;
        Ok((-1).into())
    }
}
<commit_msg>Auto merge of #1056 - RalfJung:max, r=RalfJung<commit_after>use std::collections::HashMap;
use std::convert::TryFrom;
use std::fs::{remove_file, File, OpenOptions};
use std::io::{Read, Write};

use rustc::ty::layout::{Size, Align};

use crate::stacked_borrows::Tag;
use crate::*;

#[derive(Debug)]
pub struct FileHandle {
    file: File,
}

pub struct FileHandler {
    handles: HashMap<i32, FileHandle>,
    low: i32,
}

impl Default for FileHandler {
    fn default() -> Self {
        FileHandler {
            handles: Default::default(),
            // 0, 1 and 2 are reserved for stdin, stdout and stderr.
            low: 3,
        }
    }
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn open(
        &mut self,
        path_op: OpTy<'tcx, Tag>,
        flag_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("open")?;

        let flag = this.read_scalar(flag_op)?.to_i32()?;

        let mut options = OpenOptions::new();

        let o_rdonly = this.eval_libc_i32("O_RDONLY")?;
        let o_wronly = this.eval_libc_i32("O_WRONLY")?;
        let o_rdwr = this.eval_libc_i32("O_RDWR")?;
        // The first two bits of the flag correspond to the access mode in linux, macOS and
        // windows. We need to check that in fact the access mode flags for the current platform
        // only use these two bits, otherwise we are in an unsupported platform and should error.
        if (o_rdonly | o_wronly | o_rdwr) & !0b11 != 0 {
            throw_unsup_format!("Access mode flags on this platform are unsupported");
        }
        // Now we check the access mode
        let access_mode = flag & 0b11;

        if access_mode == o_rdonly {
            options.read(true);
        } else if access_mode == o_wronly {
            options.write(true);
        } else if access_mode == o_rdwr {
            options.read(true).write(true);
        } else {
            throw_unsup_format!("Unsupported access mode {:#x}", access_mode);
        }
        // We need to check that there aren't unsupported options in `flag`. For this we try to
        // reproduce the content of `flag` in the `mirror` variable using only the supported
        // options.
        let mut mirror = access_mode;

        let o_append = this.eval_libc_i32("O_APPEND")?;
        if flag & o_append != 0 {
            options.append(true);
            mirror |= o_append;
        }
        let o_trunc = this.eval_libc_i32("O_TRUNC")?;
        if flag & o_trunc != 0 {
            options.truncate(true);
            mirror |= o_trunc;
        }
        let o_creat = this.eval_libc_i32("O_CREAT")?;
        if flag & o_creat != 0 {
            options.create(true);
            mirror |= o_creat;
        }
        let o_cloexec = this.eval_libc_i32("O_CLOEXEC")?;
        if flag & o_cloexec != 0 {
            // We do not need to do anything for this flag because `std` already sets it.
            // (Technically we do not support *not* setting this flag, but we ignore that.)
            mirror |= o_cloexec;
        }
        // If `flag` is not equal to `mirror`, there is an unsupported option enabled in `flag`,
        // then we throw an error.
        if flag != mirror {
            throw_unsup_format!("unsupported flags {:#x}", flag & !mirror);
        }

        let path = this.read_os_string_from_c_string(this.read_scalar(path_op)?.not_undef()?)?;

        let fd = options.open(path).map(|file| {
            let mut fh = &mut this.machine.file_handler;
            fh.low += 1;
            fh.handles.insert(fh.low, FileHandle { file }).unwrap_none();
            fh.low
        });

        this.try_unwrap_io_result(fd)
    }

    fn fcntl(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        cmd_op: OpTy<'tcx, Tag>,
        _arg1_op: Option<OpTy<'tcx, Tag>>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("fcntl")?;

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let cmd = this.read_scalar(cmd_op)?.to_i32()?;
        // We only support getting the flags for a descriptor.
        if cmd == this.eval_libc_i32("F_GETFD")? {
            // Currently this is the only flag that `F_GETFD` returns. It is OK to just return the
            // `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
            // always sets this flag when opening a file. However we still need to check that the
            // file itself is open.
            if this.machine.file_handler.handles.contains_key(&fd) {
                Ok(this.eval_libc_i32("FD_CLOEXEC")?)
            } else {
                this.handle_not_found()
            }
        } else {
            throw_unsup_format!("The {:#x} command is not supported for `fcntl`)", cmd);
        }
    }

    fn close(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("close")?;

        let fd = this.read_scalar(fd_op)?.to_i32()?;

        if let Some(handle) = this.machine.file_handler.handles.remove(&fd) {
            // `File::sync_all` does the checks that are done when closing a file. We do this to
            // to handle possible errors correctly.
            let result = this.try_unwrap_io_result(handle.file.sync_all().map(|_| 0i32));
            // Now we actually close the file.
            drop(handle);
            // And return the result.
            result
        } else {
            this.handle_not_found()
        }
    }

    fn read(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        this.check_no_isolation("read")?;

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.read_scalar(buf_op)?.not_undef()?;
        let count = this
            .read_scalar(count_op)?
            .to_machine_usize(&*this.tcx)?;

        // Check that the *entire* buffer is actually valid memory.
        this.memory.check_ptr_access(buf, Size::from_bytes(count), Align::from_bytes(1).unwrap())?;

        // We cap the number of read bytes to the largest value that we are able to fit in both the
        // host's and target's `isize`. This saves us from having to handle overflows later.
        let count = count
            .min(this.isize_max() as u64)
            .min(isize::max_value() as u64);

        if let Some(handle) = this.machine.file_handler.handles.get_mut(&fd) {
            // This can never fail because `count` was capped to be smaller than
            // `isize::max_value()`.
            let count = isize::try_from(count).unwrap();
            // We want to read at most `count` bytes. We are sure that `count` is not negative
            // because it was a target's `usize`. Also we are sure that its smaller than
            // `usize::max_value()` because it is a host's `isize`.
            let mut bytes = vec![0; count as usize];
            let result = handle
                .file
                .read(&mut bytes)
                // `File::read` never returns a value larger than `count`, so this cannot fail.
                .map(|c| i64::try_from(c).unwrap());

            match result {
                Ok(read_bytes) => {
                    // If reading to `bytes` did not fail, we write those bytes to the buffer.
                    this.memory.write_bytes(buf, bytes)?;
                    Ok(read_bytes)
                }
                Err(e) => {
                    this.set_last_error_from_io_error(e)?;
                    Ok(-1)
                }
            }
        } else {
            this.handle_not_found()
        }
    }

    fn write(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        this.check_no_isolation("write")?;

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.read_scalar(buf_op)?.not_undef()?;
        let count = this
            .read_scalar(count_op)?
            .to_machine_usize(&*this.tcx)?;

        // Check that the *entire* buffer is actually valid memory.
        this.memory.check_ptr_access(buf, Size::from_bytes(count), Align::from_bytes(1).unwrap())?;

        // We cap the number of written bytes to the largest value that we are able to fit in both the
        // host's and target's `isize`. This saves us from having to handle overflows later.
        let count = count
            .min(this.isize_max() as u64)
            .min(isize::max_value() as u64);

        if let Some(handle) = this.machine.file_handler.handles.get_mut(&fd) {
            let bytes = this.memory.read_bytes(buf, Size::from_bytes(count))?;
            let result = handle.file.write(&bytes).map(|c| i64::try_from(c).unwrap());
            this.try_unwrap_io_result(result)
        } else {
            this.handle_not_found()
        }
    }

    fn unlink(&mut self, path_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("unlink")?;

        let path = this.read_os_string_from_c_string(this.read_scalar(path_op)?.not_undef()?)?;

        let result = remove_file(path).map(|_| 0);

        this.try_unwrap_io_result(result)
    }

    /// Function used when a handle is not found inside `FileHandler`. It returns `Ok(-1)`and sets
    /// the last OS error to `libc::EBADF` (invalid file descriptor). This function uses
    /// `T: From<i32>` instead of `i32` directly because some fs functions return different integer
    /// types (like `read`, that returns an `i64`).
    fn handle_not_found<T: From<i32>>(&mut self) -> InterpResult<'tcx, T> {
        let this = self.eval_context_mut();
        let ebadf = this.eval_libc("EBADF")?;
        this.set_last_error(ebadf)?;
        Ok((-1).into())
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::any::Any;
use std::rc::Rc;
use std::cell::RefCell;

use alga::general::Real;
use na;
use ncollide::utils::data::hash_map::{HashMap, Entry};
use ncollide::utils::data::hash::UintTWHash;
use ncollide::shape::{Shape, ShapeHandle};
use ncollide::narrow_phase::ProximityHandler;
use ncollide::query::Proximity;
use math::{Point, Isometry};
use object::{RigidBodyHandle, SensorCollisionGroups, WorldObject};
use world::{WorldCollisionObject, RigidBodies, Sensors};

/// A shared, mutable, sensor.
pub type SensorHandle<N> = Rc<RefCell<Sensor<N>>>;

/// An object capable of detecting interferances with other entities without interacting with them.
pub struct Sensor<N: Real> {
    parent:              Option<RigidBodyHandle<N>>,
    relative_position:   Isometry<N>,
    shape:               ShapeHandle<Point<N>, Isometry<N>>,
    margin:              N,
    collision_groups:    SensorCollisionGroups,
    parent_prox:         bool,
    user_data:           Option<Box<Any>>,
    interfering_bodies:  Option<HashMap<usize, RigidBodyHandle<N>, UintTWHash>>,
    interfering_sensors: Option<HashMap<usize, SensorHandle<N>, UintTWHash>>,
    #[doc(hidden)]
    pub did_move_locally:    bool
}

impl<N: Real> Sensor<N> {
    /// Creates a new sensor.
    ///
    /// A sensor may either be attached to the rigid body `parent`, or be attached to the ground.
    /// If it has a parent, it will have a position relative to it. If the parent moves, the sensor
    /// will automatically move as well. By default, a sensor does not trigger any proximity event
    /// when it intersects its own parent.
    ///
    /// A sensor has a default margin equal to zero.
    pub fn new<G>(shape: G, parent: Option<RigidBodyHandle<N>>) -> Sensor<N>
        where G: Send + Sync + Shape<Point<N>, Isometry<N>> {
        Sensor::new_with_shared_shape(ShapeHandle::new(shape), parent)
    }

    /// Creates a new senson with a given shared shape.
    pub fn new_with_shared_shape(shape:  ShapeHandle<Point<N>, Isometry<N>>,
                                 parent: Option<RigidBodyHandle<N>>)
                                 -> Sensor<N> {
        Sensor {
            parent:              parent,
            relative_position:   na::one(),
            did_move_locally:    true,
            shape:               shape,
            margin:              na::zero(),
            collision_groups:    SensorCollisionGroups::new(),
            parent_prox:         false,
            user_data:           None,
            interfering_bodies:  None,
            interfering_sensors: None
        }
    }

    /// Reference to user-defined data attached to this sensor.
    #[inline]
    pub fn user_data(&self) -> Option<&Box<Any>> {
        self.user_data.as_ref()
    }

    /// Mutable reference to user-defined data attached to this sensor.
    #[inline]
    pub fn user_data_mut(&mut self) -> Option<&mut Box<Any>> {
        self.user_data.as_mut()
    }

    /// Attach some user-defined data to this sensor and return the old one.
    pub fn set_user_data(&mut self, user_data: Option<Box<Any>>) -> Option<Box<Any>> {
        mem::replace(&mut self.user_data, user_data)
    }

    /// Enables the collection of all body handles that intersect this sensor.
    pub fn enable_interfering_bodies_collection(&mut self) {
        if self.interfering_bodies.is_none() {
            self.interfering_bodies = Some(HashMap::new(UintTWHash::new()));
        }
    }

    /// Enables the collection of all body handles that intersect this sensor.
    pub fn enable_interfering_sensors_collection(&mut self) {
        if self.interfering_sensors.is_none() {
            self.interfering_sensors= Some(HashMap::new(UintTWHash::new()));
        }
    }

    /// Disables the collection of all body handles that intersect this sensor.
    pub fn disable_interfering_bodies_collection(&mut self) {
        self.interfering_bodies = None;
    }

    /// Disables the collection of all body handles that intersect this sensor.
    pub fn disable_interfering_sensors_collection(&mut self) {
        self.interfering_sensors = None;
    }

    /// List of rigid bodies geometrically intersecting this sensor.
    #[inline]
    pub fn interfering_bodies(&self) -> Option<RigidBodies<N>> {
        // XXX: duplicate code from World.
        // This should actually be a method on the HashMap.
        fn extract_value<N: Real>(e: &Entry<usize, RigidBodyHandle<N>>)
            -> &RigidBodyHandle<N> {
            &e.value
        }

        let extract_value_fn: fn(_) -> _ = extract_value;

        self.interfering_bodies.as_ref().map(|bs| bs.elements().iter().map(extract_value_fn))
    }

    /// List of sensors geometrically intersecting this sensor.
    #[inline]
    pub fn interfering_sensors(&self) -> Option<Sensors<N>> {
        // XXX: duplicate code from World.
        // This should actually be a method on the HashMap.
        fn extract_value<N: Real>(e: &Entry<usize, SensorHandle<N>>)
            -> &SensorHandle<N> {
            &e.value
        }

        let extract_value_fn: fn(_) -> _ = extract_value;
        self.interfering_sensors.as_ref().map(|bs| bs.elements().iter().map(extract_value_fn))
    }

    /// This sensor's position relative to `self.parent()`.
    ///
    /// If this sensor has no parent, then this relative position is actually the sensor absolute
    /// position.
    #[inline]
    pub fn relative_position(&self) -> &Isometry<N> {
        &self.relative_position
    }

    /// Sets the sensor position relative to `self.parent()`.
    ///
    /// If `self.parent()` is `None`, then this sets the sensor's absolute position.
    #[inline]
    pub fn set_relative_position(&mut self, rel_pos: Isometry<N>) {
        self.did_move_locally  = true;
        self.relative_position = rel_pos
    }

    /// This sensor's absolute position.
    #[inline]
    pub fn position(&self) -> Isometry<N> {
        match self.parent {
            Some(ref rb) => *rb.borrow().position() * self.relative_position,
            None         => self.relative_position.clone()
        }
    }

    /// Sets the sensor absolute position.
    ///
    /// If `self.parent()` is not `None`, then this automatically computes the relevant relative
    /// position and updates it.
    #[inline]
    pub fn set_position(&mut self, abs_pos: Isometry<N>) {
        self.did_move_locally = true;
        match self.parent {
            Some(ref rb) => {
                self.relative_position = rb.borrow().position().inverse() * abs_pos
            }
            None => self.relative_position = abs_pos
        }
    }

    /// This sensor position's translational component.
    ///
    #[inline]
    pub fn center(&self) -> Point<N> {
        match self.parent {
            Some(ref rb) => {
                let coords = self.relative_position.translation.vector;
                *rb.borrow().position() * Point::from_coordinates(coords)
            },
            None => Point::from_coordinates(self.relative_position.translation.vector)
        }
    }

    /// The collision margin of this sensor's shape.
    ///
    /// This is set to zero by default.
    pub fn margin(&self) -> N {
        self.margin
    }

    /// Whether or not proximity detection between this sensor and its parent is enabled.
    #[inline]
    pub fn proximity_with_parent_enabled(&self) -> bool {
        self.parent_prox
    }

    /// Enables proximity detection between this sensor and its parent if it has one.
    #[inline]
    pub fn enable_proximity_with_parent(&mut self) {
        self.parent_prox = true;
    }

    /// Disables proximity detection between this sensor and its parent if it has one.
    #[inline]
    pub fn disable_proximity_with_parent(&mut self) {
        self.parent_prox = false;
    }

    /// The rigid body to which this sensor is kinematically attached.
    ///
    /// Returns `None` if this sensor is directly attached to the scene.
    #[inline]
    pub fn parent(&self) -> Option<&RigidBodyHandle<N>> {
        self.parent.as_ref()
    }

    /// A reference of this sensor's shared shape.
    #[inline]
    pub fn shape(&self) -> &ShapeHandle<Point<N>, Isometry<N>> {
        &self.shape
    }

    /// This sensor's collision groups.
    #[inline]
    pub fn collision_groups(&self) -> &SensorCollisionGroups {
        &self.collision_groups
    }
}

#[doc(hidden)]
pub struct SensorProximityCollector;

impl<N: Real> ProximityHandler<Point<N>, Isometry<N>, WorldObject<N>> for SensorProximityCollector {
    fn handle_proximity(&mut self,
                        o1: &WorldCollisionObject<N>, o2: &WorldCollisionObject<N>,
                        _: Proximity, new_proximity: Proximity) {

        if new_proximity == Proximity::Intersecting {
            match (&o1.data, &o2.data) {
                (&WorldObject::RigidBody(ref rb1), &WorldObject::Sensor(ref s2)) => {
                    let mut s2 = s2.borrow_mut();
                    if let Some(ref mut ib) = s2.interfering_bodies {
                        let _ = ib.insert(WorldObject::rigid_body_uid(rb1), rb1.clone());
                    }
                },
                (&WorldObject::Sensor(ref s1), &WorldObject::RigidBody(ref rb2)) => {
                    let mut s1 = s1.borrow_mut();
                    if let Some(ref mut ib) = s1.interfering_bodies {
                        let _ = ib.insert(WorldObject::rigid_body_uid(rb2), rb2.clone());
                    }
                },
                (&WorldObject::Sensor(ref s1), &WorldObject::Sensor(ref s2)) => {
                    {
                        let mut s1 = s1.borrow_mut();
                        if let Some(ref mut ib) = s1.interfering_sensors {
                            let _ = ib.insert(WorldObject::sensor_uid(s2), s2.clone());
                        }
                    }

                    let mut s2 = s2.borrow_mut();
                    if let Some(ref mut ib) = s2.interfering_sensors {
                        let _ = ib.insert(WorldObject::sensor_uid(s1), s1.clone());
                    }
                },
                (&WorldObject::RigidBody(_), &WorldObject::RigidBody(_)) => { }
            }
        }
        else {
            match (&o1.data, &o2.data) {
                (&WorldObject::RigidBody(ref rb1), &WorldObject::Sensor(ref s2)) => {
                    let mut s2 = s2.borrow_mut();
                    if let Some(ref mut ib) = s2.interfering_bodies {
                        let _ = ib.remove(&WorldObject::rigid_body_uid(rb1));
                    }
                },
                (&WorldObject::Sensor(ref s1), &WorldObject::RigidBody(ref rb2)) => {
                    let mut s1 = s1.borrow_mut();
                    if let Some(ref mut ib) = s1.interfering_bodies {
                        let _ = ib.remove(&WorldObject::rigid_body_uid(rb2));
                    }
                },
                (&WorldObject::Sensor(ref s1), &WorldObject::Sensor(ref s2)) => {
                    {
                        let mut s1 = s1.borrow_mut();
                        if let Some(ref mut ib) = s1.interfering_sensors {
                            let _ = ib.remove(&WorldObject::sensor_uid(s2));
                        }
                    }

                    let mut s2 = s2.borrow_mut();
                    if let Some(ref mut ib) = s2.interfering_sensors {
                        let _ = ib.remove(&WorldObject::sensor_uid(s1));
                    }
                },
                (&WorldObject::RigidBody(_), &WorldObject::RigidBody(_)) => { }
            }
        }
    }
}
<commit_msg>Add Sensor::set_collision_groups<commit_after>use std::mem;
use std::any::Any;
use std::rc::Rc;
use std::cell::RefCell;

use alga::general::Real;
use na;
use ncollide::utils::data::hash_map::{HashMap, Entry};
use ncollide::utils::data::hash::UintTWHash;
use ncollide::shape::{Shape, ShapeHandle};
use ncollide::narrow_phase::ProximityHandler;
use ncollide::query::Proximity;
use math::{Point, Isometry};
use object::{RigidBodyHandle, SensorCollisionGroups, WorldObject};
use world::{WorldCollisionObject, RigidBodies, Sensors};

/// A shared, mutable, sensor.
pub type SensorHandle<N> = Rc<RefCell<Sensor<N>>>;

/// An object capable of detecting interferances with other entities without interacting with them.
pub struct Sensor<N: Real> {
    parent:              Option<RigidBodyHandle<N>>,
    relative_position:   Isometry<N>,
    shape:               ShapeHandle<Point<N>, Isometry<N>>,
    margin:              N,
    collision_groups:    SensorCollisionGroups,
    parent_prox:         bool,
    user_data:           Option<Box<Any>>,
    interfering_bodies:  Option<HashMap<usize, RigidBodyHandle<N>, UintTWHash>>,
    interfering_sensors: Option<HashMap<usize, SensorHandle<N>, UintTWHash>>,
    #[doc(hidden)]
    pub did_move_locally:    bool
}

impl<N: Real> Sensor<N> {
    /// Creates a new sensor.
    ///
    /// A sensor may either be attached to the rigid body `parent`, or be attached to the ground.
    /// If it has a parent, it will have a position relative to it. If the parent moves, the sensor
    /// will automatically move as well. By default, a sensor does not trigger any proximity event
    /// when it intersects its own parent.
    ///
    /// A sensor has a default margin equal to zero.
    pub fn new<G>(shape: G, parent: Option<RigidBodyHandle<N>>) -> Sensor<N>
        where G: Send + Sync + Shape<Point<N>, Isometry<N>> {
        Sensor::new_with_shared_shape(ShapeHandle::new(shape), parent)
    }

    /// Creates a new senson with a given shared shape.
    pub fn new_with_shared_shape(shape:  ShapeHandle<Point<N>, Isometry<N>>,
                                 parent: Option<RigidBodyHandle<N>>)
                                 -> Sensor<N> {
        Sensor {
            parent:              parent,
            relative_position:   na::one(),
            did_move_locally:    true,
            shape:               shape,
            margin:              na::zero(),
            collision_groups:    SensorCollisionGroups::new(),
            parent_prox:         false,
            user_data:           None,
            interfering_bodies:  None,
            interfering_sensors: None
        }
    }

    /// Reference to user-defined data attached to this sensor.
    #[inline]
    pub fn user_data(&self) -> Option<&Box<Any>> {
        self.user_data.as_ref()
    }

    /// Mutable reference to user-defined data attached to this sensor.
    #[inline]
    pub fn user_data_mut(&mut self) -> Option<&mut Box<Any>> {
        self.user_data.as_mut()
    }

    /// Attach some user-defined data to this sensor and return the old one.
    pub fn set_user_data(&mut self, user_data: Option<Box<Any>>) -> Option<Box<Any>> {
        mem::replace(&mut self.user_data, user_data)
    }

    /// Enables the collection of all body handles that intersect this sensor.
    pub fn enable_interfering_bodies_collection(&mut self) {
        if self.interfering_bodies.is_none() {
            self.interfering_bodies = Some(HashMap::new(UintTWHash::new()));
        }
    }

    /// Enables the collection of all body handles that intersect this sensor.
    pub fn enable_interfering_sensors_collection(&mut self) {
        if self.interfering_sensors.is_none() {
            self.interfering_sensors= Some(HashMap::new(UintTWHash::new()));
        }
    }

    /// Disables the collection of all body handles that intersect this sensor.
    pub fn disable_interfering_bodies_collection(&mut self) {
        self.interfering_bodies = None;
    }

    /// Disables the collection of all body handles that intersect this sensor.
    pub fn disable_interfering_sensors_collection(&mut self) {
        self.interfering_sensors = None;
    }

    /// List of rigid bodies geometrically intersecting this sensor.
    #[inline]
    pub fn interfering_bodies(&self) -> Option<RigidBodies<N>> {
        // XXX: duplicate code from World.
        // This should actually be a method on the HashMap.
        fn extract_value<N: Real>(e: &Entry<usize, RigidBodyHandle<N>>)
            -> &RigidBodyHandle<N> {
            &e.value
        }

        let extract_value_fn: fn(_) -> _ = extract_value;

        self.interfering_bodies.as_ref().map(|bs| bs.elements().iter().map(extract_value_fn))
    }

    /// List of sensors geometrically intersecting this sensor.
    #[inline]
    pub fn interfering_sensors(&self) -> Option<Sensors<N>> {
        // XXX: duplicate code from World.
        // This should actually be a method on the HashMap.
        fn extract_value<N: Real>(e: &Entry<usize, SensorHandle<N>>)
            -> &SensorHandle<N> {
            &e.value
        }

        let extract_value_fn: fn(_) -> _ = extract_value;
        self.interfering_sensors.as_ref().map(|bs| bs.elements().iter().map(extract_value_fn))
    }

    /// This sensor's position relative to `self.parent()`.
    ///
    /// If this sensor has no parent, then this relative position is actually the sensor absolute
    /// position.
    #[inline]
    pub fn relative_position(&self) -> &Isometry<N> {
        &self.relative_position
    }

    /// Sets the sensor position relative to `self.parent()`.
    ///
    /// If `self.parent()` is `None`, then this sets the sensor's absolute position.
    #[inline]
    pub fn set_relative_position(&mut self, rel_pos: Isometry<N>) {
        self.did_move_locally  = true;
        self.relative_position = rel_pos
    }

    /// This sensor's absolute position.
    #[inline]
    pub fn position(&self) -> Isometry<N> {
        match self.parent {
            Some(ref rb) => *rb.borrow().position() * self.relative_position,
            None         => self.relative_position.clone()
        }
    }

    /// Sets the sensor absolute position.
    ///
    /// If `self.parent()` is not `None`, then this automatically computes the relevant relative
    /// position and updates it.
    #[inline]
    pub fn set_position(&mut self, abs_pos: Isometry<N>) {
        self.did_move_locally = true;
        match self.parent {
            Some(ref rb) => {
                self.relative_position = rb.borrow().position().inverse() * abs_pos
            }
            None => self.relative_position = abs_pos
        }
    }

    /// This sensor position's translational component.
    ///
    #[inline]
    pub fn center(&self) -> Point<N> {
        match self.parent {
            Some(ref rb) => {
                let coords = self.relative_position.translation.vector;
                *rb.borrow().position() * Point::from_coordinates(coords)
            },
            None => Point::from_coordinates(self.relative_position.translation.vector)
        }
    }

    /// The collision margin of this sensor's shape.
    ///
    /// This is set to zero by default.
    pub fn margin(&self) -> N {
        self.margin
    }

    /// Whether or not proximity detection between this sensor and its parent is enabled.
    #[inline]
    pub fn proximity_with_parent_enabled(&self) -> bool {
        self.parent_prox
    }

    /// Enables proximity detection between this sensor and its parent if it has one.
    #[inline]
    pub fn enable_proximity_with_parent(&mut self) {
        self.parent_prox = true;
    }

    /// Disables proximity detection between this sensor and its parent if it has one.
    #[inline]
    pub fn disable_proximity_with_parent(&mut self) {
        self.parent_prox = false;
    }

    /// The rigid body to which this sensor is kinematically attached.
    ///
    /// Returns `None` if this sensor is directly attached to the scene.
    #[inline]
    pub fn parent(&self) -> Option<&RigidBodyHandle<N>> {
        self.parent.as_ref()
    }

    /// A reference of this sensor's shared shape.
    #[inline]
    pub fn shape(&self) -> &ShapeHandle<Point<N>, Isometry<N>> {
        &self.shape
    }

    /// This sensor's collision groups.
    #[inline]
    pub fn collision_groups(&self) -> &SensorCollisionGroups {
        &self.collision_groups
    }

    /// Set this sensor's collision groups.
    #[inline]
    pub fn set_collision_groups(&mut self, new_groups: SensorCollisionGroups) {
        self.collision_groups = new_groups;
    }
}

#[doc(hidden)]
pub struct SensorProximityCollector;

impl<N: Real> ProximityHandler<Point<N>, Isometry<N>, WorldObject<N>> for SensorProximityCollector {
    fn handle_proximity(&mut self,
                        o1: &WorldCollisionObject<N>, o2: &WorldCollisionObject<N>,
                        _: Proximity, new_proximity: Proximity) {

        if new_proximity == Proximity::Intersecting {
            match (&o1.data, &o2.data) {
                (&WorldObject::RigidBody(ref rb1), &WorldObject::Sensor(ref s2)) => {
                    let mut s2 = s2.borrow_mut();
                    if let Some(ref mut ib) = s2.interfering_bodies {
                        let _ = ib.insert(WorldObject::rigid_body_uid(rb1), rb1.clone());
                    }
                },
                (&WorldObject::Sensor(ref s1), &WorldObject::RigidBody(ref rb2)) => {
                    let mut s1 = s1.borrow_mut();
                    if let Some(ref mut ib) = s1.interfering_bodies {
                        let _ = ib.insert(WorldObject::rigid_body_uid(rb2), rb2.clone());
                    }
                },
                (&WorldObject::Sensor(ref s1), &WorldObject::Sensor(ref s2)) => {
                    {
                        let mut s1 = s1.borrow_mut();
                        if let Some(ref mut ib) = s1.interfering_sensors {
                            let _ = ib.insert(WorldObject::sensor_uid(s2), s2.clone());
                        }
                    }

                    let mut s2 = s2.borrow_mut();
                    if let Some(ref mut ib) = s2.interfering_sensors {
                        let _ = ib.insert(WorldObject::sensor_uid(s1), s1.clone());
                    }
                },
                (&WorldObject::RigidBody(_), &WorldObject::RigidBody(_)) => { }
            }
        }
        else {
            match (&o1.data, &o2.data) {
                (&WorldObject::RigidBody(ref rb1), &WorldObject::Sensor(ref s2)) => {
                    let mut s2 = s2.borrow_mut();
                    if let Some(ref mut ib) = s2.interfering_bodies {
                        let _ = ib.remove(&WorldObject::rigid_body_uid(rb1));
                    }
                },
                (&WorldObject::Sensor(ref s1), &WorldObject::RigidBody(ref rb2)) => {
                    let mut s1 = s1.borrow_mut();
                    if let Some(ref mut ib) = s1.interfering_bodies {
                        let _ = ib.remove(&WorldObject::rigid_body_uid(rb2));
                    }
                },
                (&WorldObject::Sensor(ref s1), &WorldObject::Sensor(ref s2)) => {
                    {
                        let mut s1 = s1.borrow_mut();
                        if let Some(ref mut ib) = s1.interfering_sensors {
                            let _ = ib.remove(&WorldObject::sensor_uid(s2));
                        }
                    }

                    let mut s2 = s2.borrow_mut();
                    if let Some(ref mut ib) = s2.interfering_sensors {
                        let _ = ib.remove(&WorldObject::sensor_uid(s1));
                    }
                },
                (&WorldObject::RigidBody(_), &WorldObject::RigidBody(_)) => { }
            }
        }
    }
}
<|endoftext|>


<commit_before>use super::{Click, CloneCell, Color, CopyCell, Event, Place, Point, Rect, Renderer, Widget, Window};

use std::cmp::min;
use std::sync::Arc;

pub struct TextBox {
    pub rect: CopyCell<Rect>,
    pub text: CloneCell<String>,
    pub text_i: CopyCell<usize>,
    pub bg: Color,
    pub fg: Color,
    pub fg_cursor: Color,
    on_click: Option<Arc<Fn(&TextBox, Point)>>,
    pressed: CopyCell<bool>,
}

impl TextBox {
    pub fn new(text: &str) -> Self {
        TextBox {
            rect: CopyCell::new(Rect::default()),
            text: CloneCell::new(text.to_string()),
            text_i: CopyCell::new(0),
            bg: Color::rgb(255, 255, 255),
            fg: Color::rgb(0, 0, 0),
            fg_cursor: Color::rgb(128, 128, 128),
            on_click: None,
            pressed: CopyCell::new(false),
        }
    }

    pub fn place(self, window: &mut Window) -> Arc<Self> {
        let rc = Arc::new(self);

        window.widgets.push(rc.clone());

        rc
    }
}

impl Click for TextBox {
    fn click(&self, point: Point){
        let on_click_option = match self.on_click {
            Some(ref on_click) => Some(on_click.clone()),
            None => None
        };

        if let Some(on_click) = on_click_option {
            on_click(self, point);
        }
    }

    fn on_click<T: Fn(&Self, Point) + 'static>(mut self, func: T) -> Self {
        self.on_click = Some(Arc::new(func));

        self
    }
}

impl Place for TextBox {
    fn position(self, x: isize, y: isize) -> Self {
        let mut rect = self.rect.get();
        rect.x = x;
        rect.y = y;
        self.rect.set(rect);

        self
    }

    fn size(self, width: usize, height: usize) -> Self {
        let mut rect = self.rect.get();
        rect.width = width;
        rect.height = height;
        self.rect.set(rect);

        self
    }
}

impl Widget for TextBox {
    fn draw(&self, renderer: &mut Renderer) {
        let rect = self.rect.get();

        renderer.rect(rect, self.bg);

        let text_i = self.text_i.get();
        let text = self.text.borrow();
        let mut x = 0;
        for (i, c) in text.char_indices() {
            if x + 8 <= rect.width as isize {
                if i == text_i {
                    renderer.rect(Rect::new(x + rect.x, rect.y, 8, 16), self.fg_cursor);
                }
                renderer.char(Point::new(x + rect.x, rect.y), c, self.fg);
            }
            x += 8;
        }

        if text.len() == text_i {
            if x + 8 <= rect.width as isize {
                renderer.rect(Rect::new(x + rect.x, rect.y, 8, 16), self.fg_cursor);
            }
        }
    }

    fn event(&self, event: Event) {
        match event {
            Event::Mouse { point, left_button, .. } => {
                let mut click = false;

                let rect = self.rect.get();
                if rect.contains(point){
                    if left_button {
                        self.pressed.set(true);
                    } else {
                        if self.pressed.get() {
                            click = true;
                        }

                        self.pressed.set(false);
                    }
                } else {
                    if ! left_button {
                        self.pressed.set(false);
                    }
                }

                if click {
                    let click_point = Point::new(point.x - rect.x, point.y - rect.y);
                    self.click(click_point);
                }
            },
            Event::Text { c } => {
                let mut text = self.text.borrow_mut();
                let text_i = self.text_i.get();
                if text.is_char_boundary(text_i) {
                    text.insert(text_i, c);
                    self.text_i.set(min(text.char_range_at(text_i).next, text.len()));
                }
            },
            Event::Backspace => {
                let mut text = self.text.borrow_mut();
                let mut text_i = self.text_i.get();
                if text.is_char_boundary(text_i) && text_i > 0 {
                    text_i = min(text.char_range_at_reverse(text_i).next, text.len());
                    if text.is_char_boundary(text_i) && text_i < text.len() {
                        text.remove(text_i);
                        self.text_i.set(min(text_i, text.len()));
                    }
                }
            },
            Event::Delete => {
                let mut text = self.text.borrow_mut();
                let text_i = self.text_i.get();
                if text.is_char_boundary(text_i) && text_i < text.len() {
                    text.remove(text_i);
                    self.text_i.set(min(text_i, text.len()));
                }
            },
            Event::LeftArrow => {
                let text = self.text.borrow();
                let text_i = self.text_i.get();
                if text.is_char_boundary(text_i) && text_i > 0 {
                    self.text_i.set(min(text.char_range_at_reverse(text_i).next, text.len()));
                }
            },
            Event::RightArrow => {
                let text = self.text.borrow();
                let text_i = self.text_i.get();
                if text.is_char_boundary(text_i) && text_i < text.len() {
                    self.text_i.set(min(text.char_range_at(text_i).next, text.len()));
                }
            },
            _ => ()
        }
    }
}
<commit_msg>Allow selection of index<commit_after>use super::{Click, CloneCell, Color, CopyCell, Event, Place, Point, Rect, Renderer, Widget, Window};

use std::cmp::min;
use std::sync::Arc;

pub struct TextBox {
    pub rect: CopyCell<Rect>,
    pub text: CloneCell<String>,
    pub text_i: CopyCell<usize>,
    pub bg: Color,
    pub fg: Color,
    pub fg_cursor: Color,
    on_click: Option<Arc<Fn(&TextBox, Point)>>,
    pressed: CopyCell<bool>,
}

impl TextBox {
    pub fn new(text: &str) -> Self {
        TextBox {
            rect: CopyCell::new(Rect::default()),
            text: CloneCell::new(text.to_string()),
            text_i: CopyCell::new(0),
            bg: Color::rgb(255, 255, 255),
            fg: Color::rgb(0, 0, 0),
            fg_cursor: Color::rgb(128, 128, 128),
            on_click: None,
            pressed: CopyCell::new(false),
        }
    }

    pub fn place(self, window: &mut Window) -> Arc<Self> {
        let rc = Arc::new(self);

        window.widgets.push(rc.clone());

        rc
    }
}

impl Click for TextBox {
    fn click(&self, point: Point){
        let on_click_option = match self.on_click {
            Some(ref on_click) => Some(on_click.clone()),
            None => None
        };

        if let Some(on_click) = on_click_option {
            on_click(self, point);
        }
    }

    fn on_click<T: Fn(&Self, Point) + 'static>(mut self, func: T) -> Self {
        self.on_click = Some(Arc::new(func));

        self
    }
}

impl Place for TextBox {
    fn position(self, x: isize, y: isize) -> Self {
        let mut rect = self.rect.get();
        rect.x = x;
        rect.y = y;
        self.rect.set(rect);

        self
    }

    fn size(self, width: usize, height: usize) -> Self {
        let mut rect = self.rect.get();
        rect.width = width;
        rect.height = height;
        self.rect.set(rect);

        self
    }
}

impl Widget for TextBox {
    fn draw(&self, renderer: &mut Renderer) {
        let rect = self.rect.get();

        renderer.rect(rect, self.bg);

        let text_i = self.text_i.get();
        let text = self.text.borrow();
        let mut x = 0;
        for (i, c) in text.char_indices() {
            if x + 8 <= rect.width as isize {
                if i == text_i {
                    renderer.rect(Rect::new(x + rect.x, rect.y, 8, 16), self.fg_cursor);
                }
                renderer.char(Point::new(x + rect.x, rect.y), c, self.fg);
            }
            x += 8;
        }

        if text.len() == text_i {
            if x + 8 <= rect.width as isize {
                renderer.rect(Rect::new(x + rect.x, rect.y, 8, 16), self.fg_cursor);
            }
        }
    }

    fn event(&self, event: Event) {
        match event {
            Event::Mouse { point, left_button, .. } => {
                let mut click = false;

                let rect = self.rect.get();
                if rect.contains(point){
                    if left_button {
                        self.pressed.set(true);
                    } else {
                        if self.pressed.get() {
                            click = true;
                        }

                        self.pressed.set(false);
                    }
                } else {
                    if ! left_button {
                        self.pressed.set(false);
                    }
                }

                if click {
                    let click_point = Point::new(point.x - rect.x, point.y - rect.y);
                    {
                        let text = self.text.borrow();
                        let mut x = 0;
                        for (i, _c) in text.char_indices() {
                            if x + 8 <= rect.width as isize && click_point.x >= x && click_point.x < x + 8{
                                self.text_i.set(i);
                            }
                            x += 8;
                        }

                        if x + 8 <= rect.width as isize && click_point.x >= x {
                            self.text_i.set(text.len());
                        }
                    }
                    self.click(click_point);
                }
            },
            Event::Text { c } => {
                let mut text = self.text.borrow_mut();
                let text_i = self.text_i.get();
                if text.is_char_boundary(text_i) {
                    text.insert(text_i, c);
                    self.text_i.set(min(text.char_range_at(text_i).next, text.len()));
                }
            },
            Event::Backspace => {
                let mut text = self.text.borrow_mut();
                let mut text_i = self.text_i.get();
                if text.is_char_boundary(text_i) && text_i > 0 {
                    text_i = min(text.char_range_at_reverse(text_i).next, text.len());
                    if text.is_char_boundary(text_i) && text_i < text.len() {
                        text.remove(text_i);
                        self.text_i.set(min(text_i, text.len()));
                    }
                }
            },
            Event::Delete => {
                let mut text = self.text.borrow_mut();
                let text_i = self.text_i.get();
                if text.is_char_boundary(text_i) && text_i < text.len() {
                    text.remove(text_i);
                    self.text_i.set(min(text_i, text.len()));
                }
            },
            Event::LeftArrow => {
                let text = self.text.borrow();
                let text_i = self.text_i.get();
                if text.is_char_boundary(text_i) && text_i > 0 {
                    self.text_i.set(min(text.char_range_at_reverse(text_i).next, text.len()));
                }
            },
            Event::RightArrow => {
                let text = self.text.borrow();
                let text_i = self.text_i.get();
                if text.is_char_boundary(text_i) && text_i < text.len() {
                    self.text_i.set(min(text.char_range_at(text_i).next, text.len()));
                }
            },
            _ => ()
        }
    }
}
<|endoftext|>


<commit_before>//! Support for log4rs configuration from TOML files.
//!
//! # Syntax
//!
//! ```toml
//! # If set, log4rs will scan the file at the specified rate in seconds for
//! # changes and automatically reconfigure the logger.
//! refresh_rate = 30
//!
//! # Appenders are configured as tables inside the "appender" table. This
//! # appender is named "foo".
//! [appender.foo]
//! # All appenders must specify a "kind", which must match the kind of an
//! # appender mapping provided to the `Creator` used to deserialize the
//! # config file.
//! kind = "console"
//!
//! # Arbitrary fields may be added to appender configurations. Remaining
//! # entries will be passed to the `CreateAppender` object associated with
//! # the specified kind.
//! pattern = "%d [%t] %m"
//!
//! # Filters attached to an appender are configured inside the "filter" array.
//! [[appender.foo.filter]]
//! # Like appenders, filters must specify a "kind".
//! kind = "threshold"
//!
//! # Also like appenders, arbitrary fields may be added to filter
//! # configurations.
//! level = "error"
//!
//! # The root logger is configured by the "root" table. It is optional.
//! # If the "root" table is not specified, the root will default to a level of
//! # "debug" and no appenders.
//! [root]
//! # The maximum log level for the root logger. Must be specified if the
//! # "root" table is defined.
//! level = "warn"
//!
//! # The list of names of appenders attached to the root logger. If not
//! # specified, defaults to an empty list.
//! appenders = ["foo"]
//!
//! # Loggers are configured as tables inside of the "logger" array.
//! [[logger]]
//! # The name of the logger. Must be specified.
//! name = "foo::bar::baz"
//!
//! # The maximum . If it is not present, the level of the logger's parent is used.
//! level = "trace"
//!
//! # A list of names of appenders attached to the logger. If not specified,
//! # defaults to an empty list.
//! appenders = ["foo"]
//!
//! # The additivity of the logger. If true, the appenders attached to this
//! # logger's parent will also be attached to this logger. If not specified,
//! # defaults to true.
//! additive = false
//! ```
use log::LogLevelFilter;
use std::collections::HashMap;
use std::default::Default;
use std::error;
use std::fmt;
use std::time::Duration;
use toml_parser::{self, Value};

use appender::{FileAppender, ConsoleAppender};
use filter::{ThresholdFilter};
use config;
use pattern::PatternLayout;
use {Append, Filter, PrivateTomlConfigExt, PrivateConfigErrorsExt};

mod raw;

/// A trait implemented by types that can create appenders.
pub trait CreateAppender: Send + 'static {
    /// Creates an appender with the specified config.
    fn create_appender(&self, config: &toml_parser::Table)
                       -> Result<Box<Append>, Box<error::Error>>;
}

/// A trait implemented by types that can create filters.
pub trait CreateFilter: Send + 'static {
    /// Creates a filter with the specified config.
    fn create_filter(&self, config: &toml_parser::Table)
                     -> Result<Box<Filter>, Box<error::Error>>;
}

/// A type that can create appenders.
///
/// `Creator` implements `Default`, which returns a `Creator` with the
/// following mappings:
///
/// * Appenders
///     * "file" -> `FileAppenderCreator`
///     * "console" -> `ConsoleAppenderCreator`
/// * Filters
///     * "threshold" -> `ThresholdFilterCreator`
pub struct Creator {
    appenders: HashMap<String, Box<CreateAppender>>,
    filters: HashMap<String, Box<CreateFilter>>,
}

impl Default for Creator {
    fn default() -> Creator {
        let mut creator = Creator::new();
        creator.add_appender("file", Box::new(FileAppenderCreator));
        creator.add_appender("console", Box::new(ConsoleAppenderCreator));
        creator.add_filter("threshold", Box::new(ThresholdFilterCreator));
        creator
    }
}

impl Creator {
    /// Creates a new `Creator` with no appender or filter mappings.
    pub fn new() -> Creator {
        Creator {
            appenders: HashMap::new(),
            filters: HashMap::new(),
        }
    }

    /// Adds a mapping from the specified `kind` to the specified appender
    /// creator.
    pub fn add_appender(&mut self, kind: &str, creator: Box<CreateAppender>) {
        self.appenders.insert(kind.to_string(), creator);
    }

    /// Adds a mapping from the specified `kind` to the specified filter
    /// creator.
    pub fn add_filter(&mut self, kind: &str, creator: Box<CreateFilter>) {
        self.filters.insert(kind.to_string(), creator);
    }

    fn create_appender(&self, kind: &str, config: &toml_parser::Table)
                       -> Result<Box<Append>, Box<error::Error>> {
        match self.appenders.get(kind) {
            Some(creator) => creator.create_appender(config),
            None => Err(Box::new(StringError(format!("No creator registered for appender kind \"{}\"", kind))))
        }
    }

    fn create_filter(&self, kind: &str, config: &toml_parser::Table)
                     -> Result<Box<Filter>, Box<error::Error>> {
        match self.filters.get(kind) {
            Some(creator) => creator.create_filter(config),
            None => Err(Box::new(StringError(format!("No creator registered for filter kind \"{}\"", kind))))
        }
    }
}

/// Errors encountered when parsing a log4rs TOML config.
#[derive(Debug)]
pub struct ParseErrors {
    errors: Vec<String>,
}

impl fmt::Display for ParseErrors {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        for error in &self.errors {
            try!(writeln!(fmt, "{}", error));
        }
        Ok(())
    }
}

impl error::Error for ParseErrors {
    fn description(&self) -> &str {
        "Errors encountered when parsing a log4rs TOML config"
    }
}

/// An error returned when deserializing a TOML configuration into a log4rs `Config`.
#[derive(Debug)]
pub enum Error {
    /// An error instantiating an appender.
    AppenderCreation(String, Box<error::Error>),
    /// An error instantiating a filter.
    FilterCreation(String, Box<error::Error>),
    /// An error when creating the log4rs `Config`.
    Config(config::Error),
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::AppenderCreation(ref appender, ref err) => {
                write!(fmt, "Error creating appender `{}`: {}", appender, err)
            }
            Error::FilterCreation(ref appender, ref err) => {
                write!(fmt, "Error creating filter for appender `{}`: {}", appender, err)
            }
            Error::Config(ref err) => write!(fmt, "Error creating config: {}", err),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        "An error encountered when deserializing a TOML configuration into a log4rs `Config`"
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::AppenderCreation(_, ref err) => Some(&**err),
            Error::FilterCreation(_, ref err) => Some(&**err),
            Error::Config(ref err) => Some(err),
        }
    }
}

/// Errors encountered when deserializing a TOML configuration into a log4rs `Config`.
#[derive(Debug)]
pub struct Errors {
    errors: Vec<Error>,
}

impl Errors {
    /// Returns the list of errors encountered.
    pub fn errors(&self) -> &[Error] {
        &self.errors
    }
}

impl fmt::Display for Errors {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        for error in &self.errors {
            try!(writeln!(fmt, "{}", error));
        }
        Ok(())
    }
}

impl error::Error for Errors {
    fn description(&self) -> &str {
        "Errors encountered when deserializing a TOML configuration into a log4rs `Config`"
    }
}

/// A deserialized TOML log4rs configuration.
pub struct Config {
    refresh_rate: Option<Duration>,
    config: config::Config,
}

impl Config {
    /// Creates a log4rs `Config` from the specified TOML config string and `Creator`.
    pub fn parse(config: &str, creator: &Creator)
                 -> Result<(Config, Result<(), Errors>), ParseErrors> {
        let mut errors = vec![];

        let config = match raw::parse(config) {
            Ok(config) => config,
            Err(errors) => return Err(ParseErrors { errors: errors }),
        };

        let raw::Config {
            refresh_rate,
            root: raw_root,
            appenders: raw_appenders,
            loggers: raw_loggers,
        } = config;

        let root = match raw_root {
            Some(raw_root) => {
                let mut root = config::Root::builder(raw_root.level);
                if let Some(appenders) = raw_root.appenders {
                    root = root.appenders(appenders);
                }
                root.build()
            }
            None => config::Root::builder(LogLevelFilter::Debug).build(),
        };

        let mut config = config::Config::builder(root);

        for (name, appender) in raw_appenders {
            match creator.create_appender(&appender.kind, &appender.config) {
                Ok(appender_obj) => {
                    let mut builder = config::Appender::builder(name.clone(), appender_obj);
                    for filter in appender.filters.unwrap_or(vec![]) {
                        match creator.create_filter(&filter.kind, &filter.config) {
                            Ok(filter) => builder= builder.filter(filter),
                            Err(err) => errors.push(Error::FilterCreation(name.clone(), err)),
                        }
                    }
                    config = config.appender(builder.build());
                }
                Err(err) => errors.push(Error::AppenderCreation(name, err)),
            }
        }

        for logger in raw_loggers {
            let raw::Logger { name, level, appenders, additive } = logger;
            let mut logger = config::Logger::builder(name, level);
            if let Some(appenders) = appenders {
                logger = logger.appenders(appenders);
            }
            if let Some(additive) = additive {
                logger = logger.additive(additive);
            }
            config = config.logger(logger.build());
        }

        let (config, config_errors) = config.build_lossy();
        if let Err(config_errors) = config_errors {
            for error in config_errors.unpack() {
                errors.push(Error::Config(error));
            }
        }

        let config = Config {
            refresh_rate: refresh_rate,
            config: config
        };

        let errors = if errors.is_empty() {
            Ok(())
        } else {
            Err(Errors {
                errors: errors
            })
        };

        Ok((config, errors))
    }

    /// Returns the requested refresh rate.
    pub fn refresh_rate(&self) -> Option<Duration> {
        self.refresh_rate
    }

    /// Returns the log4rs `Config`.
    pub fn config(&self) -> &config::Config {
        &self.config
    }
}

impl PrivateTomlConfigExt for Config {
    fn unpack(self) -> (Option<Duration>, config::Config) {
        let Config { refresh_rate, config } = self;
        (refresh_rate, config)
    }
}

#[derive(Debug)]
struct StringError(String);

impl fmt::Display for StringError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(&self.0)
    }
}

impl error::Error for StringError {
    fn description(&self) -> &str {
        &self.0
    }
}

impl error::FromError<String> for StringError {
    fn from_error(s: String) -> StringError {
        StringError(s)
    }
}

/// An appender creator for the `FileAppender`.
///
/// The `path` key is required, and specifies the path to the log file. The
/// `pattern` key is optional and specifies a `PatternLayout` pattern to be
/// used for output. The `append` key is optional and specifies whether the
/// output file should be truncated or appended to.
pub struct FileAppenderCreator;

impl CreateAppender for FileAppenderCreator {
    fn create_appender(&self, config: &toml_parser::Table)
                       -> Result<Box<Append>, Box<error::Error>> {
        let path = match config.get("path") {
            Some(&Value::String(ref path)) => path,
            Some(_) => return Err(Box::new(StringError("`path` must be a string".to_string()))),
            None => return Err(Box::new(StringError("`path` is required".to_string()))),
        };

        let mut appender = FileAppender::builder(path);
        match config.get("pattern") {
            Some(&Value::String(ref pattern)) => {
                appender = appender.pattern(try!(PatternLayout::new(pattern)));
            }
            Some(_) => return Err(Box::new(StringError("`pattern` must be a string".to_string()))),
            None => {}
        }

        match config.get("append") {
            Some(&Value::Boolean(append)) => appender = appender.append(append),
            None => {}
            Some(_) => return Err(Box::new(StringError("`append` must be a bool".to_string()))),
        }

        match appender.build() {
            Ok(appender) => Ok(Box::new(appender)),
            Err(err) => Err(Box::new(err))
        }
    }
}

/// An appender creator for the `ConsoleAppender`.
///
/// The `pattern` key is optional and specifies a `PatternLayout` pattern to be
/// used for output.
pub struct ConsoleAppenderCreator;

impl CreateAppender for ConsoleAppenderCreator {
    fn create_appender(&self, config: &toml_parser::Table)
                       -> Result<Box<Append>, Box<error::Error>> {
        let mut appender = ConsoleAppender::builder();
        match config.get("pattern") {
            Some(&Value::String(ref pattern)) => {
                appender = appender.pattern(try!(PatternLayout::new(pattern)));
            }
            Some(_) => return Err(Box::new(StringError("`pattern` must be a string".to_string()))),
            None => {}
        }

        Ok(Box::new(appender.build()))
    }
}

/// A filter creator for the `ThresholdFilter`.
///
/// The `level` key is required and specifies the threshold for the filter.
pub struct ThresholdFilterCreator;

impl CreateFilter for ThresholdFilterCreator {
    fn create_filter(&self, config: &toml_parser::Table)
                     -> Result<Box<Filter>, Box<error::Error>> {
        let level = match config.get("level") {
            Some(&Value::String(ref level)) => level,
            Some(_) => return Err(Box::new(StringError("`level` must be a string".to_string()))),
            None => return Err(Box::new(StringError("`level` must be provided".to_string()))),
        };

        let level = match level.parse() {
            Ok(level) => level,
            Err(_) => return Err(Box::new(StringError(format!("Invalid `level` \"{}\"", level)))),
        };

        Ok(Box::new(ThresholdFilter::new(level)))
    }
}
<commit_msg>Make creators take configs by value<commit_after>//! Support for log4rs configuration from TOML files.
//!
//! # Syntax
//!
//! ```toml
//! # If set, log4rs will scan the file at the specified rate in seconds for
//! # changes and automatically reconfigure the logger.
//! refresh_rate = 30
//!
//! # Appenders are configured as tables inside the "appender" table. This
//! # appender is named "foo".
//! [appender.foo]
//! # All appenders must specify a "kind", which must match the kind of an
//! # appender mapping provided to the `Creator` used to deserialize the
//! # config file.
//! kind = "console"
//!
//! # Arbitrary fields may be added to appender configurations. Remaining
//! # entries will be passed to the `CreateAppender` object associated with
//! # the specified kind.
//! pattern = "%d [%t] %m"
//!
//! # Filters attached to an appender are configured inside the "filter" array.
//! [[appender.foo.filter]]
//! # Like appenders, filters must specify a "kind".
//! kind = "threshold"
//!
//! # Also like appenders, arbitrary fields may be added to filter
//! # configurations.
//! level = "error"
//!
//! # The root logger is configured by the "root" table. It is optional.
//! # If the "root" table is not specified, the root will default to a level of
//! # "debug" and no appenders.
//! [root]
//! # The maximum log level for the root logger. Must be specified if the
//! # "root" table is defined.
//! level = "warn"
//!
//! # The list of names of appenders attached to the root logger. If not
//! # specified, defaults to an empty list.
//! appenders = ["foo"]
//!
//! # Loggers are configured as tables inside of the "logger" array.
//! [[logger]]
//! # The name of the logger. Must be specified.
//! name = "foo::bar::baz"
//!
//! # The maximum . If it is not present, the level of the logger's parent is used.
//! level = "trace"
//!
//! # A list of names of appenders attached to the logger. If not specified,
//! # defaults to an empty list.
//! appenders = ["foo"]
//!
//! # The additivity of the logger. If true, the appenders attached to this
//! # logger's parent will also be attached to this logger. If not specified,
//! # defaults to true.
//! additive = false
//! ```
use log::LogLevelFilter;
use std::collections::HashMap;
use std::default::Default;
use std::error;
use std::fmt;
use std::time::Duration;
use toml_parser::{self, Value};

use appender::{FileAppender, ConsoleAppender};
use filter::{ThresholdFilter};
use config;
use pattern::PatternLayout;
use {Append, Filter, PrivateTomlConfigExt, PrivateConfigErrorsExt};

mod raw;

/// A trait implemented by types that can create appenders.
pub trait CreateAppender: Send + 'static {
    /// Creates an appender with the specified config.
    fn create_appender(&self, config: toml_parser::Table)
                       -> Result<Box<Append>, Box<error::Error>>;
}

/// A trait implemented by types that can create filters.
pub trait CreateFilter: Send + 'static {
    /// Creates a filter with the specified config.
    fn create_filter(&self, config: toml_parser::Table)
                     -> Result<Box<Filter>, Box<error::Error>>;
}

/// A type that can create appenders.
///
/// `Creator` implements `Default`, which returns a `Creator` with the
/// following mappings:
///
/// * Appenders
///     * "file" -> `FileAppenderCreator`
///     * "console" -> `ConsoleAppenderCreator`
/// * Filters
///     * "threshold" -> `ThresholdFilterCreator`
pub struct Creator {
    appenders: HashMap<String, Box<CreateAppender>>,
    filters: HashMap<String, Box<CreateFilter>>,
}

impl Default for Creator {
    fn default() -> Creator {
        let mut creator = Creator::new();
        creator.add_appender("file", Box::new(FileAppenderCreator));
        creator.add_appender("console", Box::new(ConsoleAppenderCreator));
        creator.add_filter("threshold", Box::new(ThresholdFilterCreator));
        creator
    }
}

impl Creator {
    /// Creates a new `Creator` with no appender or filter mappings.
    pub fn new() -> Creator {
        Creator {
            appenders: HashMap::new(),
            filters: HashMap::new(),
        }
    }

    /// Adds a mapping from the specified `kind` to the specified appender
    /// creator.
    pub fn add_appender(&mut self, kind: &str, creator: Box<CreateAppender>) {
        self.appenders.insert(kind.to_string(), creator);
    }

    /// Adds a mapping from the specified `kind` to the specified filter
    /// creator.
    pub fn add_filter(&mut self, kind: &str, creator: Box<CreateFilter>) {
        self.filters.insert(kind.to_string(), creator);
    }

    fn create_appender(&self, kind: &str, config: toml_parser::Table)
                       -> Result<Box<Append>, Box<error::Error>> {
        match self.appenders.get(kind) {
            Some(creator) => creator.create_appender(config),
            None => Err(Box::new(StringError(format!("No creator registered for appender kind \"{}\"", kind))))
        }
    }

    fn create_filter(&self, kind: &str, config: toml_parser::Table)
                     -> Result<Box<Filter>, Box<error::Error>> {
        match self.filters.get(kind) {
            Some(creator) => creator.create_filter(config),
            None => Err(Box::new(StringError(format!("No creator registered for filter kind \"{}\"", kind))))
        }
    }
}

/// Errors encountered when parsing a log4rs TOML config.
#[derive(Debug)]
pub struct ParseErrors {
    errors: Vec<String>,
}

impl fmt::Display for ParseErrors {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        for error in &self.errors {
            try!(writeln!(fmt, "{}", error));
        }
        Ok(())
    }
}

impl error::Error for ParseErrors {
    fn description(&self) -> &str {
        "Errors encountered when parsing a log4rs TOML config"
    }
}

/// An error returned when deserializing a TOML configuration into a log4rs `Config`.
#[derive(Debug)]
pub enum Error {
    /// An error instantiating an appender.
    AppenderCreation(String, Box<error::Error>),
    /// An error instantiating a filter.
    FilterCreation(String, Box<error::Error>),
    /// An error when creating the log4rs `Config`.
    Config(config::Error),
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::AppenderCreation(ref appender, ref err) => {
                write!(fmt, "Error creating appender `{}`: {}", appender, err)
            }
            Error::FilterCreation(ref appender, ref err) => {
                write!(fmt, "Error creating filter for appender `{}`: {}", appender, err)
            }
            Error::Config(ref err) => write!(fmt, "Error creating config: {}", err),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        "An error encountered when deserializing a TOML configuration into a log4rs `Config`"
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::AppenderCreation(_, ref err) => Some(&**err),
            Error::FilterCreation(_, ref err) => Some(&**err),
            Error::Config(ref err) => Some(err),
        }
    }
}

/// Errors encountered when deserializing a TOML configuration into a log4rs `Config`.
#[derive(Debug)]
pub struct Errors {
    errors: Vec<Error>,
}

impl Errors {
    /// Returns the list of errors encountered.
    pub fn errors(&self) -> &[Error] {
        &self.errors
    }
}

impl fmt::Display for Errors {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        for error in &self.errors {
            try!(writeln!(fmt, "{}", error));
        }
        Ok(())
    }
}

impl error::Error for Errors {
    fn description(&self) -> &str {
        "Errors encountered when deserializing a TOML configuration into a log4rs `Config`"
    }
}

/// A deserialized TOML log4rs configuration.
pub struct Config {
    refresh_rate: Option<Duration>,
    config: config::Config,
}

impl Config {
    /// Creates a log4rs `Config` from the specified TOML config string and `Creator`.
    pub fn parse(config: &str, creator: &Creator)
                 -> Result<(Config, Result<(), Errors>), ParseErrors> {
        let mut errors = vec![];

        let config = match raw::parse(config) {
            Ok(config) => config,
            Err(errors) => return Err(ParseErrors { errors: errors }),
        };

        let raw::Config {
            refresh_rate,
            root: raw_root,
            appenders: raw_appenders,
            loggers: raw_loggers,
        } = config;

        let root = match raw_root {
            Some(raw_root) => {
                let mut root = config::Root::builder(raw_root.level);
                if let Some(appenders) = raw_root.appenders {
                    root = root.appenders(appenders);
                }
                root.build()
            }
            None => config::Root::builder(LogLevelFilter::Debug).build(),
        };

        let mut config = config::Config::builder(root);

        for (name, raw::Appender { kind, config: raw_config, filters }) in raw_appenders {
            match creator.create_appender(&kind, raw_config) {
                Ok(appender_obj) => {
                    let mut builder = config::Appender::builder(name.clone(), appender_obj);
                    for raw::Filter { kind, config } in filters.unwrap_or(vec![]) {
                        match creator.create_filter(&kind, config) {
                            Ok(filter) => builder = builder.filter(filter),
                            Err(err) => errors.push(Error::FilterCreation(name.clone(), err)),
                        }
                    }
                    config = config.appender(builder.build());
                }
                Err(err) => errors.push(Error::AppenderCreation(name, err)),
            }
        }

        for logger in raw_loggers {
            let raw::Logger { name, level, appenders, additive } = logger;
            let mut logger = config::Logger::builder(name, level);
            if let Some(appenders) = appenders {
                logger = logger.appenders(appenders);
            }
            if let Some(additive) = additive {
                logger = logger.additive(additive);
            }
            config = config.logger(logger.build());
        }

        let (config, config_errors) = config.build_lossy();
        if let Err(config_errors) = config_errors {
            for error in config_errors.unpack() {
                errors.push(Error::Config(error));
            }
        }

        let config = Config {
            refresh_rate: refresh_rate,
            config: config
        };

        let errors = if errors.is_empty() {
            Ok(())
        } else {
            Err(Errors {
                errors: errors
            })
        };

        Ok((config, errors))
    }

    /// Returns the requested refresh rate.
    pub fn refresh_rate(&self) -> Option<Duration> {
        self.refresh_rate
    }

    /// Returns the log4rs `Config`.
    pub fn config(&self) -> &config::Config {
        &self.config
    }
}

impl PrivateTomlConfigExt for Config {
    fn unpack(self) -> (Option<Duration>, config::Config) {
        let Config { refresh_rate, config } = self;
        (refresh_rate, config)
    }
}

#[derive(Debug)]
struct StringError(String);

impl fmt::Display for StringError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(&self.0)
    }
}

impl error::Error for StringError {
    fn description(&self) -> &str {
        &self.0
    }
}

impl error::FromError<String> for StringError {
    fn from_error(s: String) -> StringError {
        StringError(s)
    }
}

/// An appender creator for the `FileAppender`.
///
/// The `path` key is required, and specifies the path to the log file. The
/// `pattern` key is optional and specifies a `PatternLayout` pattern to be
/// used for output. The `append` key is optional and specifies whether the
/// output file should be truncated or appended to.
pub struct FileAppenderCreator;

impl CreateAppender for FileAppenderCreator {
    fn create_appender(&self, mut config: toml_parser::Table)
                       -> Result<Box<Append>, Box<error::Error>> {
        let path = match config.remove("path") {
            Some(Value::String(path)) => path,
            Some(_) => return Err(Box::new(StringError("`path` must be a string".to_string()))),
            None => return Err(Box::new(StringError("`path` is required".to_string()))),
        };

        let mut appender = FileAppender::builder(&path);
        match config.remove("pattern") {
            Some(Value::String(pattern)) => {
                appender = appender.pattern(try!(PatternLayout::new(&pattern)));
            }
            Some(_) => return Err(Box::new(StringError("`pattern` must be a string".to_string()))),
            None => {}
        }

        match config.remove("append") {
            Some(Value::Boolean(append)) => appender = appender.append(append),
            None => {}
            Some(_) => return Err(Box::new(StringError("`append` must be a bool".to_string()))),
        }

        match appender.build() {
            Ok(appender) => Ok(Box::new(appender)),
            Err(err) => Err(Box::new(err))
        }
    }
}

/// An appender creator for the `ConsoleAppender`.
///
/// The `pattern` key is optional and specifies a `PatternLayout` pattern to be
/// used for output.
pub struct ConsoleAppenderCreator;

impl CreateAppender for ConsoleAppenderCreator {
    fn create_appender(&self, mut config: toml_parser::Table)
                       -> Result<Box<Append>, Box<error::Error>> {
        let mut appender = ConsoleAppender::builder();
        match config.remove("pattern") {
            Some(Value::String(pattern)) => {
                appender = appender.pattern(try!(PatternLayout::new(&pattern)));
            }
            Some(_) => return Err(Box::new(StringError("`pattern` must be a string".to_string()))),
            None => {}
        }

        Ok(Box::new(appender.build()))
    }
}

/// A filter creator for the `ThresholdFilter`.
///
/// The `level` key is required and specifies the threshold for the filter.
pub struct ThresholdFilterCreator;

impl CreateFilter for ThresholdFilterCreator {
    fn create_filter(&self, mut config: toml_parser::Table)
                     -> Result<Box<Filter>, Box<error::Error>> {
        let level = match config.remove("level") {
            Some(Value::String(level)) => level,
            Some(_) => return Err(Box::new(StringError("`level` must be a string".to_string()))),
            None => return Err(Box::new(StringError("`level` must be provided".to_string()))),
        };

        let level = match level.parse() {
            Ok(level) => level,
            Err(_) => return Err(Box::new(StringError(format!("Invalid `level` \"{}\"", level)))),
        };

        Ok(Box::new(ThresholdFilter::new(level)))
    }
}
<|endoftext|>


<commit_before>use platform::time::time_now;
use storage::CacheError;

use super::cmd::Resp;

pub fn bytes_to_u64(bytes: &Vec<u8>) -> Option<u64> {
    match String::from_utf8(bytes.clone()) {
        Ok(st) => {
            match st.parse::<u64>() {
                Ok(num) => Some(num),
                Err(_) => None,
            }
        }
        Err(_) => None,
    }
}

pub fn u64_to_bytes(num: &u64) -> Vec<u8> {
    let arr = num.to_string().into_bytes();

    let mut bytes = vec![];
    bytes.extend(arr);
    bytes
}

pub fn convert_exptime(exptime: u32) -> Option<f64> {
    // If exptime is greater than zero it means it's set, otherwise unset
    if exptime > 0 {
        let tm;

        // Is it an interval greater than 30 days? Then it's a timestamp
        if exptime > 60 * 60 * 24 * 30 {
            tm = exptime as f64;

        } else {
            // Otherwise it's relative from now
            tm = time_now() + exptime as f64;
        }

        return Some(tm);
    }

    None
}

pub fn from_cache_err(err: &CacheError) -> Resp {
    match *err {
        CacheError::KeyTooLong => {
            Resp::ClientError("bad command line format".to_string())
        }
        CacheError::ValueTooLong => {
            Resp::ServerError("object too large for cache".to_string())
        }
        _ => Resp::Error,
    }
}

#[test]
fn test_bytes_to_u64() {
    // whitespace
    assert_eq!(None, bytes_to_u64(&vec![b' ', b'2']));

    // alpha
    assert_eq!(None, bytes_to_u64(&vec![b'0', b'x', b'2']));

    // negative
    assert_eq!(None, bytes_to_u64(&vec![b'-', b'2']));

    // too long for u64
    assert_eq!(None, bytes_to_u64(&vec![b'1'; 255]));

    // ok
    assert_eq!(12, bytes_to_u64(&vec![b'1', b'2']).unwrap());
}

#[test]
fn test_u64_to_bytes() {
    // any u64 is representable as bytes so there are no boundary conditions to
    // check
    assert_eq!(vec![b'1', b'2'], u64_to_bytes(&12));
}
<commit_msg>add dedicated tests module<commit_after>use platform::time::time_now;
use storage::CacheError;

use super::cmd::Resp;

pub fn bytes_to_u64(bytes: &Vec<u8>) -> Option<u64> {
    match String::from_utf8(bytes.clone()) {
        Ok(st) => {
            match st.parse::<u64>() {
                Ok(num) => Some(num),
                Err(_) => None,
            }
        }
        Err(_) => None,
    }
}

pub fn u64_to_bytes(num: &u64) -> Vec<u8> {
    let arr = num.to_string().into_bytes();

    let mut bytes = vec![];
    bytes.extend(arr);
    bytes
}

pub fn convert_exptime(exptime: u32) -> Option<f64> {
    // If exptime is greater than zero it means it's set, otherwise unset
    if exptime > 0 {
        let tm;

        // Is it an interval greater than 30 days? Then it's a timestamp
        if exptime > 60 * 60 * 24 * 30 {
            tm = exptime as f64;

        } else {
            // Otherwise it's relative from now
            tm = time_now() + exptime as f64;
        }

        return Some(tm);
    }

    None
}

pub fn from_cache_err(err: &CacheError) -> Resp {
    match *err {
        CacheError::KeyTooLong => {
            Resp::ClientError("bad command line format".to_string())
        }
        CacheError::ValueTooLong => {
            Resp::ServerError("object too large for cache".to_string())
        }
        _ => Resp::Error,
    }
}

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

    #[test]
    fn test_bytes_to_u64() {
        // whitespace
        assert_eq!(None, bytes_to_u64(&vec![b' ', b'2']));

        // alpha
        assert_eq!(None, bytes_to_u64(&vec![b'0', b'x', b'2']));

        // negative
        assert_eq!(None, bytes_to_u64(&vec![b'-', b'2']));

        // too long for u64
        assert_eq!(None, bytes_to_u64(&vec![b'1'; 255]));

        // ok
        assert_eq!(12, bytes_to_u64(&vec![b'1', b'2']).unwrap());
    }

    #[test]
    fn test_u64_to_bytes() {
        // any u64 is representable as bytes so there are no boundary conditions to
        // check
        assert_eq!(vec![b'1', b'2'], u64_to_bytes(&12));
    }
}
<|endoftext|>


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

//! A dbus proxy for speaking with secret service's `Service` Interface.

use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use zbus;
use zbus_macros::dbus_proxy;
use zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Value};
use zvariant_derive::Type;

use super::SecretStruct;

/// A dbus proxy for speaking with secret service's `Service` Interface.
///
/// This will derive ServiceInterfaceProxy
///
/// Note that `Value` in the method signatures corresponds to `VARIANT` dbus type.
#[dbus_proxy(
    interface = "org.freedesktop.Secret.Service",
)]
trait ServiceInterface {
    fn open_session(&self, algorithm: String, input: Value) -> zbus::Result<OpenSessionResult>;

    fn create_collection(&self, properties: HashMap<String, Value>, alias: String) -> zbus::Result<CreateCollectionResult>;

    fn search_items(&self, attributes: HashMap<String, String>) -> zbus::Result<SearchItemsResult>;

    fn unlock(&self, objects: Vec<ObjectPath>) -> zbus::Result<UnlockResult>;

    fn lock(&self, objects: Vec<ObjectPath>) -> zbus::Result<LockResult>;

    fn get_secrets(&self, objects: Vec<ObjectPath>) -> zbus::Result<HashMap<OwnedObjectPath, SecretStruct>>;

    /// Returns collection
    fn read_alias(&self, name: String) -> zbus::Result<OwnedObjectPath>;

    fn set_alias(&self, name: String, collection: ObjectPath) -> zbus::Result<()>;

    /// Returns collections
    /// TODO patch zvariant to allow Vec<OwnedObjectPath>
    #[dbus_proxy(property)]
    fn collections(&self) -> zbus::fdo::Result<zvariant::Array>;
}

#[derive(Debug, Serialize, Deserialize, Type)]
pub struct OpenSessionResult {
    pub(crate) output: OwnedValue,
    pub(crate) result: OwnedObjectPath,
}

#[derive(Debug, Serialize, Deserialize, Type)]
pub struct CreateCollectionResult {
    pub(crate) collection: OwnedObjectPath,
    pub(crate) prompt: OwnedObjectPath,
}

#[derive(Debug, Serialize, Deserialize, Type)]
pub struct SearchItemsResult {
    pub(crate) unlocked: Vec<OwnedObjectPath>,
    pub(crate) locked: Vec<OwnedObjectPath>,
}

#[derive(Debug, Serialize, Deserialize, Type)]
pub struct UnlockResult {
    pub(crate) unlocked: Vec<OwnedObjectPath>,
    pub(crate) prompt: OwnedObjectPath,
}

#[derive(Debug, Serialize, Deserialize, Type)]
pub struct LockResult {
    pub(crate) locked: Vec<OwnedObjectPath>,
    pub(crate) prompt: OwnedObjectPath,
}
<commit_msg>question on returning Vec<ObjectPath> instead of Vec<OwnedObjectPath><commit_after>//Copyright 2020 secret-service-rs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! A dbus proxy for speaking with secret service's `Service` Interface.

use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use zbus;
use zbus_macros::dbus_proxy;
use zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Value};
use zvariant_derive::Type;

use super::SecretStruct;

/// A dbus proxy for speaking with secret service's `Service` Interface.
///
/// This will derive ServiceInterfaceProxy
///
/// Note that `Value` in the method signatures corresponds to `VARIANT` dbus type.
#[dbus_proxy(
    interface = "org.freedesktop.Secret.Service",
)]
trait ServiceInterface {
    fn open_session(&self, algorithm: String, input: Value) -> zbus::Result<OpenSessionResult>;

    fn create_collection(&self, properties: HashMap<String, Value>, alias: String) -> zbus::Result<CreateCollectionResult>;

    fn search_items(&self, attributes: HashMap<String, String>) -> zbus::Result<SearchItemsResult>;

    fn unlock(&self, objects: Vec<ObjectPath>) -> zbus::Result<UnlockResult>;

    fn lock(&self, objects: Vec<ObjectPath>) -> zbus::Result<LockResult>;

    fn get_secrets(&self, objects: Vec<ObjectPath>) -> zbus::Result<HashMap<OwnedObjectPath, SecretStruct>>;

    /// Returns collection
    fn read_alias(&self, name: String) -> zbus::Result<OwnedObjectPath>;

    fn set_alias(&self, name: String, collection: ObjectPath) -> zbus::Result<()>;

    /// Returns collections
    // TODO Not sure why ObjectPath would be allowed but OwnedObjectPath would not be? Should Owned
    // be preferred here?
    #[dbus_proxy(property)]
    fn collections(&self) -> zbus::fdo::Result<Vec<ObjectPath>>;
}

#[derive(Debug, Serialize, Deserialize, Type)]
pub struct OpenSessionResult {
    pub(crate) output: OwnedValue,
    pub(crate) result: OwnedObjectPath,
}

#[derive(Debug, Serialize, Deserialize, Type)]
pub struct CreateCollectionResult {
    pub(crate) collection: OwnedObjectPath,
    pub(crate) prompt: OwnedObjectPath,
}

#[derive(Debug, Serialize, Deserialize, Type)]
pub struct SearchItemsResult {
    pub(crate) unlocked: Vec<OwnedObjectPath>,
    pub(crate) locked: Vec<OwnedObjectPath>,
}

#[derive(Debug, Serialize, Deserialize, Type)]
pub struct UnlockResult {
    pub(crate) unlocked: Vec<OwnedObjectPath>,
    pub(crate) prompt: OwnedObjectPath,
}

#[derive(Debug, Serialize, Deserialize, Type)]
pub struct LockResult {
    pub(crate) locked: Vec<OwnedObjectPath>,
    pub(crate) prompt: OwnedObjectPath,
}
<|endoftext|>