<commit_before>use core::cell::Cell;
use hil::{Callback, Driver, NUM_PROCS};
use hil::alarm::{Alarm, AlarmClient, Frequency};
use hil::timer::{Timer, TimerClient};

#[derive(Copy, Clone)]
enum Schedule {
    Oneshot,
    Repeating { interval: u32 }
}

pub struct AlarmToTimer<'a, Alrm: Alarm + 'a> {
    schedule: Cell<Schedule>,
    alarm: &'a Alrm,
    client: Cell<Option<&'a TimerClient>>
}

impl<'a, Alrm: Alarm> AlarmToTimer<'a, Alrm> {
    pub const fn new(alarm: &'a Alrm) -> AlarmToTimer<'a, Alrm> {
        AlarmToTimer {
            schedule: Cell::new(Schedule::Oneshot),
            alarm: alarm,
            client: Cell::new(None)
        }
    }

    pub fn set_client(&self, client: &'a TimerClient) {
        self.client.set(Some(client));
    }
}

impl<'a, Alrm: Alarm> Timer for AlarmToTimer<'a, Alrm> {
    fn now(&self) -> u32 {
        self.alarm.now()
    }

    fn oneshot(&self, interval_ms: u32) {
        let interval = interval_ms * <Alrm::Frequency>::frequency() / 1000;

        self.schedule.set(Schedule::Oneshot);

        let when = interval.wrapping_add(self.alarm.now());
        self.alarm.set_alarm(when);
    }

    #[inline(never)]
    fn repeat(&self, interval_ms: u32) {
        let interval = interval_ms * <Alrm::Frequency>::frequency() / 2;

        self.schedule.set(Schedule::Repeating {interval: interval});

        let when = interval.wrapping_add(self.alarm.now());
        self.alarm.set_alarm(when);
    }
}

impl<'a, Alrm: Alarm> AlarmClient for AlarmToTimer<'a, Alrm> {
    fn fired(&self) {
        let now = self.now();

        match self.schedule.get() {
            Schedule::Oneshot => self.alarm.disable_alarm(),

            Schedule::Repeating { interval } => {
                let when = interval.wrapping_add(now);
                self.alarm.set_alarm(when);
            }
        }

        self.client.get().map(|client| client.fired(now) );
    }
}

#[derive(Copy, Clone)]
struct TimerData {
    t0: u32,
    interval: u32,
    repeating: bool,
    callback: Callback
}

pub struct TimerDriver<'a, T: Timer + 'a> {
    timer: &'a T,
    app_timers: [Cell<Option<TimerData>>; NUM_PROCS]
}

impl<'a, T: Timer> TimerDriver<'a, T> {
    pub const fn new(timer: &'a T) -> TimerDriver<'a, T> {
        TimerDriver {
            timer: timer,
            app_timers: [Cell::new(None); NUM_PROCS]
        }
    }
}

impl<'a, T: Timer> Driver for TimerDriver<'a, T> {
    fn subscribe(&self, _: usize, callback: Callback) -> isize {
        self.app_timers[callback.app_id().idx()].set(Some(TimerData {
            t0: 0,
            interval: 0,
            repeating: false,
            callback: callback
        }));
        0
    }

    fn command(&self, cmd_type: usize, interval: usize) -> isize {
        let interval = interval as u32;
        self.app_timers[0].get().map(|td| {
            match cmd_type {
                0 /* Oneshot */ => {
                    self.app_timers[0].set(
                            Some(TimerData {
                                t0: self.timer.now(),
                                interval: interval,
                                repeating: false,
                                callback: td.callback
                            })
                    );
                    self.timer.oneshot(interval);
                    0
                },
                1 /* Repeating */ => {
                    self.app_timers[0].set(
                            Some(TimerData {
                                t0: self.timer.now(),
                                interval: interval,
                                repeating: true,
                                callback: td.callback
                            })
                    );
                    self.timer.repeat(interval);
                    0
                },
                _ => -1
            }
        }).unwrap_or(-2)
    }
}

impl<'a, T: Timer> TimerClient for TimerDriver<'a, T> {
    fn fired(&self, now: u32) {
        for mtimer in self.app_timers.iter() {
            mtimer.get().map(|timer| {
                let elapsed = now.wrapping_sub(timer.t0);
                if elapsed >= timer.interval {
                    let mut cb = timer.callback;
                    cb.schedule(now as usize, 0, 0);
                }
            });
        }
    }
}

<commit_msg>Fix repeating timer driver<commit_after>use core::cell::Cell;
use hil::{Callback, Driver, NUM_PROCS};
use hil::alarm::{Alarm, AlarmClient, Frequency};
use hil::timer::{Timer, TimerClient};

#[derive(Copy, Clone)]
enum Schedule {
    Oneshot,
    Repeating { interval: u32 }
}

pub struct AlarmToTimer<'a, Alrm: Alarm + 'a> {
    schedule: Cell<Schedule>,
    alarm: &'a Alrm,
    client: Cell<Option<&'a TimerClient>>
}

impl<'a, Alrm: Alarm> AlarmToTimer<'a, Alrm> {
    pub const fn new(alarm: &'a Alrm) -> AlarmToTimer<'a, Alrm> {
        AlarmToTimer {
            schedule: Cell::new(Schedule::Oneshot),
            alarm: alarm,
            client: Cell::new(None)
        }
    }

    pub fn set_client(&self, client: &'a TimerClient) {
        self.client.set(Some(client));
    }
}

impl<'a, Alrm: Alarm> Timer for AlarmToTimer<'a, Alrm> {
    fn now(&self) -> u32 {
        self.alarm.now()
    }

    fn oneshot(&self, interval_ms: u32) {
        let interval = interval_ms * <Alrm::Frequency>::frequency() / 1000;

        self.schedule.set(Schedule::Oneshot);

        let when = interval.wrapping_add(self.alarm.now());
        self.alarm.set_alarm(when);
    }

    #[inline(never)]
    fn repeat(&self, interval_ms: u32) {
        let interval = interval_ms * <Alrm::Frequency>::frequency() / 1000;

        self.schedule.set(Schedule::Repeating {interval: interval});

        let when = interval.wrapping_add(self.alarm.now());
        self.alarm.set_alarm(when);
    }
}

impl<'a, Alrm: Alarm> AlarmClient for AlarmToTimer<'a, Alrm> {
    fn fired(&self) {
        let now = self.now();

        match self.schedule.get() {
            Schedule::Oneshot => self.alarm.disable_alarm(),

            Schedule::Repeating { interval } => {
                let when = interval.wrapping_add(now);
                self.alarm.set_alarm(when);
            }
        }

        self.client.get().map(|client| client.fired(now) );
    }
}

#[derive(Copy, Clone)]
struct TimerData {
    t0: u32,
    interval: u32,
    repeating: bool,
    callback: Callback
}

pub struct TimerDriver<'a, T: Timer + 'a> {
    timer: &'a T,
    app_timers: [Cell<Option<TimerData>>; NUM_PROCS]
}

impl<'a, T: Timer> TimerDriver<'a, T> {
    pub const fn new(timer: &'a T) -> TimerDriver<'a, T> {
        TimerDriver {
            timer: timer,
            app_timers: [Cell::new(None); NUM_PROCS]
        }
    }
}

impl<'a, T: Timer> Driver for TimerDriver<'a, T> {
    fn subscribe(&self, _: usize, callback: Callback) -> isize {
        self.app_timers[callback.app_id().idx()].set(Some(TimerData {
            t0: 0,
            interval: 0,
            repeating: false,
            callback: callback
        }));
        0
    }

    fn command(&self, cmd_type: usize, interval: usize) -> isize {
        let interval = interval as u32;
        self.app_timers[0].get().map(|td| {
            match cmd_type {
                0 /* Oneshot */ => {
                    self.app_timers[0].set(
                            Some(TimerData {
                                t0: self.timer.now(),
                                interval: interval,
                                repeating: false,
                                callback: td.callback
                            })
                    );
                    self.timer.oneshot(interval);
                    0
                },
                1 /* Repeating */ => {
                    self.app_timers[0].set(
                            Some(TimerData {
                                t0: self.timer.now(),
                                interval: interval,
                                repeating: true,
                                callback: td.callback
                            })
                    );
                    self.timer.repeat(interval);
                    0
                },
                _ => -1
            }
        }).unwrap_or(-2)
    }
}

impl<'a, T: Timer> TimerClient for TimerDriver<'a, T> {
    fn fired(&self, now: u32) {
        for mtimer in self.app_timers.iter() {
            mtimer.get().map(|timer| {
                let elapsed = now.wrapping_sub(timer.t0);
                if elapsed >= timer.interval {
                    let mut cb = timer.callback;
                    cb.schedule(now as usize, 0, 0);
                }
            });
        }
    }
}

<|endoftext|>


<commit_before>#![feature(macro_rules)]

extern crate tgff;
extern crate test;

use std::io::fs::PathExtensions;
use std::io::File;
use tgff::Parser;

macro_rules! assert_ok(
    ($result: expr) => (
        if let Err(err) = $result {
            assert!(false, "{}", err);
        }
    );
)

macro_rules! assert_within(
    ($one:expr, $two:expr, $delta:expr) => (
        assert!(std::num::abs($one - $two) < $delta)
    );
)

macro_rules! assert_close(
    ($one:expr, $two:expr) => (assert_within!($one, $two, 1e-8));
)

#[test]
fn parser_process() {
    let content = read_fixture("simple.tgff");
    let mut parser = Parser::new(content.as_slice());
    let r = parser.process().unwrap();

    assert_eq!(r.attributes["HYPERPERIOD".to_string()], 1180);
    assert_eq!(r.graphs.len(), 5);
    assert_eq!(r.tables.len(), 3);

    let periods = vec![590, 1180, 1180, 590, 1180];
    let tasks = vec![12, 20, 24, 8, 20];
    let arcs = vec![19, 25, 28, 7, 24];
    let deadlines = vec![1, 6, 8, 3, 6];

    for i in range(0u, 5) {
        assert_eq!(r.graphs[i].name, "TASK_GRAPH".to_string());
        assert_eq!(r.graphs[i].id, i);
        assert_eq!(r.graphs[i].attributes["PERIOD".to_string()], periods[i]);
        assert_eq!(r.graphs[i].tasks.len(), tasks[i]);
        assert_eq!(r.graphs[i].arcs.len(), arcs[i]);
        assert_eq!(r.graphs[i].deadlines.len(), deadlines[i]);
    }

    let prices = vec![70.1121, 71.4235, 80.491];

    for i in range(0u, 3) {
        assert_eq!(r.tables[i].name, "COMMUN".to_string());
        assert_eq!(r.tables[i].id, i);
        assert_close!(r.tables[i].attributes["price".to_string()], prices[i]);
        assert_eq!(r.tables[i].columns.len(), 2);
        assert_eq!(r.tables[i].columns[0].name, "type".to_string());
        assert_eq!(r.tables[i].columns[1].name, "exec_time".to_string());
    }

    let data = vec![
        48.5893, 33.4384, 34.2468, 51.2027, 51.3571,
        30.3827, 43.3982, 60.9097, 36.0322, 34.7446,
        45.3479, 31.7221, 49.6842, 52.0635, 44.7690,
        37.7183, 54.7523, 58.4432, 33.1266, 48.2143,
        31.2946, 45.9168, 36.4521, 61.6448, 49.4966,
        37.1130, 40.1642, 38.9454, 41.6213, 42.1084,
        42.4186, 42.5145, 34.4180, 33.4178, 32.4243,
        63.7925, 50.3810, 51.9030, 46.4714, 35.0566,
        41.8399, 30.1513, 31.7449, 57.3263, 61.2321,
        44.9932, 32.0830, 37.9489, 62.4774, 39.2500,
    ];

    for i in range(0u, data.len()) {
        assert_close!(r.tables[1].columns[1].data[i], data[i]);
    }
}

fn read_fixture(name: &'static str) -> String {
    let path = Path::new("tests").join_many(["fixtures", name]);
    assert!(path.exists());
    File::open(&path).read_to_string().unwrap()
}
<commit_msg>Added a complex test case<commit_after>#![feature(macro_rules)]

extern crate tgff;
extern crate test;

use std::io::fs::PathExtensions;
use std::io::File;
use tgff::Parser;

macro_rules! assert_ok(
    ($result: expr) => (
        if let Err(err) = $result {
            assert!(false, "{}", err);
        }
    );
)

macro_rules! assert_within(
    ($one:expr, $two:expr, $delta:expr) => (
        assert!(std::num::abs($one - $two) < $delta)
    );
)

macro_rules! assert_close(
    ($one:expr, $two:expr) => (assert_within!($one, $two, 1e-8));
)

#[test]
fn parser_process_simple() {
    let content = read_fixture("simple.tgff");
    let mut parser = Parser::new(content.as_slice());
    let r = parser.process().unwrap();

    assert_eq!(r.attributes["HYPERPERIOD".to_string()], 1180);
    assert_eq!(r.graphs.len(), 5);
    assert_eq!(r.tables.len(), 3);

    let periods = vec![590, 1180, 1180, 590, 1180];
    let tasks = vec![12, 20, 24, 8, 20];
    let arcs = vec![19, 25, 28, 7, 24];
    let deadlines = vec![1, 6, 8, 3, 6];

    for i in range(0u, 5) {
        assert_eq!(r.graphs[i].name, "TASK_GRAPH".to_string());
        assert_eq!(r.graphs[i].id, i);
        assert_eq!(r.graphs[i].attributes["PERIOD".to_string()], periods[i]);
        assert_eq!(r.graphs[i].tasks.len(), tasks[i]);
        assert_eq!(r.graphs[i].arcs.len(), arcs[i]);
        assert_eq!(r.graphs[i].deadlines.len(), deadlines[i]);
    }

    let prices = vec![70.1121, 71.4235, 80.491];

    for i in range(0u, 3) {
        assert_eq!(r.tables[i].name, "COMMUN".to_string());
        assert_eq!(r.tables[i].id, i);
        assert_close!(r.tables[i].attributes["price".to_string()], prices[i]);
        assert_eq!(r.tables[i].columns.len(), 2);
        assert_eq!(r.tables[i].columns[0].name, "type".to_string());
        assert_eq!(r.tables[i].columns[1].name, "exec_time".to_string());
    }

    let data = vec![
        48.5893, 33.4384, 34.2468, 51.2027, 51.3571,
        30.3827, 43.3982, 60.9097, 36.0322, 34.7446,
        45.3479, 31.7221, 49.6842, 52.0635, 44.7690,
        37.7183, 54.7523, 58.4432, 33.1266, 48.2143,
        31.2946, 45.9168, 36.4521, 61.6448, 49.4966,
        37.1130, 40.1642, 38.9454, 41.6213, 42.1084,
        42.4186, 42.5145, 34.4180, 33.4178, 32.4243,
        63.7925, 50.3810, 51.9030, 46.4714, 35.0566,
        41.8399, 30.1513, 31.7449, 57.3263, 61.2321,
        44.9932, 32.0830, 37.9489, 62.4774, 39.2500,
    ];

    for i in range(0u, data.len()) {
        assert_close!(r.tables[1].columns[1].data[i], data[i]);
    }
}

#[test]
fn parser_process_complex() {
    let content = read_fixture("032_640.tgff");
    let mut parser = Parser::new(content.as_slice());
    let r = parser.process().unwrap();

    assert_eq!(r.graphs.len(), 1);
    assert_eq!(r.graphs[0].tasks.len(), 640);
    assert_eq!(r.graphs[0].arcs.len(), 848);
    assert_eq!(r.graphs[0].deadlines.len(), 259);

    assert_eq!(r.tables.len(), 32);
    for table in r.tables.iter() {
        assert_eq!(table.attributes.len(), 1);
        assert_eq!(table.columns.len(), 4);
        for column in table.columns.iter() {
            assert_eq!(column.data.len(), 320);
        }
    }
}

fn read_fixture(name: &'static str) -> String {
    let path = Path::new("tests").join_many(["fixtures", name]);
    assert!(path.exists());
    File::open(&path).read_to_string().unwrap()
}
<|endoftext|>


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

use std::{
    collections::HashMap,
    fmt::Debug,
    os::unix::io::RawFd,
    path::{Path, PathBuf},
};

use chrono::{DateTime, Utc};
use serde_json::Value;
use uuid::Uuid;

use devicemapper::{Bytes, Sectors, DM};

use crate::{
    engine::types::{
        BlockDevTier, CreateAction, DeleteAction, DevUuid, EncryptionInfo, FilesystemUuid,
        KeyDescription, MappingCreateAction, MaybeDbusPath, Name, PoolUuid, RenameAction,
        ReportType, SetCreateAction, SetDeleteAction, SetUnlockAction, UnlockMethod,
    },
    stratis::StratisResult,
};

pub const DEV_PATH: &str = "/dev/stratis";
/// The maximum size of pool passphrases stored in the kernel keyring
pub const MAX_STRATIS_PASS_SIZE: usize = 512 / 8;

pub trait KeyActions {
    /// Set a key in the kernel keyring. The output is an idempotent return type
    /// containing a `bool` which indicates whether a key with the requested
    /// key description was in the keyring and the key data was updated.
    ///
    /// If `interactive` is `Some(_)`, the end of a passphrase should be delimited
    /// by a newline.
    ///
    /// If `interactive` is `Some(true)`, stratisd will change the terminal settings
    /// on the interactive file descriptor to prompt the user without echo and with
    /// a few additional security measures. This requires that stdin is a terminal.
    ///
    /// If `interactive` is `Some(false)`, it is up to the user to remove the echo
    /// property on the file descriptor and any other settings that they require.
    ///
    /// Successful return values:
    /// * `Ok(MappingCreateAction::Identity)`: The key was already in the keyring
    /// with the appropriate key description and key data.
    /// * `Ok(MappingCreateAction::Created(()))`: The key was newly added to the
    /// keyring.
    /// * `Ok(MappingCreateAction::Changed)`: The key description was already present
    /// in the keyring but the key data was updated.
    fn set(
        &mut self,
        key_desc: &KeyDescription,
        key_fd: RawFd,
    ) -> StratisResult<MappingCreateAction<()>>;

    /// Return a list of all key descriptions of keys added to the keyring by
    /// Stratis that are still valid.
    fn list(&self) -> StratisResult<Vec<KeyDescription>>;

    /// Unset a key with the given key description in the root persistent kernel
    /// keyring.
    fn unset(&mut self, key_desc: &KeyDescription) -> StratisResult<DeleteAction<()>>;
}

/// An interface for reporting internal engine state.
pub trait Report {
    /// Supported engine state report.
    ///
    /// NOTE: The JSON schema for this report is not guaranteed to be stable.
    fn engine_state_report(&self) -> Value;

    /// Unsupported reports. The available reports and JSON schemas of these reports may change.
    fn get_report(&self, report_type: ReportType) -> Value;
}

pub trait Filesystem: Debug {
    /// path of the device node
    fn devnode(&self) -> PathBuf;

    /// When the filesystem was created.
    fn created(&self) -> DateTime<Utc>;

    /// path to mount the filesystem by
    fn path_to_mount_filesystem(&self, pool_name: &str, fs_name: &str) -> PathBuf;

    /// The amount of data stored on the filesystem, including overhead.
    fn used(&self) -> StratisResult<Bytes>;

    /// Set dbus path associated with the Pool.
    fn set_dbus_path(&mut self, path: MaybeDbusPath);

    /// Get dbus path associated with the Pool.
    fn get_dbus_path(&self) -> &MaybeDbusPath;
}

pub trait BlockDev: Debug {
    /// Get the device path for the block device.
    fn devnode(&self) -> &Path;

    /// Get the canonicalized path to display to the user representing this block device.
    fn user_path(&self) -> StratisResult<PathBuf>;

    /// Get the path to the device on which the Stratis metadata is stored.
    fn metadata_path(&self) -> &Path;

    /// Get the user-settable string associated with this blockdev.
    fn user_info(&self) -> Option<&str>;

    /// Get the hardware ID for this blockdev.
    fn hardware_info(&self) -> Option<&str>;

    /// The time that this blockdev was initialized by Stratis, rounded down
    /// to the nearest second.
    fn initialization_time(&self) -> DateTime<Utc>;

    /// The total size of the device, including space not usable for data.
    fn size(&self) -> Sectors;

    /// Set dbus path associated with the BlockDev.
    fn set_dbus_path(&mut self, path: MaybeDbusPath);

    /// Get dbus path associated with the BlockDev.
    fn get_dbus_path(&self) -> &MaybeDbusPath;

    /// Get the status of whether a block device is encrypted or not.
    fn is_encrypted(&self) -> bool;
}

pub trait Pool: Debug {
    /// Initialize the cache with the provided cache block devices.
    /// Returns a list of the the block devices that were actually added as cache
    /// devices. In practice, this will have three types of return values:
    /// * An error if the cache has already been initialized with a different set
    /// of block devices.
    /// * `SetCreateAction::Identity` if the cache has already been initialized with
    /// the same set of block devices.
    /// * `SetCreateAction::Created` containing all provided block devices if the
    /// cache has not yet been initialized.
    ///
    /// This ensures the contract of providing a truly idempotent API as the cache
    /// can only be initialized once and if an attempt is made to initialize it
    /// twice with different sets of block devices, the user should be notified
    /// of their error.
    fn init_cache(
        &mut self,
        pool_uuid: PoolUuid,
        pool_name: &str,
        blockdevs: &[&Path],
    ) -> StratisResult<SetCreateAction<DevUuid>>;

    /// Creates the filesystems specified by specs.
    /// Returns a list of the names of filesystems actually created.
    /// Returns an error if any of the specified names are already in use
    /// for filesystems in this pool. If the same name is passed multiple
    /// times, the size associated with the last item is used.
    fn create_filesystems<'a, 'b>(
        &'a mut self,
        pool_uuid: PoolUuid,
        specs: &[(&'b str, Option<Sectors>)],
    ) -> StratisResult<SetCreateAction<(&'b str, FilesystemUuid)>>;

    /// Adds blockdevs specified by paths to pool.
    /// Returns a list of uuids corresponding to devices actually added.
    /// Returns an error if a blockdev can not be added because it is owned
    /// or there was an error while reading or writing a blockdev.
    /// Also return an error if the tier specified is Cache, and the cache
    /// is not yet initialized.
    fn add_blockdevs(
        &mut self,
        pool_uuid: PoolUuid,
        pool_name: &str,
        paths: &[&Path],
        tier: BlockDevTier,
    ) -> StratisResult<SetCreateAction<DevUuid>>;

    /// Bind all devices in the given pool for automated unlocking
    /// using clevis.
    fn bind_clevis(&mut self, pin: String, clevis_info: Value) -> StratisResult<CreateAction<()>>;

    /// Unbind all devices in the given pool from using clevis.
    fn unbind_clevis(&mut self) -> StratisResult<DeleteAction<()>>;

    /// Ensures that all designated filesystems are gone from pool.
    /// Returns a list of the filesystems found, and actually destroyed.
    /// This list will be a subset of the uuids passed in fs_uuids.
    /// Precondition: All filesystems given must be unmounted.
    fn destroy_filesystems<'a>(
        &'a mut self,
        pool_name: &str,
        fs_uuids: &[FilesystemUuid],
    ) -> StratisResult<SetDeleteAction<FilesystemUuid>>;

    /// Rename filesystem
    /// Rename pool with uuid to new_name.
    /// Raises an error if the mapping can't be applied because
    /// new_name is already in use.
    /// The result indicate whether an action was performed, and if not, why.
    fn rename_filesystem(
        &mut self,
        pool_name: &str,
        uuid: FilesystemUuid,
        new_name: &str,
    ) -> StratisResult<RenameAction<FilesystemUuid>>;

    /// Snapshot filesystem
    /// Create a CoW snapshot of the origin
    fn snapshot_filesystem(
        &mut self,
        pool_uuid: PoolUuid,
        origin_uuid: FilesystemUuid,
        snapshot_name: &str,
    ) -> StratisResult<CreateAction<(FilesystemUuid, &mut dyn Filesystem)>>;

    /// The total number of Sectors belonging to this pool.
    /// There are no exclusions, so this number includes overhead sectors
    /// of all sorts, sectors allocated for every sort of metadata by
    /// Stratis or devicemapper and therefore not available to the user for
    /// storing their data. There is no larger physical size number that can be
    /// associated with a pool.
    fn total_physical_size(&self) -> Sectors;

    /// The number of Sectors in this pool that are currently in use by the
    /// pool for some purpose, and therefore not available for future use,
    /// by any subcomponent of Stratis, either for internal managment or to
    /// store user data.
    fn total_physical_used(&self) -> StratisResult<Sectors>;

    /// Get all the filesystems belonging to this pool.
    fn filesystems(&self) -> Vec<(Name, FilesystemUuid, &dyn Filesystem)>;

    /// Get all the filesystems belonging to this pool as mutable references.
    fn filesystems_mut(&mut self) -> Vec<(Name, FilesystemUuid, &mut dyn Filesystem)>;

    /// Get the filesystem in this pool with this UUID.
    fn get_filesystem(&self, uuid: FilesystemUuid) -> Option<(Name, &dyn Filesystem)>;

    /// Get the mutable filesystem in this pool with this UUID.
    fn get_mut_filesystem(&mut self, uuid: FilesystemUuid) -> Option<(Name, &mut dyn Filesystem)>;

    /// Get _all_ the blockdevs that belong to this pool.
    /// All really means all. For example, it does not exclude cache blockdevs.
    fn blockdevs(&self) -> Vec<(Uuid, BlockDevTier, &dyn BlockDev)>;

    /// Get all the blockdevs belonging to this pool as mutable references.
    fn blockdevs_mut(&mut self) -> Vec<(DevUuid, BlockDevTier, &mut dyn BlockDev)>;

    /// Get the blockdev in this pool with this UUID.
    fn get_blockdev(&self, uuid: DevUuid) -> Option<(BlockDevTier, &dyn BlockDev)>;

    /// Get a mutable reference to the blockdev in this pool with this UUID.
    fn get_mut_blockdev(&mut self, uuid: DevUuid) -> Option<(BlockDevTier, &mut dyn BlockDev)>;

    /// Set the user-settable string associated with the blockdev specified
    /// by the uuid.
    fn set_blockdev_user_info(
        &mut self,
        pool_name: &str,
        uuid: DevUuid,
        user_info: Option<&str>,
    ) -> StratisResult<RenameAction<DevUuid>>;

    /// Set dbus path associated with the Pool.
    fn set_dbus_path(&mut self, path: MaybeDbusPath);

    /// Get dbus path associated with the Pool.
    fn get_dbus_path(&self) -> &MaybeDbusPath;

    /// true if the pool has a cache, otherwise false
    fn has_cache(&self) -> bool;

    /// Determine if the pool's data is encrypted
    fn is_encrypted(&self) -> bool;

    /// Get all encryption information for this pool.
    fn encryption_info(&self) -> Option<&EncryptionInfo>;
}

pub trait Engine: Debug + Report {
    /// Create a Stratis pool.
    /// Returns the UUID of the newly created pool.
    /// Returns an error if the redundancy code does not correspond to a
    /// supported redundancy.
    fn create_pool(
        &mut self,
        name: &str,
        blockdev_paths: &[&Path],
        redundancy: Option<u16>,
        key_desc: Option<KeyDescription>,
    ) -> StratisResult<CreateAction<PoolUuid>>;

    /// Handle a libudev event.
    /// If the handling action resulted in pool creation, return the pool
    /// and its UUID.
    ///
    /// Precondition: the subsystem of the device evented on is "block".
    fn handle_event(&mut self, event: &libudev::Event) -> Option<(Name, PoolUuid, &mut dyn Pool)>;

    /// Destroy a pool.
    /// Ensures that the pool of the given UUID is absent on completion.
    /// Returns true if some action was necessary, otherwise false.
    fn destroy_pool(&mut self, uuid: PoolUuid) -> StratisResult<DeleteAction<PoolUuid>>;

    /// Rename pool with uuid to new_name.
    /// Raises an error if the mapping can't be applied because
    /// new_name is already in use.
    /// Returns true if it was necessary to perform an action, false if not.
    fn rename_pool(
        &mut self,
        uuid: PoolUuid,
        new_name: &str,
    ) -> StratisResult<RenameAction<PoolUuid>>;

    /// Unlock all encrypted devices registered under a given pool UUID.
    /// This method returns a `Vec<DevUuid>`. This `Vec` will contain UUIDs of
    /// devices that were newly unlocked while ignoring devices that are already
    /// in the unlocked state. If some devices are able to be unlocked
    /// and some fail, an error is returned as all devices should be able to
    /// be unlocked if the necessary key is in the keyring.
    fn unlock_pool(
        &mut self,
        uuid: PoolUuid,
        unlock_method: UnlockMethod,
    ) -> StratisResult<SetUnlockAction<DevUuid>>;

    /// Find the pool designated by uuid.
    fn get_pool(&self, uuid: PoolUuid) -> Option<(Name, &dyn Pool)>;

    /// Get a mutable referent to the pool designated by uuid.
    fn get_mut_pool(&mut self, uuid: PoolUuid) -> Option<(Name, &mut dyn Pool)>;

    /// Get a mapping of encrypted pool UUIDs for pools that have not yet
    /// been set up and need to be unlocked to their encryption infos.
    fn locked_pools(&self) -> HashMap<PoolUuid, EncryptionInfo>;

    /// Configure the simulator. Obsolete.
    fn configure_simulator(&mut self, _: u32) -> StratisResult<()> {
        Ok(())
    }

    /// Get all pools belonging to this engine.
    fn pools(&self) -> Vec<(Name, PoolUuid, &dyn Pool)>;

    /// Get mutable references to all pools belonging to this engine.
    fn pools_mut(&mut self) -> Vec<(Name, PoolUuid, &mut dyn Pool)>;

    /// Return the DM file descriptor to check for DM events. If the engine
    /// is the sim engine, the value is None.
    fn get_dm_context(&self) -> Option<&'static DM>;

    /// Notify the engine that an event has occurred on the DM file descriptor.
    fn evented(&mut self) -> StratisResult<()>;

    /// Get the handler for kernel keyring operations.
    fn get_key_handler(&self) -> &dyn KeyActions;

    /// Get the handler for kernel keyring operations mutably.
    fn get_key_handler_mut(&mut self) -> &mut dyn KeyActions;

    /// Return true if this engine is the simulator engine, otherwise false.
    fn is_sim(&self) -> bool;
}
<commit_msg>Update docs for KeyActions::set()<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::{
    collections::HashMap,
    fmt::Debug,
    os::unix::io::RawFd,
    path::{Path, PathBuf},
};

use chrono::{DateTime, Utc};
use serde_json::Value;
use uuid::Uuid;

use devicemapper::{Bytes, Sectors, DM};

use crate::{
    engine::types::{
        BlockDevTier, CreateAction, DeleteAction, DevUuid, EncryptionInfo, FilesystemUuid,
        KeyDescription, MappingCreateAction, MaybeDbusPath, Name, PoolUuid, RenameAction,
        ReportType, SetCreateAction, SetDeleteAction, SetUnlockAction, UnlockMethod,
    },
    stratis::StratisResult,
};

pub const DEV_PATH: &str = "/dev/stratis";
/// The maximum size of pool passphrases stored in the kernel keyring
pub const MAX_STRATIS_PASS_SIZE: usize = 512 / 8;

pub trait KeyActions {
    /// Set a key in the kernel keyring. The output is an idempotent return type
    /// containing a `bool` which indicates whether a key with the requested
    /// key description was in the keyring and the key data was updated.
    ///
    /// Successful return values:
    /// * `Ok(MappingCreateAction::Identity)`: The key was already in the keyring
    /// with the appropriate key description and key data.
    /// * `Ok(MappingCreateAction::Created(()))`: The key was newly added to the
    /// keyring.
    /// * `Ok(MappingCreateAction::Changed)`: The key description was already present
    /// in the keyring but the key data was updated.
    fn set(
        &mut self,
        key_desc: &KeyDescription,
        key_fd: RawFd,
    ) -> StratisResult<MappingCreateAction<()>>;

    /// Return a list of all key descriptions of keys added to the keyring by
    /// Stratis that are still valid.
    fn list(&self) -> StratisResult<Vec<KeyDescription>>;

    /// Unset a key with the given key description in the root persistent kernel
    /// keyring.
    fn unset(&mut self, key_desc: &KeyDescription) -> StratisResult<DeleteAction<()>>;
}

/// An interface for reporting internal engine state.
pub trait Report {
    /// Supported engine state report.
    ///
    /// NOTE: The JSON schema for this report is not guaranteed to be stable.
    fn engine_state_report(&self) -> Value;

    /// Unsupported reports. The available reports and JSON schemas of these reports may change.
    fn get_report(&self, report_type: ReportType) -> Value;
}

pub trait Filesystem: Debug {
    /// path of the device node
    fn devnode(&self) -> PathBuf;

    /// When the filesystem was created.
    fn created(&self) -> DateTime<Utc>;

    /// path to mount the filesystem by
    fn path_to_mount_filesystem(&self, pool_name: &str, fs_name: &str) -> PathBuf;

    /// The amount of data stored on the filesystem, including overhead.
    fn used(&self) -> StratisResult<Bytes>;

    /// Set dbus path associated with the Pool.
    fn set_dbus_path(&mut self, path: MaybeDbusPath);

    /// Get dbus path associated with the Pool.
    fn get_dbus_path(&self) -> &MaybeDbusPath;
}

pub trait BlockDev: Debug {
    /// Get the device path for the block device.
    fn devnode(&self) -> &Path;

    /// Get the canonicalized path to display to the user representing this block device.
    fn user_path(&self) -> StratisResult<PathBuf>;

    /// Get the path to the device on which the Stratis metadata is stored.
    fn metadata_path(&self) -> &Path;

    /// Get the user-settable string associated with this blockdev.
    fn user_info(&self) -> Option<&str>;

    /// Get the hardware ID for this blockdev.
    fn hardware_info(&self) -> Option<&str>;

    /// The time that this blockdev was initialized by Stratis, rounded down
    /// to the nearest second.
    fn initialization_time(&self) -> DateTime<Utc>;

    /// The total size of the device, including space not usable for data.
    fn size(&self) -> Sectors;

    /// Set dbus path associated with the BlockDev.
    fn set_dbus_path(&mut self, path: MaybeDbusPath);

    /// Get dbus path associated with the BlockDev.
    fn get_dbus_path(&self) -> &MaybeDbusPath;

    /// Get the status of whether a block device is encrypted or not.
    fn is_encrypted(&self) -> bool;
}

pub trait Pool: Debug {
    /// Initialize the cache with the provided cache block devices.
    /// Returns a list of the the block devices that were actually added as cache
    /// devices. In practice, this will have three types of return values:
    /// * An error if the cache has already been initialized with a different set
    /// of block devices.
    /// * `SetCreateAction::Identity` if the cache has already been initialized with
    /// the same set of block devices.
    /// * `SetCreateAction::Created` containing all provided block devices if the
    /// cache has not yet been initialized.
    ///
    /// This ensures the contract of providing a truly idempotent API as the cache
    /// can only be initialized once and if an attempt is made to initialize it
    /// twice with different sets of block devices, the user should be notified
    /// of their error.
    fn init_cache(
        &mut self,
        pool_uuid: PoolUuid,
        pool_name: &str,
        blockdevs: &[&Path],
    ) -> StratisResult<SetCreateAction<DevUuid>>;

    /// Creates the filesystems specified by specs.
    /// Returns a list of the names of filesystems actually created.
    /// Returns an error if any of the specified names are already in use
    /// for filesystems in this pool. If the same name is passed multiple
    /// times, the size associated with the last item is used.
    fn create_filesystems<'a, 'b>(
        &'a mut self,
        pool_uuid: PoolUuid,
        specs: &[(&'b str, Option<Sectors>)],
    ) -> StratisResult<SetCreateAction<(&'b str, FilesystemUuid)>>;

    /// Adds blockdevs specified by paths to pool.
    /// Returns a list of uuids corresponding to devices actually added.
    /// Returns an error if a blockdev can not be added because it is owned
    /// or there was an error while reading or writing a blockdev.
    /// Also return an error if the tier specified is Cache, and the cache
    /// is not yet initialized.
    fn add_blockdevs(
        &mut self,
        pool_uuid: PoolUuid,
        pool_name: &str,
        paths: &[&Path],
        tier: BlockDevTier,
    ) -> StratisResult<SetCreateAction<DevUuid>>;

    /// Bind all devices in the given pool for automated unlocking
    /// using clevis.
    fn bind_clevis(&mut self, pin: String, clevis_info: Value) -> StratisResult<CreateAction<()>>;

    /// Unbind all devices in the given pool from using clevis.
    fn unbind_clevis(&mut self) -> StratisResult<DeleteAction<()>>;

    /// Ensures that all designated filesystems are gone from pool.
    /// Returns a list of the filesystems found, and actually destroyed.
    /// This list will be a subset of the uuids passed in fs_uuids.
    /// Precondition: All filesystems given must be unmounted.
    fn destroy_filesystems<'a>(
        &'a mut self,
        pool_name: &str,
        fs_uuids: &[FilesystemUuid],
    ) -> StratisResult<SetDeleteAction<FilesystemUuid>>;

    /// Rename filesystem
    /// Rename pool with uuid to new_name.
    /// Raises an error if the mapping can't be applied because
    /// new_name is already in use.
    /// The result indicate whether an action was performed, and if not, why.
    fn rename_filesystem(
        &mut self,
        pool_name: &str,
        uuid: FilesystemUuid,
        new_name: &str,
    ) -> StratisResult<RenameAction<FilesystemUuid>>;

    /// Snapshot filesystem
    /// Create a CoW snapshot of the origin
    fn snapshot_filesystem(
        &mut self,
        pool_uuid: PoolUuid,
        origin_uuid: FilesystemUuid,
        snapshot_name: &str,
    ) -> StratisResult<CreateAction<(FilesystemUuid, &mut dyn Filesystem)>>;

    /// The total number of Sectors belonging to this pool.
    /// There are no exclusions, so this number includes overhead sectors
    /// of all sorts, sectors allocated for every sort of metadata by
    /// Stratis or devicemapper and therefore not available to the user for
    /// storing their data. There is no larger physical size number that can be
    /// associated with a pool.
    fn total_physical_size(&self) -> Sectors;

    /// The number of Sectors in this pool that are currently in use by the
    /// pool for some purpose, and therefore not available for future use,
    /// by any subcomponent of Stratis, either for internal managment or to
    /// store user data.
    fn total_physical_used(&self) -> StratisResult<Sectors>;

    /// Get all the filesystems belonging to this pool.
    fn filesystems(&self) -> Vec<(Name, FilesystemUuid, &dyn Filesystem)>;

    /// Get all the filesystems belonging to this pool as mutable references.
    fn filesystems_mut(&mut self) -> Vec<(Name, FilesystemUuid, &mut dyn Filesystem)>;

    /// Get the filesystem in this pool with this UUID.
    fn get_filesystem(&self, uuid: FilesystemUuid) -> Option<(Name, &dyn Filesystem)>;

    /// Get the mutable filesystem in this pool with this UUID.
    fn get_mut_filesystem(&mut self, uuid: FilesystemUuid) -> Option<(Name, &mut dyn Filesystem)>;

    /// Get _all_ the blockdevs that belong to this pool.
    /// All really means all. For example, it does not exclude cache blockdevs.
    fn blockdevs(&self) -> Vec<(Uuid, BlockDevTier, &dyn BlockDev)>;

    /// Get all the blockdevs belonging to this pool as mutable references.
    fn blockdevs_mut(&mut self) -> Vec<(DevUuid, BlockDevTier, &mut dyn BlockDev)>;

    /// Get the blockdev in this pool with this UUID.
    fn get_blockdev(&self, uuid: DevUuid) -> Option<(BlockDevTier, &dyn BlockDev)>;

    /// Get a mutable reference to the blockdev in this pool with this UUID.
    fn get_mut_blockdev(&mut self, uuid: DevUuid) -> Option<(BlockDevTier, &mut dyn BlockDev)>;

    /// Set the user-settable string associated with the blockdev specified
    /// by the uuid.
    fn set_blockdev_user_info(
        &mut self,
        pool_name: &str,
        uuid: DevUuid,
        user_info: Option<&str>,
    ) -> StratisResult<RenameAction<DevUuid>>;

    /// Set dbus path associated with the Pool.
    fn set_dbus_path(&mut self, path: MaybeDbusPath);

    /// Get dbus path associated with the Pool.
    fn get_dbus_path(&self) -> &MaybeDbusPath;

    /// true if the pool has a cache, otherwise false
    fn has_cache(&self) -> bool;

    /// Determine if the pool's data is encrypted
    fn is_encrypted(&self) -> bool;

    /// Get all encryption information for this pool.
    fn encryption_info(&self) -> Option<&EncryptionInfo>;
}

pub trait Engine: Debug + Report {
    /// Create a Stratis pool.
    /// Returns the UUID of the newly created pool.
    /// Returns an error if the redundancy code does not correspond to a
    /// supported redundancy.
    fn create_pool(
        &mut self,
        name: &str,
        blockdev_paths: &[&Path],
        redundancy: Option<u16>,
        key_desc: Option<KeyDescription>,
    ) -> StratisResult<CreateAction<PoolUuid>>;

    /// Handle a libudev event.
    /// If the handling action resulted in pool creation, return the pool
    /// and its UUID.
    ///
    /// Precondition: the subsystem of the device evented on is "block".
    fn handle_event(&mut self, event: &libudev::Event) -> Option<(Name, PoolUuid, &mut dyn Pool)>;

    /// Destroy a pool.
    /// Ensures that the pool of the given UUID is absent on completion.
    /// Returns true if some action was necessary, otherwise false.
    fn destroy_pool(&mut self, uuid: PoolUuid) -> StratisResult<DeleteAction<PoolUuid>>;

    /// Rename pool with uuid to new_name.
    /// Raises an error if the mapping can't be applied because
    /// new_name is already in use.
    /// Returns true if it was necessary to perform an action, false if not.
    fn rename_pool(
        &mut self,
        uuid: PoolUuid,
        new_name: &str,
    ) -> StratisResult<RenameAction<PoolUuid>>;

    /// Unlock all encrypted devices registered under a given pool UUID.
    /// This method returns a `Vec<DevUuid>`. This `Vec` will contain UUIDs of
    /// devices that were newly unlocked while ignoring devices that are already
    /// in the unlocked state. If some devices are able to be unlocked
    /// and some fail, an error is returned as all devices should be able to
    /// be unlocked if the necessary key is in the keyring.
    fn unlock_pool(
        &mut self,
        uuid: PoolUuid,
        unlock_method: UnlockMethod,
    ) -> StratisResult<SetUnlockAction<DevUuid>>;

    /// Find the pool designated by uuid.
    fn get_pool(&self, uuid: PoolUuid) -> Option<(Name, &dyn Pool)>;

    /// Get a mutable referent to the pool designated by uuid.
    fn get_mut_pool(&mut self, uuid: PoolUuid) -> Option<(Name, &mut dyn Pool)>;

    /// Get a mapping of encrypted pool UUIDs for pools that have not yet
    /// been set up and need to be unlocked to their encryption infos.
    fn locked_pools(&self) -> HashMap<PoolUuid, EncryptionInfo>;

    /// Configure the simulator. Obsolete.
    fn configure_simulator(&mut self, _: u32) -> StratisResult<()> {
        Ok(())
    }

    /// Get all pools belonging to this engine.
    fn pools(&self) -> Vec<(Name, PoolUuid, &dyn Pool)>;

    /// Get mutable references to all pools belonging to this engine.
    fn pools_mut(&mut self) -> Vec<(Name, PoolUuid, &mut dyn Pool)>;

    /// Return the DM file descriptor to check for DM events. If the engine
    /// is the sim engine, the value is None.
    fn get_dm_context(&self) -> Option<&'static DM>;

    /// Notify the engine that an event has occurred on the DM file descriptor.
    fn evented(&mut self) -> StratisResult<()>;

    /// Get the handler for kernel keyring operations.
    fn get_key_handler(&self) -> &dyn KeyActions;

    /// Get the handler for kernel keyring operations mutably.
    fn get_key_handler_mut(&mut self) -> &mut dyn KeyActions;

    /// Return true if this engine is the simulator engine, otherwise false.
    fn is_sim(&self) -> bool;
}
<|endoftext|>


<commit_before>use std::env;
use std::ffi::{CString, OsStr};
use std::fs::{self, File, OpenOptions};
use std::io;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::{MetadataExt, OpenOptionsExt};
use std::path::Path;
use util;

#[cfg(not(target_os = "redox"))]
use libc::{c_char, c_int, link, rename, unlink};

#[cfg(not(target_os = "redox"))]
#[inline(always)]
pub fn cvt_err(result: c_int) -> io::Result<c_int> {
    if result == -1 {
        Err(io::Error::last_os_error())
    } else {
        Ok(result)
    }
}

#[cfg(target_os = "redox")]
#[inline(always)]
pub fn cvt_err(result: Result<usize, syscall::Error>) -> io::Result<usize> {
    result.map_err(|err| io::Error::from_raw_os_error(err.errno))
}

// Stolen from std.
pub fn cstr(path: &Path) -> io::Result<CString> {
    CString::new(path.as_os_str().as_bytes())
        .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "path contained a null"))
}

pub fn create_named(path: &Path) -> io::Result<File> {
    OpenOptions::new()
        .read(true)
        .write(true)
        .create_new(true)
        .mode(0o600)
        .open(path)
}

fn create_unlinked(path: &Path) -> io::Result<File> {
    let tmp;
    // shadow this to decrease the lifetime. It can't live longer than `tmp`.
    let mut path = path;
    if !path.is_absolute() {
        let cur_dir = env::current_dir()?;
        tmp = cur_dir.join(path);
        path = &tmp;
    }

    let f = create_named(path)?;
    // don't care whether the path has already been unlinked,
    // but perhaps there are some IO error conditions we should send up?
    let _ = fs::remove_file(path);
    Ok(f)
}

#[cfg(target_os = "linux")]
pub fn create(dir: &Path) -> io::Result<File> {
    use libc::{open64, O_CLOEXEC, O_EXCL, O_RDWR, O_TMPFILE};
    use std::os::unix::io::FromRawFd;
    match unsafe {
        let path = cstr(dir)?;
        open64(
            path.as_ptr() as *const c_char,
            O_CLOEXEC | O_EXCL | O_TMPFILE | O_RDWR,
            0o600,
        )
    } {
        -1 => create_unix(dir),
        fd => Ok(unsafe { FromRawFd::from_raw_fd(fd) }),
    }
}

#[cfg(not(target_os = "linux"))]
pub fn create(dir: &Path) -> io::Result<File> {
    create_unix(dir)
}

fn create_unix(dir: &Path) -> io::Result<File> {
    util::create_helper(
        dir,
        OsStr::new(".tmp"),
        OsStr::new(""),
        ::NUM_RAND_CHARS,
        |path| create_unlinked(&path),
    )
}

pub fn reopen(file: &File, path: &Path) -> io::Result<File> {
    let new_file = OpenOptions::new().read(true).write(true).open(path)?;
    let old_meta = file.metadata()?;
    let new_meta = new_file.metadata()?;
    if old_meta.dev() != new_meta.dev() || old_meta.ino() != new_meta.ino() {
        return Err(io::Error::new(
            io::ErrorKind::NotFound,
            "original tempfile has been replaced",
        ));
    }
    Ok(new_file)
}

#[cfg(not(target_os = "redox"))]
pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> {
    unsafe {
        let old_path = cstr(old_path)?;
        let new_path = cstr(new_path)?;
        if overwrite {
            cvt_err(rename(
                old_path.as_ptr() as *const c_char,
                new_path.as_ptr() as *const c_char,
            ))?;
        } else {
            cvt_err(link(
                old_path.as_ptr() as *const c_char,
                new_path.as_ptr() as *const c_char,
            ))?;
            // Ignore unlink errors. Can we do better?
            // On recent linux, we can use renameat2 to do this atomically.
            let _ = unlink(old_path.as_ptr() as *const c_char);
        }
        Ok(())
    }
}

#[cfg(target_os = "redox")]
pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> {
    // XXX implement when possible
    Err(io::Error::from_raw_os_error(syscall::ENOSYS))
}
<commit_msg>use stdlib open options for creating unnamed temporary files<commit_after>use std::env;
use std::ffi::{CString, OsStr};
use std::fs::{self, File, OpenOptions};
use std::io;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::{MetadataExt, OpenOptionsExt};
use std::path::Path;
use util;

#[cfg(not(target_os = "redox"))]
use libc::{c_char, c_int, link, rename, unlink};

#[cfg(not(target_os = "redox"))]
#[inline(always)]
pub fn cvt_err(result: c_int) -> io::Result<c_int> {
    if result == -1 {
        Err(io::Error::last_os_error())
    } else {
        Ok(result)
    }
}

#[cfg(target_os = "redox")]
#[inline(always)]
pub fn cvt_err(result: Result<usize, syscall::Error>) -> io::Result<usize> {
    result.map_err(|err| io::Error::from_raw_os_error(err.errno))
}

// Stolen from std.
pub fn cstr(path: &Path) -> io::Result<CString> {
    CString::new(path.as_os_str().as_bytes())
        .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "path contained a null"))
}

pub fn create_named(path: &Path) -> io::Result<File> {
    OpenOptions::new()
        .read(true)
        .write(true)
        .create_new(true)
        .mode(0o600)
        .open(path)
}

fn create_unlinked(path: &Path) -> io::Result<File> {
    let tmp;
    // shadow this to decrease the lifetime. It can't live longer than `tmp`.
    let mut path = path;
    if !path.is_absolute() {
        let cur_dir = env::current_dir()?;
        tmp = cur_dir.join(path);
        path = &tmp;
    }

    let f = create_named(path)?;
    // don't care whether the path has already been unlinked,
    // but perhaps there are some IO error conditions we should send up?
    let _ = fs::remove_file(path);
    Ok(f)
}

#[cfg(target_os = "linux")]
pub fn create(dir: &Path) -> io::Result<File> {
    use libc::{EISDIR, ENOENT, EOPNOTSUPP, O_EXCL, O_TMPFILE};
    OpenOptions::new()
        .read(true)
        .write(true)
        .custom_flags(O_TMPFILE | O_EXCL) // do not mix with `create_new(true)`
        .open(dir)
        .or_else(|e| {
            match e.raw_os_error() {
                // These are the three "not supported" error codes for O_TMPFILE.
                Some(EOPNOTSUPP) | Some(EISDIR) | Some(ENOENT) => create_unix(dir),
                _ => Err(e),
            }
        })
}

#[cfg(not(target_os = "linux"))]
pub fn create(dir: &Path) -> io::Result<File> {
    create_unix(dir)
}

fn create_unix(dir: &Path) -> io::Result<File> {
    util::create_helper(
        dir,
        OsStr::new(".tmp"),
        OsStr::new(""),
        ::NUM_RAND_CHARS,
        |path| create_unlinked(&path),
    )
}

pub fn reopen(file: &File, path: &Path) -> io::Result<File> {
    let new_file = OpenOptions::new().read(true).write(true).open(path)?;
    let old_meta = file.metadata()?;
    let new_meta = new_file.metadata()?;
    if old_meta.dev() != new_meta.dev() || old_meta.ino() != new_meta.ino() {
        return Err(io::Error::new(
            io::ErrorKind::NotFound,
            "original tempfile has been replaced",
        ));
    }
    Ok(new_file)
}

#[cfg(not(target_os = "redox"))]
pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> {
    unsafe {
        let old_path = cstr(old_path)?;
        let new_path = cstr(new_path)?;
        if overwrite {
            cvt_err(rename(
                old_path.as_ptr() as *const c_char,
                new_path.as_ptr() as *const c_char,
            ))?;
        } else {
            cvt_err(link(
                old_path.as_ptr() as *const c_char,
                new_path.as_ptr() as *const c_char,
            ))?;
            // Ignore unlink errors. Can we do better?
            // On recent linux, we can use renameat2 to do this atomically.
            let _ = unlink(old_path.as_ptr() as *const c_char);
        }
        Ok(())
    }
}

#[cfg(target_os = "redox")]
pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> {
    // XXX implement when possible
    Err(io::Error::from_raw_os_error(syscall::ENOSYS))
}
<|endoftext|>


<commit_before>extern crate chrono;
extern crate fuse;
extern crate libc;
extern crate regex;
extern crate rustc_serialize;
extern crate time;
extern crate users;

use chrono::*;
use fuse::{Filesystem, Request, ReplyData, ReplyEntry, ReplyAttr, ReplyDirectory};
use rustc_serialize::json;
use std::path::Path;

use fs;
use gd;
use object;

use fs::helpers::{create_inode_directory_attributes, create_inode_file_attributes};

pub struct GoodDataFS {
    pub client: gd::GoodDataClient,
    pub users_cache: users::UsersCache,
}

impl Drop for GoodDataFS {
    fn drop(&mut self) {
        println!("Unmounting GoodData Filesystem");
    }
}

#[allow(dead_code)]
impl GoodDataFS {
    pub fn client(&self) -> &gd::GoodDataClient {
        &self.client
    }

    pub fn get_projects_dir_attributes(&self) -> fuse::FileAttr {
        create_inode_directory_attributes(fs::constants::INODE_PROJECTS)
    }

    pub fn get_root_dir_attributes(&self) -> fuse::FileAttr {
        create_inode_directory_attributes(fs::constants::INODE_ROOT)
    }

    pub fn get_user_json_attributes(&self) -> fuse::FileAttr {
        let json = format!("{}\n",
                           json::as_pretty_json(&self.client.user()).to_string());

        let user = json::decode::<object::AccountSetting>(&json);

        let ts = UTC.datetime_from_str(&user.unwrap().accountSetting.updated.unwrap()[..],
                               "%Y-%m-%d %H:%M:%S")
            .unwrap()
            .timestamp();

        let updated = time::Timespec::new(ts, 0);

        create_inode_file_attributes(fs::constants::INODE_USER, json.len() as u64, updated)
    }

    pub fn get_projects_json_attributes(&self) -> fuse::FileAttr {
        let json = format!("{}\n",
                           json::as_pretty_json(&self.client.projects()).to_string());

        create_inode_file_attributes(fs::constants::INODE_PROJECTS_JSON,
                                     json.len() as u64,
                                     fs::constants::DEFAULT_CREATE_TIME)
    }
}

impl Filesystem for GoodDataFS {
    fn getattr(&mut self, req: &Request, ino: u64, reply: ReplyAttr) {
        fs::ops::getattr(self, req, ino, reply)
    }

    fn lookup(&mut self, req: &Request, parent: u64, name: &Path, reply: ReplyEntry) {
        fs::ops::lookup(self, req, parent, &name, reply)
    }

    fn read(&mut self,
            req: &Request,
            ino: u64,
            fh: u64,
            offset: u64,
            size: u32,
            reply: ReplyData) {
        fs::ops::read(self, req, ino, fh, offset, size, reply)
    }

    fn readdir(&mut self, req: &Request, ino: u64, fh: u64, offset: u64, reply: ReplyDirectory) {
        fs::ops::readdir(self, req, ino, fh, offset, reply)
    }
}

impl GoodDataFS {
    pub fn mount(self, mountpoint: String) {
        const VERSION: &'static str = env!("CARGO_PKG_VERSION");
        println!("Mounting GoodData as Filesystem ({}), mountpoint: {}",
                 VERSION,
                 mountpoint);

        fuse::mount(self, &mountpoint, &[]);
    }
}
<commit_msg>Use debug fuse option<commit_after>extern crate chrono;
extern crate fuse;
extern crate libc;
extern crate regex;
extern crate rustc_serialize;
extern crate time;
extern crate users;

use chrono::*;
use fuse::{Filesystem, Request, ReplyData, ReplyEntry, ReplyAttr, ReplyDirectory};
use rustc_serialize::json;
use std::path::Path;

use std::ffi::OsStr;

use fs;
use gd;
use object;

use fs::helpers::{create_inode_directory_attributes, create_inode_file_attributes};

pub struct GoodDataFS {
    pub client: gd::GoodDataClient,
    pub users_cache: users::UsersCache,
}

impl Drop for GoodDataFS {
    fn drop(&mut self) {
        println!("Unmounting GoodData Filesystem");
    }
}

#[allow(dead_code)]
impl GoodDataFS {
    pub fn client(&self) -> &gd::GoodDataClient {
        &self.client
    }

    pub fn get_projects_dir_attributes(&self) -> fuse::FileAttr {
        create_inode_directory_attributes(fs::constants::INODE_PROJECTS)
    }

    pub fn get_root_dir_attributes(&self) -> fuse::FileAttr {
        create_inode_directory_attributes(fs::constants::INODE_ROOT)
    }

    pub fn get_user_json_attributes(&self) -> fuse::FileAttr {
        let json = format!("{}\n",
                           json::as_pretty_json(&self.client.user()).to_string());

        let user = json::decode::<object::AccountSetting>(&json);

        let ts = UTC.datetime_from_str(&user.unwrap().accountSetting.updated.unwrap()[..],
                               "%Y-%m-%d %H:%M:%S")
            .unwrap()
            .timestamp();

        let updated = time::Timespec::new(ts, 0);

        create_inode_file_attributes(fs::constants::INODE_USER, json.len() as u64, updated)
    }

    pub fn get_projects_json_attributes(&self) -> fuse::FileAttr {
        let json = format!("{}\n",
                           json::as_pretty_json(&self.client.projects()).to_string());

        create_inode_file_attributes(fs::constants::INODE_PROJECTS_JSON,
                                     json.len() as u64,
                                     fs::constants::DEFAULT_CREATE_TIME)
    }
}

impl Filesystem for GoodDataFS {
    fn getattr(&mut self, req: &Request, ino: u64, reply: ReplyAttr) {
        fs::ops::getattr(self, req, ino, reply)
    }

    fn lookup(&mut self, req: &Request, parent: u64, name: &Path, reply: ReplyEntry) {
        fs::ops::lookup(self, req, parent, &name, reply)
    }

    fn read(&mut self,
            req: &Request,
            ino: u64,
            fh: u64,
            offset: u64,
            size: u32,
            reply: ReplyData) {
        fs::ops::read(self, req, ino, fh, offset, size, reply)
    }

    fn readdir(&mut self, req: &Request, ino: u64, fh: u64, offset: u64, reply: ReplyDirectory) {
        fs::ops::readdir(self, req, ino, fh, offset, reply)
    }
}

impl GoodDataFS {
    pub fn mount(self, mountpoint: String) {
        const VERSION: &'static str = env!("CARGO_PKG_VERSION");
        println!("Mounting GoodData as Filesystem ({}), mountpoint: {}",
                 VERSION,
                 mountpoint);

        fuse::mount(self, &mountpoint, &[&OsStr::new("debug")]);
    }
}
<|endoftext|>


<commit_before>//! Defines a triangle mesh geometry. Intersection tests are accelerated internally
//! by storing the triangles of the mesh in a BVH
//!
//! # Scene Usage Example
//! The mesh is specified by the OBJ file to load and the name of the specific
//! model within the file to use. The file and other loaded models are kept loaded
//! so you can easily use the same or other models in the file as well. If no name is
//! assigned to the model in the file it will be given the name "unnamed_model",
//! however it's recommended to name your models.
//!
//! ```json
//! "geometry": {
//!     "type": "mesh",
//!     "file": "./suzanne.obj",
//!     "model": "Suzanne"
//! }
//! ```

extern crate tobj;

use std::sync::Arc;
use std::path::Path;
use std::collections::HashMap;

use geometry::{Geometry, DifferentialGeometry, Boundable, BBox, BVH};
use linalg::{self, Normal, Vector, Ray, Point};

/// A mesh composed of triangles, specified by directly passing the position,
/// normal and index buffers for the triangles making up the mesh
pub struct Mesh {
    bvh: BVH<Triangle>,
}

impl Mesh {
    /// Create a new Mesh from the triangles described in the buffers passed
    /// This data could come from an OBJ file via [tobj](https://github.com/Twinklebear/tobj)
    /// for example.
    pub fn new(positions: Arc<Vec<Point>>, normals: Arc<Vec<Normal>>, texcoords: Arc<Vec<Point>>,
               indices: Vec<u32>) -> Mesh {
        let triangles = indices.chunks(3).map(|i| {
            Triangle::new(i[0] as usize, i[1] as usize, i[2] as usize, positions.clone(),
                          normals.clone(), texcoords.clone())
            }).collect();
        Mesh { bvh: BVH::new(16, triangles) }
    }
    /// Load all the meshes defined in an OBJ file and return them in a hashmap that maps the
    /// model's name in the file to its loaded mesh
    /// TODO: Currently materials are ignored
    pub fn load_obj(file_name: &Path) -> HashMap<String, Arc<Mesh>> {
        match tobj::load_obj(file_name) {
            Ok((models, _)) => {
                let mut meshes = HashMap::new();
                for m in models {
                    println!("Loading model {}", m.name);
                    let mesh = m.mesh;
                    if mesh.normals.is_empty() || mesh.texcoords.is_empty() {
                        print!("Mesh::load_obj error! Normals and texture coordinates are required!");
                        println!("Skipping {}", m.name);
                        continue;
                    }
                    println!("Number of triangles {}", mesh.indices.len() / 3);
                    let positions = Arc::new(mesh.positions.chunks(3).map(|i| Point::new(i[0], i[1], i[2]))
                                             .collect());
                    let normals = Arc::new(mesh.normals.chunks(3).map(|i| Normal::new(i[0], i[1], i[2]))
                                           .collect());
                    let texcoords = Arc::new(mesh.texcoords.chunks(2).map(|i| Point::new(i[0], i[1], 0.0))
                                             .collect());
                    meshes.insert(m.name, Arc::new(Mesh::new(positions, normals, texcoords, mesh.indices)));
                }
                meshes
            },
            Err(e) => {
                println!("Failed to load {:?} due to {:?}", file_name, e);
                HashMap::new()
            },
        }
    }
}

impl Geometry for Mesh {
    fn intersect(&self, ray: &mut linalg::Ray) -> Option<DifferentialGeometry> {
        self.bvh.intersect(ray, |r, i| i.intersect(r))
    }
}

impl Boundable for Mesh {
    fn bounds(&self) -> BBox {
        self.bvh.bounds()
    }
}

/// A triangle in some mesh. Just stores a reference to the mesh
/// and the indices of each vertex
pub struct Triangle {
    a: usize,
    b: usize,
    c: usize,
    positions: Arc<Vec<Point>>,
    normals: Arc<Vec<Normal>>,
    texcoords: Arc<Vec<Point>>,
}

impl Triangle {
    /// Create a new triangle representing a triangle within the mesh passed
    pub fn new(a: usize, b: usize, c: usize, positions: Arc<Vec<Point>>,
               normals: Arc<Vec<Normal>>, texcoords: Arc<Vec<Point>>) -> Triangle {
        Triangle { a: a, b: b, c: c, positions: positions, normals: normals,
                   texcoords: texcoords }
    }
}

impl Geometry for Triangle {
    fn intersect(&self, ray: &mut Ray) -> Option<DifferentialGeometry> {
        let pa = &self.positions[self.a];
        let pb = &self.positions[self.b];
        let pc = &self.positions[self.c];

        let e = [*pb - *pa, *pc - *pa];
        let mut s = [Vector::broadcast(0.0); 2];
        s[0] = linalg::cross(&ray.d, &e[1]);
        let div = match linalg::dot(&s[0], &e[0]) {
            // 0.0 => degenerate triangle, can't hit
            0.0 => return None,
            d => 1.0 / d,
        };

        let d = ray.o - *pa;
        let mut bary = [0.0; 3];
        bary[0] = linalg::dot(&d, &s[0]) * div;
        // Check that the first barycentric coordinate is in the triangle bounds
        if bary[0] < -1.0e-8 || bary[0] > 1.0 {
            return None;
        }

        s[1] = linalg::cross(&d, &e[0]);
        bary[1] = linalg::dot(&ray.d, &s[1]) * div;
        // Check the second barycentric coordinate is in the triangle bounds
        if bary[1] < -1.0e-8 || bary[0] + bary[1] > 1.0 {
            return None;
        }

        // We've hit the triangle with the ray, now check the hit location is in the ray range
        let t = linalg::dot(&e[1], &s[1]) * div;
        if t < ray.min_t || t > ray.max_t {
            return None;
        }
        bary[2] = 1.0 - bary[0] - bary[1];
        ray.max_t = t;
        let p = ray.at(t);

        // Now compute normal at this location on the triangle
        let na = &self.normals[self.a];
        let nb = &self.normals[self.b];
        let nc = &self.normals[self.c];
        let n = (bary[2] * *na + bary[0] * *nb + bary[1] * *nc).normalized();

        // Compute parameterization of surface and various derivatives for texturing
        // Triangles are parameterized by the obj texcoords at the vertices
        let ta = &self.texcoords[self.a];
        let tb = &self.texcoords[self.b];
        let tc = &self.texcoords[self.c];
        // Triangle points can be found by p_i = p_0 + u_i dp/du + v_i dp/dv
        // we use this property to find the derivatives dp/du and dp/dv
        let du = [ta.x - tc.x, tb.x - tc.x];
        let dv = [ta.y - tc.y, tb.y - tc.y];
        let det = du[0] * dv[1] - dv[0] * du[1];
        //If the texcoords are degenerate pick arbitrary coordinate system
        let (dp_du, dp_dv) =
            if det == 0.0 {
                linalg::coordinate_system(&linalg::cross(&e[1], &e[0]).normalized())
            }
            else {
                let det = 1.0 / det;
                let dp = [*pa - *pc, *pb - *pc];
                let dp_du = (dv[1] * dp[0] - dv[0] * dp[1]) * det;
                let dp_dv = (-du[1] * dp[0] + du[0] * dp[1]) * det;
                (dp_du, dp_dv)
            };
        Some(DifferentialGeometry::new(&p, &n, &dp_du, &dp_dv, self))
    }
}

impl Boundable for Triangle {
    fn bounds(&self) -> BBox {
        BBox::singular(self.positions[self.a])
            .point_union(&self.positions[self.b])
            .point_union(&self.positions[self.c])
    }
}

<commit_msg>Tweak printing of # of tris in mesh to be clearer<commit_after>//! Defines a triangle mesh geometry. Intersection tests are accelerated internally
//! by storing the triangles of the mesh in a BVH
//!
//! # Scene Usage Example
//! The mesh is specified by the OBJ file to load and the name of the specific
//! model within the file to use. The file and other loaded models are kept loaded
//! so you can easily use the same or other models in the file as well. If no name is
//! assigned to the model in the file it will be given the name "unnamed_model",
//! however it's recommended to name your models.
//!
//! ```json
//! "geometry": {
//!     "type": "mesh",
//!     "file": "./suzanne.obj",
//!     "model": "Suzanne"
//! }
//! ```

extern crate tobj;

use std::sync::Arc;
use std::path::Path;
use std::collections::HashMap;

use geometry::{Geometry, DifferentialGeometry, Boundable, BBox, BVH};
use linalg::{self, Normal, Vector, Ray, Point};

/// A mesh composed of triangles, specified by directly passing the position,
/// normal and index buffers for the triangles making up the mesh
pub struct Mesh {
    bvh: BVH<Triangle>,
}

impl Mesh {
    /// Create a new Mesh from the triangles described in the buffers passed
    /// This data could come from an OBJ file via [tobj](https://github.com/Twinklebear/tobj)
    /// for example.
    pub fn new(positions: Arc<Vec<Point>>, normals: Arc<Vec<Normal>>, texcoords: Arc<Vec<Point>>,
               indices: Vec<u32>) -> Mesh {
        let triangles = indices.chunks(3).map(|i| {
            Triangle::new(i[0] as usize, i[1] as usize, i[2] as usize, positions.clone(),
                          normals.clone(), texcoords.clone())
            }).collect();
        Mesh { bvh: BVH::new(16, triangles) }
    }
    /// Load all the meshes defined in an OBJ file and return them in a hashmap that maps the
    /// model's name in the file to its loaded mesh
    /// TODO: Currently materials are ignored
    pub fn load_obj(file_name: &Path) -> HashMap<String, Arc<Mesh>> {
        match tobj::load_obj(file_name) {
            Ok((models, _)) => {
                let mut meshes = HashMap::new();
                for m in models {
                    println!("Loading model {}", m.name);
                    let mesh = m.mesh;
                    if mesh.normals.is_empty() || mesh.texcoords.is_empty() {
                        print!("Mesh::load_obj error! Normals and texture coordinates are required!");
                        println!("Skipping {}", m.name);
                        continue;
                    }
                    println!("{} has {} triangles", m.name, mesh.indices.len() / 3);
                    let positions = Arc::new(mesh.positions.chunks(3).map(|i| Point::new(i[0], i[1], i[2]))
                                             .collect());
                    let normals = Arc::new(mesh.normals.chunks(3).map(|i| Normal::new(i[0], i[1], i[2]))
                                           .collect());
                    let texcoords = Arc::new(mesh.texcoords.chunks(2).map(|i| Point::new(i[0], i[1], 0.0))
                                             .collect());
                    meshes.insert(m.name, Arc::new(Mesh::new(positions, normals, texcoords, mesh.indices)));
                }
                meshes
            },
            Err(e) => {
                println!("Failed to load {:?} due to {:?}", file_name, e);
                HashMap::new()
            },
        }
    }
}

impl Geometry for Mesh {
    fn intersect(&self, ray: &mut linalg::Ray) -> Option<DifferentialGeometry> {
        self.bvh.intersect(ray, |r, i| i.intersect(r))
    }
}

impl Boundable for Mesh {
    fn bounds(&self) -> BBox {
        self.bvh.bounds()
    }
}

/// A triangle in some mesh. Just stores a reference to the mesh
/// and the indices of each vertex
pub struct Triangle {
    a: usize,
    b: usize,
    c: usize,
    positions: Arc<Vec<Point>>,
    normals: Arc<Vec<Normal>>,
    texcoords: Arc<Vec<Point>>,
}

impl Triangle {
    /// Create a new triangle representing a triangle within the mesh passed
    pub fn new(a: usize, b: usize, c: usize, positions: Arc<Vec<Point>>,
               normals: Arc<Vec<Normal>>, texcoords: Arc<Vec<Point>>) -> Triangle {
        Triangle { a: a, b: b, c: c, positions: positions, normals: normals,
                   texcoords: texcoords }
    }
}

impl Geometry for Triangle {
    fn intersect(&self, ray: &mut Ray) -> Option<DifferentialGeometry> {
        let pa = &self.positions[self.a];
        let pb = &self.positions[self.b];
        let pc = &self.positions[self.c];

        let e = [*pb - *pa, *pc - *pa];
        let mut s = [Vector::broadcast(0.0); 2];
        s[0] = linalg::cross(&ray.d, &e[1]);
        let div = match linalg::dot(&s[0], &e[0]) {
            // 0.0 => degenerate triangle, can't hit
            0.0 => return None,
            d => 1.0 / d,
        };

        let d = ray.o - *pa;
        let mut bary = [0.0; 3];
        bary[0] = linalg::dot(&d, &s[0]) * div;
        // Check that the first barycentric coordinate is in the triangle bounds
        if bary[0] < -1.0e-8 || bary[0] > 1.0 {
            return None;
        }

        s[1] = linalg::cross(&d, &e[0]);
        bary[1] = linalg::dot(&ray.d, &s[1]) * div;
        // Check the second barycentric coordinate is in the triangle bounds
        if bary[1] < -1.0e-8 || bary[0] + bary[1] > 1.0 {
            return None;
        }

        // We've hit the triangle with the ray, now check the hit location is in the ray range
        let t = linalg::dot(&e[1], &s[1]) * div;
        if t < ray.min_t || t > ray.max_t {
            return None;
        }
        bary[2] = 1.0 - bary[0] - bary[1];
        ray.max_t = t;
        let p = ray.at(t);

        // Now compute normal at this location on the triangle
        let na = &self.normals[self.a];
        let nb = &self.normals[self.b];
        let nc = &self.normals[self.c];
        let n = (bary[2] * *na + bary[0] * *nb + bary[1] * *nc).normalized();

        // Compute parameterization of surface and various derivatives for texturing
        // Triangles are parameterized by the obj texcoords at the vertices
        let ta = &self.texcoords[self.a];
        let tb = &self.texcoords[self.b];
        let tc = &self.texcoords[self.c];
        // Triangle points can be found by p_i = p_0 + u_i dp/du + v_i dp/dv
        // we use this property to find the derivatives dp/du and dp/dv
        let du = [ta.x - tc.x, tb.x - tc.x];
        let dv = [ta.y - tc.y, tb.y - tc.y];
        let det = du[0] * dv[1] - dv[0] * du[1];
        //If the texcoords are degenerate pick arbitrary coordinate system
        let (dp_du, dp_dv) =
            if det == 0.0 {
                linalg::coordinate_system(&linalg::cross(&e[1], &e[0]).normalized())
            }
            else {
                let det = 1.0 / det;
                let dp = [*pa - *pc, *pb - *pc];
                let dp_du = (dv[1] * dp[0] - dv[0] * dp[1]) * det;
                let dp_dv = (-du[1] * dp[0] + du[0] * dp[1]) * det;
                (dp_du, dp_dv)
            };
        Some(DifferentialGeometry::new(&p, &n, &dp_du, &dp_dv, self))
    }
}

impl Boundable for Triangle {
    fn bounds(&self) -> BBox {
        BBox::singular(self.positions[self.a])
            .point_union(&self.positions[self.b])
            .point_union(&self.positions[self.c])
    }
}

<|endoftext|>


<commit_before>use std::str::CharIndices;
use std::ascii::AsciiExt;
use std::iter::Peekable;
use std::ops::Range;

macro_rules! my_try {
    ($e:expr) => ({
        match $e {
            Ok(x) => x,
            Err(x) => return Some(Err(x)),
        }
    });
}

#[derive(Clone, Copy, Debug)]
pub enum Token<'input> {
    OpenParen,
    ClosingParen,
    Dot,
    Quote,
    WhiteSpace,
    Char(char),
    Integer(i64),
    String(&'input str),
    Symbol(&'input str),
}

#[derive(Debug)]
pub struct Tokenizer<'input> {
    text: &'input str,
    chars: Peekable<CharIndices<'input>>,
}

pub type Spanned<T> = (usize, T, usize);

#[derive(Debug)]
pub enum Error {
    InvalidCharacter(char),
    UnexpectedToken(usize),
    UnexpectedEof,
}

impl<'input> Tokenizer<'input> {
    pub fn new(text: &'input str) -> Tokenizer<'input> {
        Tokenizer {
            text: text,
            chars: text.char_indices().peekable(),
        }
    }

    fn next_char(&mut self) -> Option<(usize, char)> {
        self.chars.next()
    }

    fn peek_next(&mut self) -> Option<&(usize, char)> {
        self.chars.peek()
    }

    // consumes all tokens until f returns true.
    // leaves the token for which f returned true in self.chars()
    fn eat_until<F: Fn(char) -> bool>(&mut self, start: usize, f: F) -> &'input str {
        match self.eat_until_strict(start, f) {
            Ok(x) => x,
            Err(_) => &self.text[start..],
        }
    }

    // same as eat_until, but returns an error if eof was encountered before f returned true
    fn eat_until_strict<F: Fn(char) -> bool>(&mut self, start: usize, f: F) -> Result<&'input str, Error> {
        while let Some(&(pos, c)) = self.peek_next() {
            if f(c) {
                return Ok(&self.text[start..pos]);
            } else {
                self.next_char(); //bump
            }
        }
        Err(Error::UnexpectedEof)
    }

    fn token_at(at: Range<usize>, tok: Token) -> Option<Result<Spanned<Token>, Error>> {
        Some(Ok((at.start, tok, at.end)))
    }

    fn parse_char(&mut self, start: usize) -> Option<Result<Spanned<Token<'input>>, Error>> {
        let s = self.eat_until(start, |c| end_of_item(c) && c != ')');

        if s.len() <= 2 {
            Tokenizer::token_at(start..start+s.len(), Token::Symbol(s))
        } else if s.len() == 3 {
            let c = s.chars().skip(2).next().unwrap();
            if valid_char(c) {
                Tokenizer::token_at(start..start+4, Token::Char(c))
            } else {
                Some(Err(Error::UnexpectedToken(start+2)))
            }
        } else if s.len() == 4 {
            let token = match s {
                r"#\\n" => Token::Char('\n'),
                r"#\\t" => Token::Char('\t'),
                r"#\\s" => Token::Char(' '),
                _ => return Some(Err(Error::UnexpectedToken(start+2))),
            };
            Tokenizer::token_at(start+2..start+4, token)
        } else {
            Some(Err(Error::UnexpectedToken(start+2)))
        }
    }

    fn parse_integer(&mut self, start: usize) -> Option<Result<Spanned<Token<'input>>, Error>> {
        let int_slice = self.eat_until(start, end_of_item);
        for (pos, c) in int_slice.char_indices() {
            if !numeric(c) {
                if pos == 0 && c == '-' { continue }
                return Some(Err(Error::UnexpectedToken(pos)));
            }
        }
        let num: i64 = int_slice.parse().unwrap();
        Tokenizer::token_at(start..start+int_slice.len(), Token::Integer(num))
    }

    fn parse_symbol(&mut self, start: usize) -> Option<Result<Spanned<Token<'input>>, Error>> {
        let symbol = self.eat_until(start, end_of_item);
        Tokenizer::token_at(start..start+symbol.len(), Token::Symbol(symbol))
    }

    fn parse_string(&mut self, start: usize) -> Option<Result<Spanned<Token<'input>>, Error>> {
        let mut str_len = 0;
        loop {
            match self.eat_until_strict(start + str_len, |c| c == '"') {
                Ok(substr) => {
                    self.next_char(); // bump so that '"' is skipped in next iteration/token

                    let mut rev_iter = substr.chars().rev().map(|x| x == '\\');
                    let last_was_slash = rev_iter.next().unwrap_or(false);
                    let second_last_was_slash = rev_iter.next().unwrap_or(false);

                    if last_was_slash && !second_last_was_slash {
                        str_len += substr.len() + 1; // + 1: include '"'
                    } else {
                        str_len += substr.len();
                        let s = &self.text[start..start+str_len];
                        // check for ascii only strings (for now)
                        for c in s.chars() {
                            if !c.is_ascii() {
                                return Some(Err(Error::InvalidCharacter(c)));
                            }
                        }
                        return Tokenizer::token_at(start..start+str_len, Token::String(s));
                    }
                }
                Err(x) => return Some(Err(x)),
            };
        }
    }
}

impl<'input> Iterator for Tokenizer<'input> {
    type Item = Result<Spanned<Token<'input>>, Error>;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            match self.next_char() {
                Some((s, '(')) => return Tokenizer::token_at(s..s+1, Token::OpenParen),
                Some((s, ')')) => return Tokenizer::token_at(s..s+1, Token::ClosingParen),
                Some((s, '\'')) => return Tokenizer::token_at(s..s+1, Token::Quote),
                Some((s, '.')) => return Tokenizer::token_at(s..s+1, Token::Dot),
                Some((s, '#')) => return self.parse_char(s),
                // special case '-'
                Some((start, '-')) => {
                    if let Some(&(_, c)) = self.peek_next() {
                        // we got a number
                        if numeric(c) {
                            return self.parse_integer(start);
                        }
                    }
                    return self.parse_symbol(start);
                },
                // number
                Some((start, x)) if numeric(x) => return self.parse_integer(start),
                // string, + 1 bc we don't need the '"' in string content
                Some((start, '"')) => return self.parse_string(start+1),
                // symbol
                Some((start, _)) => return self.parse_symbol(start),
                None => return None,
            }
        }
    }
}

fn whitespace(x: char) -> bool {
    x == ' ' ||
    x == '\n' ||
    x == '\t'
}

fn end_of_item(x: char) -> bool {
    whitespace(x) ||
    x == ')'
}

fn numeric(x: char) -> bool {
    '0' <= x && x <= '9'
}

fn valid_char(x: char) -> bool {
    x >= '!' &&
    x <= '~'
}
<commit_msg>implement whitespace lexing<commit_after>use std::str::CharIndices;
use std::ascii::AsciiExt;
use std::iter::Peekable;
use std::ops::Range;

macro_rules! my_try {
    ($e:expr) => ({
        match $e {
            Ok(x) => x,
            Err(x) => return Some(Err(x)),
        }
    });
}

#[derive(Clone, Copy, Debug)]
pub enum Token<'input> {
    OpenParen,
    ClosingParen,
    Dot,
    Quote,
    WhiteSpace,
    Char(char),
    Integer(i64),
    String(&'input str),
    Symbol(&'input str),
}

#[derive(Debug)]
pub struct Tokenizer<'input> {
    text: &'input str,
    chars: Peekable<CharIndices<'input>>,
}

pub type Spanned<T> = (usize, T, usize);

#[derive(Debug)]
pub enum Error {
    InvalidCharacter(char),
    UnexpectedToken(usize),
    UnexpectedEof,
}

impl<'input> Tokenizer<'input> {
    pub fn new(text: &'input str) -> Tokenizer<'input> {
        Tokenizer {
            text: text,
            chars: text.char_indices().peekable(),
        }
    }

    fn next_char(&mut self) -> Option<(usize, char)> {
        self.chars.next()
    }

    fn peek_next(&mut self) -> Option<&(usize, char)> {
        self.chars.peek()
    }

    // consumes all tokens until f returns true.
    // leaves the token for which f returned true in self.chars()
    fn eat_until<F: Fn(char) -> bool>(&mut self, start: usize, f: F) -> &'input str {
        match self.eat_until_strict(start, f) {
            Ok(x) => x,
            Err(_) => &self.text[start..],
        }
    }

    // same as eat_until, but returns an error if eof was encountered before f returned true
    fn eat_until_strict<F: Fn(char) -> bool>(&mut self, start: usize, f: F) -> Result<&'input str, Error> {
        while let Some(&(pos, c)) = self.peek_next() {
            if f(c) {
                return Ok(&self.text[start..pos]);
            } else {
                self.next_char(); //bump
            }
        }
        Err(Error::UnexpectedEof)
    }

    fn token_at(at: Range<usize>, tok: Token) -> Option<Result<Spanned<Token>, Error>> {
        Some(Ok((at.start, tok, at.end)))
    }

    fn parse_char(&mut self, start: usize) -> Option<Result<Spanned<Token<'input>>, Error>> {
        let s = self.eat_until(start, |c| end_of_item(c) && c != ')');

        if s.len() <= 2 {
            Tokenizer::token_at(start..start+s.len(), Token::Symbol(s))
        } else if s.len() == 3 {
            let c = s.chars().skip(2).next().unwrap();
            if valid_char(c) {
                Tokenizer::token_at(start..start+4, Token::Char(c))
            } else {
                Some(Err(Error::UnexpectedToken(start+2)))
            }
        } else if s.len() == 4 {
            let token = match s {
                r"#\\n" => Token::Char('\n'),
                r"#\\t" => Token::Char('\t'),
                r"#\\s" => Token::Char(' '),
                _ => return Some(Err(Error::UnexpectedToken(start+2))),
            };
            Tokenizer::token_at(start+2..start+4, token)
        } else {
            Some(Err(Error::UnexpectedToken(start+2)))
        }
    }

    fn parse_integer(&mut self, start: usize) -> Option<Result<Spanned<Token<'input>>, Error>> {
        let int_slice = self.eat_until(start, end_of_item);
        for (pos, c) in int_slice.char_indices() {
            if !numeric(c) {
                if pos == 0 && c == '-' { continue }
                return Some(Err(Error::UnexpectedToken(pos)));
            }
        }
        let num: i64 = int_slice.parse().unwrap();
        Tokenizer::token_at(start..start+int_slice.len(), Token::Integer(num))
    }

    fn parse_symbol(&mut self, start: usize) -> Option<Result<Spanned<Token<'input>>, Error>> {
        let symbol = self.eat_until(start, end_of_item);
        Tokenizer::token_at(start..start+symbol.len(), Token::Symbol(symbol))
    }

    fn parse_string(&mut self, start: usize) -> Option<Result<Spanned<Token<'input>>, Error>> {
        let mut str_len = 0;
        loop {
            match self.eat_until_strict(start + str_len, |c| c == '"') {
                Ok(substr) => {
                    self.next_char(); // bump so that '"' is skipped in next iteration/token

                    let mut rev_iter = substr.chars().rev().map(|x| x == '\\');
                    let last_was_slash = rev_iter.next().unwrap_or(false);
                    let second_last_was_slash = rev_iter.next().unwrap_or(false);

                    if last_was_slash && !second_last_was_slash {
                        str_len += substr.len() + 1; // + 1: include '"'
                    } else {
                        str_len += substr.len();
                        let s = &self.text[start..start+str_len];
                        // check for ascii only strings (for now)
                        for c in s.chars() {
                            if !c.is_ascii() {
                                return Some(Err(Error::InvalidCharacter(c)));
                            }
                        }
                        return Tokenizer::token_at(start..start+str_len, Token::String(s));
                    }
                }
                Err(x) => return Some(Err(x)),
            };
        }
    }
}

impl<'input> Iterator for Tokenizer<'input> {
    type Item = Result<Spanned<Token<'input>>, Error>;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            match self.next_char() {
                Some((s, '(')) => return Tokenizer::token_at(s..s+1, Token::OpenParen),
                Some((s, ')')) => return Tokenizer::token_at(s..s+1, Token::ClosingParen),
                Some((s, '\'')) => return Tokenizer::token_at(s..s+1, Token::Quote),
                Some((s, '.')) => return Tokenizer::token_at(s..s+1, Token::Dot),
                Some((s, '#')) => return self.parse_char(s),
                // special case '-'
                Some((start, '-')) => {
                    if let Some(&(_, c)) = self.peek_next() {
                        // we got a number
                        if numeric(c) {
                            return self.parse_integer(start);
                        }
                    }
                    return self.parse_symbol(start);
                },
                // number
                Some((start, x)) if numeric(x) => return self.parse_integer(start),
                // whitespace
                Some((start, x)) if whitespace(x) => {
                    let len = self.eat_until(start, |c| !whitespace(c)).len();
                    return Tokenizer::token_at(start..len, Token::WhiteSpace);
                },
                // string, + 1 bc we don't need the '"' in string content
                Some((start, '"')) => return self.parse_string(start+1),
                // symbol
                Some((start, _)) => return self.parse_symbol(start),
                None => return None,
            }
        }
    }
}

fn whitespace(x: char) -> bool {
    x == ' ' ||
    x == '\n' ||
    x == '\t'
}

fn end_of_item(x: char) -> bool {
    whitespace(x) ||
    x == ')'
}

fn numeric(x: char) -> bool {
    '0' <= x && x <= '9'
}

fn valid_char(x: char) -> bool {
    x >= '!' &&
    x <= '~'
}
<|endoftext|>