<commit_before>use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io::stderr;
use std::path::Path;
use std::process;

const PREVIEW_LEN: usize = 50;

#[derive(Debug, PartialEq)]
struct Match {
    offset: usize,
    key: u8,
    preview: String,
}

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    if args.len() < 2 {
        writeln!(stderr(), "usage: roxor <file> <crib>").unwrap();
        process::exit(1);
    }

    let path = Path::new(&args[0]);
    let crib = args[1].as_bytes();

    if crib.len() < 2 {
        writeln!(stderr(), "error: crib too short").unwrap();
        process::exit(1);
    }

    let mut file = match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", path.display(), why),
        Ok(file) => file,
    };

    let mut ciphertext: Vec<u8> = Vec::new();
    file.read_to_end(&mut ciphertext).unwrap();

    if ciphertext.len() < crib.len() {
        writeln!(stderr(), "error: ciphertext too short").unwrap();
        process::exit(1);
    }

    for m in attack_cipher(&ciphertext[..], crib) {
        println!("Found text at 0x{:x} (XOR key 0x{:02x})", m.offset, m.key);
        println!("  preview: {}", m.preview);
    }
}

fn attack_cipher(ciphertext: &[u8], crib: &[u8]) -> Vec<Match> {
    let mut matches: Vec<Match> = Vec::new();
    for (i, x) in ciphertext.iter().enumerate() {
        let mut old_key = x ^ crib[0];
        let mut j = 1;
        while j < crib.len() && (i + j) < ciphertext.len() {
            let key = ciphertext[i + j] ^ crib[j];
            if key != old_key {
                break;
            }
            old_key = key;
            j += 1;
            if j == crib.len() {
                let preview: String = ciphertext[i..]
                                          .iter()
                                          .take(PREVIEW_LEN)
                                          .map(|x| x ^ key)
                                          .map(|x| {
                                              if x >= 32 && x <= 126 {
                                                  x as char
                                              } else {
                                                  '.'
                                              }
                                          })
                                          .collect();
                matches.push(Match {
                    offset: i,
                    key: key,
                    preview: preview,
                });
            }
        }
    }
    return matches;
}

#[cfg(test)]
mod tests {
    use super::{Match, attack_cipher};

    struct Test {
        ciphertext: &'static [u8],
        crib: &'static [u8],
        matches: Vec<Match>,
    }

    #[test]
    fn test_attack_cipher() {
        let tests = vec![
            Test {
                ciphertext: &[],
                crib: &[],
                matches: vec![],
            },
            Test {
                ciphertext: b"haystack",
                crib: b"needle",
                matches: vec![],
            },
            Test {
                ciphertext: b"needle in haystack",
                crib: b"needle",
                matches: vec![
                    Match { offset: 0, key: 0, preview: "needle in haystack".to_string() },
                ],
            },
            Test {
                ciphertext: b"a needle, another needle",
                crib: b"needle",
                matches: vec![
                    Match { offset: 2, key: 0, preview: "needle, another needle".to_string() },
                    Match { offset: 18, key: 0, preview: "needle".to_string() },
                ],
            },
            Test {
                ciphertext: b"\x23\x62\x2c\x27\x27\x26\x2e\x27\x6e\x62\x23\x2c\x2d\x36\x2a\x27\x30\x62\x2c\x27\x27\x26\x2e\x27",
                crib: b"needle",
                matches: vec![
                    Match { offset: 2, key: 0x42, preview: "needle, another needle".to_string() },
                    Match { offset: 18, key: 0x42, preview: "needle".to_string() },
                ],
            },
        ];

        for t in tests.iter() {
            assert_eq!(t.matches, attack_cipher(t.ciphertext, t.crib));
        }
    }
}
<commit_msg>:RustFmt<commit_after>use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io::stderr;
use std::path::Path;
use std::process;

const PREVIEW_LEN: usize = 50;

#[derive(Debug, PartialEq)]
struct Match {
    offset: usize,
    key: u8,
    preview: String,
}

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    if args.len() < 2 {
        writeln!(stderr(), "usage: roxor <file> <crib>").unwrap();
        process::exit(1);
    }

    let path = Path::new(&args[0]);
    let crib = args[1].as_bytes();

    if crib.len() < 2 {
        writeln!(stderr(), "error: crib too short").unwrap();
        process::exit(1);
    }

    let mut file = match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", path.display(), why),
        Ok(file) => file,
    };

    let mut ciphertext: Vec<u8> = Vec::new();
    file.read_to_end(&mut ciphertext).unwrap();

    if ciphertext.len() < crib.len() {
        writeln!(stderr(), "error: ciphertext too short").unwrap();
        process::exit(1);
    }

    for m in attack_cipher(&ciphertext[..], crib) {
        println!("Found text at 0x{:x} (XOR key 0x{:02x})", m.offset, m.key);
        println!("  preview: {}", m.preview);
    }
}

fn attack_cipher(ciphertext: &[u8], crib: &[u8]) -> Vec<Match> {
    let mut matches: Vec<Match> = Vec::new();
    for (i, x) in ciphertext.iter().enumerate() {
        let mut old_key = x ^ crib[0];
        let mut j = 1;
        while j < crib.len() && (i + j) < ciphertext.len() {
            let key = ciphertext[i + j] ^ crib[j];
            if key != old_key {
                break;
            }
            old_key = key;
            j += 1;
            if j == crib.len() {
                let preview: String = ciphertext[i..]
                    .iter()
                    .take(PREVIEW_LEN)
                    .map(|x| x ^ key)
                    .map(|x| { if x >= 32 && x <= 126 { x as char } else { '.' } })
                    .collect();
                matches.push(Match {
                    offset: i,
                    key: key,
                    preview: preview,
                });
            }
        }
    }
    return matches;
}

#[cfg(test)]
mod tests {
    use super::{Match, attack_cipher};

    struct Test {
        ciphertext: &'static [u8],
        crib: &'static [u8],
        matches: Vec<Match>,
    }

    #[test]
    fn test_attack_cipher() {
        let tests = vec![
            Test {
                ciphertext: &[],
                crib: &[],
                matches: vec![],
            },
            Test {
                ciphertext: b"haystack",
                crib: b"needle",
                matches: vec![],
            },
            Test {
                ciphertext: b"needle in haystack",
                crib: b"needle",
                matches: vec![
                    Match { offset: 0, key: 0, preview: "needle in haystack".to_string() },
                ],
            },
            Test {
                ciphertext: b"a needle, another needle",
                crib: b"needle",
                matches: vec![
                    Match { offset: 2, key: 0, preview: "needle, another needle".to_string() },
                    Match { offset: 18, key: 0, preview: "needle".to_string() },
                ],
            },
            Test {
                ciphertext: b"\x23\x62\x2c\x27\x27\x26\x2e\x27\x6e\x62\x23\x2c\x2d\x36\x2a\x27\x30\x62\x2c\x27\x27\x26\x2e\x27",
                crib: b"needle",
                matches: vec![
                    Match { offset: 2, key: 0x42, preview: "needle, another needle".to_string() },
                    Match { offset: 18, key: 0x42, preview: "needle".to_string() },
                ],
            },
        ];

        for t in tests.iter() {
            assert_eq!(t.matches, attack_cipher(t.ciphertext, t.crib));
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

pub mod convert;

use std::path::PathBuf;

use convert::FromLossy;

use rrg_macro::ack;

include!(concat!(env!("OUT_DIR"), "/grr.rs"));

impl From<bool> for DataBlob {

    fn from(value: bool) -> DataBlob {
        DataBlob {
            boolean: Some(value),
            ..Default::default()
        }
    }
}

impl From<i64> for DataBlob {

    fn from(value: i64) -> DataBlob {
        DataBlob {
            integer: Some(value),
            ..Default::default()
        }
    }
}

impl From<f32> for DataBlob {

    fn from(value: f32) -> DataBlob {
        DataBlob {
            float: Some(value),
            ..Default::default()
        }
    }
}

impl From<Vec<u8>> for DataBlob {

    fn from(value: Vec<u8>) -> DataBlob {
        DataBlob {
            data: Some(value),
            ..Default::default()
        }
    }
}

impl From<String> for DataBlob {

    fn from(value: String) -> DataBlob  {
        DataBlob {
            string: Some(value),
            ..Default::default()
        }
    }
}

impl KeyValue {

    /// Creates an empty key-value.
    ///
    /// Both the key and the value are going to be equal to `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg_proto::KeyValue;
    ///
    /// let entry = KeyValue::empty();
    /// assert_eq!(entry.k, None);
    /// assert_eq!(entry.v, None);
    /// ```
    pub fn empty() -> KeyValue {
        KeyValue {
            k: None,
            v: None,
        }
    }

    /// Creates a key-value pair.
    ///
    /// Both the key and the value are going to be equal to the given values.
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg_proto::KeyValue;
    ///
    /// let entry = KeyValue::pair(String::from("foo"), 42i64);
    /// assert_eq!(entry.k.unwrap().string, Some(String::from("foo")));
    /// assert_eq!(entry.v.unwrap().integer, Some(42));
    /// ```
    pub fn pair<K, V>(key: K, value: V) -> KeyValue
    where
        K: Into<DataBlob>,
        V: Into<DataBlob>,
    {
        KeyValue {
            k: Some(key.into()),
            v: Some(value.into()),
        }
    }

    /// Creates a key-only key-value.
    ///
    /// The key is going to be equal to the given value and the key will be
    /// `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg_proto::KeyValue;
    ///
    /// let entry = KeyValue::key(String::from("quux"));
    /// assert_eq!(entry.k.unwrap().string, Some(String::from("quux")));
    /// assert_eq!(entry.v, None);
    /// ```
    pub fn key<K>(key: K) -> KeyValue
    where
        K: Into<DataBlob>,
    {
        KeyValue {
            k: Some(key.into()),
            v: None,
        }
    }
}

impl std::iter::FromIterator<KeyValue> for AttributedDict {

    fn from_iter<I>(iter: I) -> AttributedDict
    where
        I: IntoIterator<Item = KeyValue>,
    {
        AttributedDict {
            dat: iter.into_iter().collect(),
        }
    }
}

impl<K, V> std::iter::FromIterator<(K, V)> for AttributedDict
where
    K: Into<DataBlob>,
    V: Into<DataBlob>,
{
    fn from_iter<I>(iter: I) -> AttributedDict
    where
        I: IntoIterator<Item = (K, V)>,
    {
        let pair = |(key, value)| KeyValue::pair(key, value);
        iter.into_iter().map(pair).collect()
    }
}

impl FromLossy<std::fs::Metadata> for StatEntry {

    fn from_lossy(metadata: std::fs::Metadata) -> StatEntry {
        use std::convert::{TryFrom, TryInto};
        #[cfg(target_family = "unix")]
        use std::os::unix::fs::MetadataExt;

        // TODO: Fix definition of `StatEntry`.
        // `StatEntry` defines insufficient integer width for some fields. For
        // now we just ignore errors, but the definition should be improved.
        let some = |value: u64| Some(value.try_into().unwrap_or(0));

        let atime_secs = ack! {
            metadata.accessed(),
            error: "failed to obtain file access time"
        }.and_then(|atime| ack! {
            secs(atime),
            error: "failed to convert access time to seconds"
        });

        let mtime_secs = ack! {
            metadata.modified(),
            error: "failed to obtain file modification time"
        }.and_then(|mtime| ack! {
            secs(mtime),
            error: "failed to convert modification time to seconds"
        });

        let btime_secs = ack! {
            metadata.created(),
            error: "failed to obtain file creation time"
        }.and_then(|btime| ack! {
            secs(btime),
            error: "failed to convert creation time to seconds"
        });

        #[cfg(target_family = "unix")]
        let ctime_secs = ack! {
            u64::try_from(metadata.ctime()),
            error: "negative inode change time"
        };

        StatEntry {
            #[cfg(target_family = "unix")]
            st_mode: Some(metadata.mode().into()),
            #[cfg(target_family = "unix")]
            st_ino: some(metadata.ino()),
            #[cfg(target_family = "unix")]
            st_dev: some(metadata.dev()),
            #[cfg(target_family = "unix")]
            st_rdev: some(metadata.rdev()),
            #[cfg(target_family = "unix")]
            st_nlink: some(metadata.nlink()),
            #[cfg(target_family = "unix")]
            st_uid: Some(metadata.uid()),
            #[cfg(target_family = "unix")]
            st_gid: Some(metadata.gid()),
            st_size: Some(metadata.len()),
            st_atime: atime_secs,
            st_mtime: mtime_secs,
            #[cfg(target_family = "unix")]
            st_ctime: ctime_secs,
            st_btime: btime_secs,
            #[cfg(target_family = "unix")]
            st_blocks: some(metadata.blocks()),
            #[cfg(target_family = "unix")]
            st_blksize: some(metadata.blksize()),
            ..Default::default()
        }
    }
}

/// An error type for situations where parsing path specification failed.
#[derive(Clone, Debug)]
pub enum ParsePathSpecError {
    /// Attempted to parse an empty path.
    Empty,
    /// Attempted to parse a path of unknown type.
    UnknownType(i32),
    /// Attempted to parse a path of invalid type.
    InvalidType(path_spec::PathType),
}

impl std::fmt::Display for ParsePathSpecError {

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

        match *self {
            Empty => {
                write!(fmt, "empty path")
            }
            UnknownType(value) => {
                write!(fmt, "unknown path type value: {}", value)
            }
            InvalidType(value) => {
                write!(fmt, "invalid path type: {:?}", value)
            }
        }
    }
}

impl std::error::Error for ParsePathSpecError {

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

impl std::convert::TryFrom<PathSpec> for PathBuf {

    type Error = ParsePathSpecError;

    fn try_from(spec: PathSpec) -> Result<PathBuf, ParsePathSpecError> {
        use ParsePathSpecError::*;

        let path_type = spec.pathtype.unwrap_or_default();
        match path_spec::PathType::from_i32(path_type) {
            Some(path_spec::PathType::Os) => (),
            Some(path_type) => return Err(InvalidType(path_type)),
            None => return Err(UnknownType(path_type)),
        };

        match spec.path {
            Some(path) if path.len() > 0 => Ok(PathBuf::from(path)),
            _ => Err(ParsePathSpecError::Empty),
        }
    }
}

impl From<PathBuf> for PathSpec {

    fn from(path: PathBuf) -> PathSpec {
        PathSpec {
            path: Some(path.to_string_lossy().into_owned()),
            pathtype: Some(path_spec::PathType::Os.into()),
            ..Default::default()
        }
    }
}

/// An error type for failures of converting timestamps to nanoseconds.
#[derive(Clone, Debug)]
pub enum NanosError {
    /// Attempted to convert pre-epoch system time.
    Epoch(std::time::SystemTimeError),
    /// Attempted to convert a value outside of 64-bit unsigned integer range.
    Overflow(std::num::TryFromIntError),
}

impl NanosError {

    /// Creates a nanosecond conversion error from an integer overflow error.
    pub fn overflow(error: std::num::TryFromIntError) -> NanosError {
        NanosError::Overflow(error)
    }
}

impl std::fmt::Display for NanosError {

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

        match *self {
            Epoch(ref error) => {
                write!(fmt, "pre-epoch system time: {}", error)
            }
            Overflow(ref error) => {
                write!(fmt, "system time value too big: {}", error)
            }
        }
    }
}

impl std::error::Error for NanosError {

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

        match *self {
            Epoch(ref error) => Some(error),
            Overflow(ref error) => Some(error),
        }
    }
}

impl From<std::time::SystemTimeError> for NanosError {

    fn from(error: std::time::SystemTimeError) -> NanosError {
        NanosError::Epoch(error)
    }
}

/// An error type for failures of converting timestamps to microseconds.
#[derive(Clone, Debug)]
pub struct MicrosError {
    error: NanosError,
}

impl std::fmt::Display for MicrosError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        self.error.fmt(fmt)
    }
}

impl From<NanosError> for MicrosError {

    fn from(error: NanosError) -> MicrosError {
        MicrosError {
            error: error,
        }
    }
}

impl From<MicrosError> for NanosError {

    fn from(error: MicrosError) -> NanosError {
        error.error
    }
}

/// An error type for failures of converting timestamps to seconds.
#[derive(Clone, Debug)]
pub struct SecsError {
    error: NanosError,
}

impl std::fmt::Display for SecsError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        self.error.fmt(fmt)
    }
}

impl From<NanosError> for SecsError {

    fn from(error: NanosError) -> SecsError {
        SecsError {
            error: error,
        }
    }
}

impl From<SecsError> for NanosError {

    fn from(error: SecsError) -> NanosError {
        error.error
    }
}

/// Converts system time into epoch nanoseconds.
///
/// Some GRR messages use epoch nanoseconds for representing timestamps. In such
/// cases this function can be useful to convert from more idiomatic types.
///
/// # Examples
///
/// ```
/// use rrg_proto::nanos;
///
/// assert_eq!(nanos(std::time::UNIX_EPOCH).unwrap(), 0);
/// ```
pub fn nanos(time: std::time::SystemTime) -> Result<u64, NanosError> {
    let time_nanos = time.duration_since(std::time::UNIX_EPOCH)?.as_nanos();

    use std::convert::TryInto as _;
    time_nanos.try_into().map_err(NanosError::overflow)
}

/// Converts system time into epoch microseconds.
///
/// Because most GRR messages use epoch microseconds for representing timestamps
/// this function be be useful to convert from more idiomatic representations.
///
/// # Examples
///
/// ```
/// use rrg_proto::micros;
///
/// assert_eq!(micros(std::time::UNIX_EPOCH).unwrap(), 0);
/// ```
pub fn micros(time: std::time::SystemTime) -> Result<u64, MicrosError> {
    Ok(nanos(time)? / 1_000)
}

/// Converts system time into epoch seconds.
///
/// Some GRR messages use epoch seconds for representing timestamps. In such
/// cases this function can be useful to convert from more idiomatic types.
///
/// # Examples
///
/// ```
/// use rrg_proto::secs;
///
/// assert_eq!(secs(std::time::UNIX_EPOCH).unwrap(), 0);
/// ```
pub fn secs(time: std::time::SystemTime) -> Result<u64, SecsError> {
    Ok(nanos(time)? / 1_000_000_000)
}
<commit_msg>Refactor the time conversion error hierarchy.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

pub mod convert;

use std::path::PathBuf;

use convert::FromLossy;

use rrg_macro::ack;

include!(concat!(env!("OUT_DIR"), "/grr.rs"));

impl From<bool> for DataBlob {

    fn from(value: bool) -> DataBlob {
        DataBlob {
            boolean: Some(value),
            ..Default::default()
        }
    }
}

impl From<i64> for DataBlob {

    fn from(value: i64) -> DataBlob {
        DataBlob {
            integer: Some(value),
            ..Default::default()
        }
    }
}

impl From<f32> for DataBlob {

    fn from(value: f32) -> DataBlob {
        DataBlob {
            float: Some(value),
            ..Default::default()
        }
    }
}

impl From<Vec<u8>> for DataBlob {

    fn from(value: Vec<u8>) -> DataBlob {
        DataBlob {
            data: Some(value),
            ..Default::default()
        }
    }
}

impl From<String> for DataBlob {

    fn from(value: String) -> DataBlob  {
        DataBlob {
            string: Some(value),
            ..Default::default()
        }
    }
}

impl KeyValue {

    /// Creates an empty key-value.
    ///
    /// Both the key and the value are going to be equal to `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg_proto::KeyValue;
    ///
    /// let entry = KeyValue::empty();
    /// assert_eq!(entry.k, None);
    /// assert_eq!(entry.v, None);
    /// ```
    pub fn empty() -> KeyValue {
        KeyValue {
            k: None,
            v: None,
        }
    }

    /// Creates a key-value pair.
    ///
    /// Both the key and the value are going to be equal to the given values.
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg_proto::KeyValue;
    ///
    /// let entry = KeyValue::pair(String::from("foo"), 42i64);
    /// assert_eq!(entry.k.unwrap().string, Some(String::from("foo")));
    /// assert_eq!(entry.v.unwrap().integer, Some(42));
    /// ```
    pub fn pair<K, V>(key: K, value: V) -> KeyValue
    where
        K: Into<DataBlob>,
        V: Into<DataBlob>,
    {
        KeyValue {
            k: Some(key.into()),
            v: Some(value.into()),
        }
    }

    /// Creates a key-only key-value.
    ///
    /// The key is going to be equal to the given value and the key will be
    /// `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg_proto::KeyValue;
    ///
    /// let entry = KeyValue::key(String::from("quux"));
    /// assert_eq!(entry.k.unwrap().string, Some(String::from("quux")));
    /// assert_eq!(entry.v, None);
    /// ```
    pub fn key<K>(key: K) -> KeyValue
    where
        K: Into<DataBlob>,
    {
        KeyValue {
            k: Some(key.into()),
            v: None,
        }
    }
}

impl std::iter::FromIterator<KeyValue> for AttributedDict {

    fn from_iter<I>(iter: I) -> AttributedDict
    where
        I: IntoIterator<Item = KeyValue>,
    {
        AttributedDict {
            dat: iter.into_iter().collect(),
        }
    }
}

impl<K, V> std::iter::FromIterator<(K, V)> for AttributedDict
where
    K: Into<DataBlob>,
    V: Into<DataBlob>,
{
    fn from_iter<I>(iter: I) -> AttributedDict
    where
        I: IntoIterator<Item = (K, V)>,
    {
        let pair = |(key, value)| KeyValue::pair(key, value);
        iter.into_iter().map(pair).collect()
    }
}

impl FromLossy<std::fs::Metadata> for StatEntry {

    fn from_lossy(metadata: std::fs::Metadata) -> StatEntry {
        use std::convert::{TryFrom, TryInto};
        #[cfg(target_family = "unix")]
        use std::os::unix::fs::MetadataExt;

        // TODO: Fix definition of `StatEntry`.
        // `StatEntry` defines insufficient integer width for some fields. For
        // now we just ignore errors, but the definition should be improved.
        let some = |value: u64| Some(value.try_into().unwrap_or(0));

        let atime_secs = ack! {
            metadata.accessed(),
            error: "failed to obtain file access time"
        }.and_then(|atime| ack! {
            secs(atime),
            error: "failed to convert access time to seconds"
        });

        let mtime_secs = ack! {
            metadata.modified(),
            error: "failed to obtain file modification time"
        }.and_then(|mtime| ack! {
            secs(mtime),
            error: "failed to convert modification time to seconds"
        });

        let btime_secs = ack! {
            metadata.created(),
            error: "failed to obtain file creation time"
        }.and_then(|btime| ack! {
            secs(btime),
            error: "failed to convert creation time to seconds"
        });

        #[cfg(target_family = "unix")]
        let ctime_secs = ack! {
            u64::try_from(metadata.ctime()),
            error: "negative inode change time"
        };

        StatEntry {
            #[cfg(target_family = "unix")]
            st_mode: Some(metadata.mode().into()),
            #[cfg(target_family = "unix")]
            st_ino: some(metadata.ino()),
            #[cfg(target_family = "unix")]
            st_dev: some(metadata.dev()),
            #[cfg(target_family = "unix")]
            st_rdev: some(metadata.rdev()),
            #[cfg(target_family = "unix")]
            st_nlink: some(metadata.nlink()),
            #[cfg(target_family = "unix")]
            st_uid: Some(metadata.uid()),
            #[cfg(target_family = "unix")]
            st_gid: Some(metadata.gid()),
            st_size: Some(metadata.len()),
            st_atime: atime_secs,
            st_mtime: mtime_secs,
            #[cfg(target_family = "unix")]
            st_ctime: ctime_secs,
            st_btime: btime_secs,
            #[cfg(target_family = "unix")]
            st_blocks: some(metadata.blocks()),
            #[cfg(target_family = "unix")]
            st_blksize: some(metadata.blksize()),
            ..Default::default()
        }
    }
}

/// An error type for situations where parsing path specification failed.
#[derive(Clone, Debug)]
pub enum ParsePathSpecError {
    /// Attempted to parse an empty path.
    Empty,
    /// Attempted to parse a path of unknown type.
    UnknownType(i32),
    /// Attempted to parse a path of invalid type.
    InvalidType(path_spec::PathType),
}

impl std::fmt::Display for ParsePathSpecError {

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

        match *self {
            Empty => {
                write!(fmt, "empty path")
            }
            UnknownType(value) => {
                write!(fmt, "unknown path type value: {}", value)
            }
            InvalidType(value) => {
                write!(fmt, "invalid path type: {:?}", value)
            }
        }
    }
}

impl std::error::Error for ParsePathSpecError {

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

impl std::convert::TryFrom<PathSpec> for PathBuf {

    type Error = ParsePathSpecError;

    fn try_from(spec: PathSpec) -> Result<PathBuf, ParsePathSpecError> {
        use ParsePathSpecError::*;

        let path_type = spec.pathtype.unwrap_or_default();
        match path_spec::PathType::from_i32(path_type) {
            Some(path_spec::PathType::Os) => (),
            Some(path_type) => return Err(InvalidType(path_type)),
            None => return Err(UnknownType(path_type)),
        };

        match spec.path {
            Some(path) if path.len() > 0 => Ok(PathBuf::from(path)),
            _ => Err(ParsePathSpecError::Empty),
        }
    }
}

impl From<PathBuf> for PathSpec {

    fn from(path: PathBuf) -> PathSpec {
        PathSpec {
            path: Some(path.to_string_lossy().into_owned()),
            pathtype: Some(path_spec::PathType::Os.into()),
            ..Default::default()
        }
    }
}

/// An error type for failures that can occur when converting timestamps.
#[derive(Clone, Debug)]
pub enum TimeConversionError {
    /// Attempted to convert pre-epoch system time.
    Epoch(std::time::SystemTimeError),
    /// Attempted to convert a value outside of 64-bit unsigned integer range.
    Overflow(std::num::TryFromIntError),
}

impl TimeConversionError {

    /// Creates a conversion error from an integer overflow error.
    pub fn overflow(error: std::num::TryFromIntError) -> TimeConversionError {
        TimeConversionError::Overflow(error)
    }
}

impl std::fmt::Display for TimeConversionError {

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

        match *self {
            Epoch(ref error) => {
                write!(fmt, "pre-epoch system time: {}", error)
            }
            Overflow(ref error) => {
                write!(fmt, "system time value too big: {}", error)
            }
        }
    }
}

impl std::error::Error for TimeConversionError {

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

        match *self {
            Epoch(ref error) => Some(error),
            Overflow(ref error) => Some(error),
        }
    }
}

impl From<std::time::SystemTimeError> for TimeConversionError {

    fn from(error: std::time::SystemTimeError) -> TimeConversionError {
        TimeConversionError::Epoch(error)
    }
}

/// Converts system time into epoch nanoseconds.
///
/// Some GRR messages use epoch nanoseconds for representing timestamps. In such
/// cases this function can be useful to convert from more idiomatic types.
///
/// # Examples
///
/// ```
/// use rrg_proto::nanos;
///
/// assert_eq!(nanos(std::time::UNIX_EPOCH).unwrap(), 0);
/// ```
pub fn nanos(time: std::time::SystemTime) -> Result<u64, TimeConversionError> {
    let time_nanos = time.duration_since(std::time::UNIX_EPOCH)?.as_nanos();

    use std::convert::TryInto as _;
    time_nanos.try_into().map_err(TimeConversionError::overflow)
}

/// Converts system time into epoch microseconds.
///
/// Because most GRR messages use epoch microseconds for representing timestamps
/// this function be be useful to convert from more idiomatic representations.
///
/// # Examples
///
/// ```
/// use rrg_proto::micros;
///
/// assert_eq!(micros(std::time::UNIX_EPOCH).unwrap(), 0);
/// ```
pub fn micros(time: std::time::SystemTime) -> Result<u64, TimeConversionError> {
    Ok(nanos(time)? / 1_000)
}

/// Converts system time into epoch seconds.
///
/// Some GRR messages use epoch seconds for representing timestamps. In such
/// cases this function can be useful to convert from more idiomatic types.
///
/// # Examples
///
/// ```
/// use rrg_proto::secs;
///
/// assert_eq!(secs(std::time::UNIX_EPOCH).unwrap(), 0);
/// ```
pub fn secs(time: std::time::SystemTime) -> Result<u64, TimeConversionError> {
    Ok(nanos(time)? / 1_000_000_000)
}
<|endoftext|>


<commit_before>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
#![deny(missing_docs)]
//! Minimal implementation of the [HTTP/1.0](https://tools.ietf.org/html/rfc1945)
//! and [HTTP/1.1](https://www.ietf.org/rfc/rfc2616.txt) protocols.
//!
//! HTTP/1.1 has a mandatory header **Host**, but as this crate is only used
//! for parsing MMDS requests, this header (if present) is ignored.
//!
//! This HTTP implementation is stateless thus it does not support chunking or
//! compression.
//!
//! ## Supported Headers
//! The **micro_http** crate has support for parsing the following **Request**
//! headers:
//! - Content-Length
//! - Expect
//! - Transfer-Encoding
//!
//! The **Response** does not have a public interface for adding headers, but whenever
//! a write to the **Body** is made, the headers **ContentLength** and **MediaType**
//! are automatically updated.
//!
//! ### Media Types
//! The supported media types are:
//! - text/plain
//! - application/json
//!
//! ## Supported Methods
//! The supported HTTP Methods are:
//! - GET
//! - PUT
//! - PATCH
//!
//! ## Supported Status Codes
//! The supported status codes are:
//!
//! - Continue - 100
//! - OK - 200
//! - No Content - 204
//! - Bad Request - 400
//! - Not Found - 404
//! - Internal Server Error - 500
//! - Not Implemented - 501
//!
//! ## Example for parsing an HTTP Request from a slice
//! ```
//! extern crate micro_http;
//! use micro_http::{Request, Version};
//!
//! let http_request = Request::try_from(b"GET http://localhost/home HTTP/1.0\r\n\r\n").unwrap();
//! assert_eq!(http_request.http_version(), Version::Http10);
//! assert_eq!(http_request.uri().get_abs_path(), "/home");
//! ```
//!
//! ## Example for creating an HTTP Response
//! ```
//! extern crate micro_http;
//! use micro_http::{Body, Response, StatusCode, Version, MediaType};
//!
//! let mut response = Response::new(Version::Http10, StatusCode::OK);
//! let body = String::from("This is a test");
//! response.set_body(Body::new(body.clone()));
//! response.set_content_type(MediaType::PlainText);
//!
//! assert!(response.status() == StatusCode::OK);
//! assert_eq!(response.body().unwrap(), Body::new(body));
//! assert_eq!(response.http_version(), Version::Http10);
//!
//! let mut response_buf: [u8; 126] = [0; 126];
//! assert!(response.write_all(&mut response_buf.as_mut()).is_ok());
//! ```

mod common;
mod connection;
mod request;
mod response;
mod server;
use common::ascii;
use common::headers;

pub use connection::{ConnectionError, HttpConnection};
pub use request::{Request, RequestError};
pub use response::{Response, StatusCode};
pub use server::{HttpServer, ServerError, ServerRequest, ServerResponse};

pub use common::headers::{Headers, MediaType};
pub use common::{Body, Method, Version};
<commit_msg>micro_http: updated documentation in lib.rs<commit_after>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
#![deny(missing_docs)]
//! Minimal implementation of the [HTTP/1.0](https://tools.ietf.org/html/rfc1945)
//! and [HTTP/1.1](https://www.ietf.org/rfc/rfc2616.txt) protocols.
//!
//! HTTP/1.1 has a mandatory header **Host**, but as this crate is only used
//! for parsing API requests, this header (if present) is ignored.
//!
//! This HTTP implementation is stateless thus it does not support chunking or
//! compression.
//!
//! ## Supported Headers
//! The **micro_http** crate has support for parsing the following **Request**
//! headers:
//! - Content-Length
//! - Expect
//! - Transfer-Encoding
//!
//! The **Response** does not have a public interface for adding headers, but whenever
//! a write to the **Body** is made, the headers **ContentLength** and **MediaType**
//! are automatically updated.
//!
//! ### Media Types
//! The supported media types are:
//! - text/plain
//! - application/json
//!
//! ## Supported Methods
//! The supported HTTP Methods are:
//! - GET
//! - PUT
//! - PATCH
//!
//! ## Supported Status Codes
//! The supported status codes are:
//!
//! - Continue - 100
//! - OK - 200
//! - No Content - 204
//! - Bad Request - 400
//! - Not Found - 404
//! - Internal Server Error - 500
//! - Not Implemented - 501
//!
//! ## Example for parsing an HTTP Request from a slice
//! ```
//! extern crate micro_http;
//! use micro_http::{Request, Version};
//!
//! let http_request = Request::try_from(b"GET http://localhost/home HTTP/1.0\r\n\r\n").unwrap();
//! assert_eq!(http_request.http_version(), Version::Http10);
//! assert_eq!(http_request.uri().get_abs_path(), "/home");
//! ```
//!
//! ## Example for creating an HTTP Response
//! ```
//! extern crate micro_http;
//! use micro_http::{Body, Response, StatusCode, Version, MediaType};
//!
//! let mut response = Response::new(Version::Http10, StatusCode::OK);
//! let body = String::from("This is a test");
//! response.set_body(Body::new(body.clone()));
//! response.set_content_type(MediaType::PlainText);
//!
//! assert!(response.status() == StatusCode::OK);
//! assert_eq!(response.body().unwrap(), Body::new(body));
//! assert_eq!(response.http_version(), Version::Http10);
//!
//! let mut response_buf: [u8; 126] = [0; 126];
//! assert!(response.write_all(&mut response_buf.as_mut()).is_ok());
//! ```
//!
//! `HttpConnection` can be used for automatic data exchange and parsing when
//! handling a client, but it only supports one stream.
//!
//! For handling multiple clients use `HttpServer`, which multiplexes `HttpConnection`s
//! and offers an easy to use interface. The server can run in either blocking or
//! non-blocking mode. Non-blocking is achieved by using `epoll` to make sure
//! `requests` will never block when called.
//!
//! ## Example for using the server
//!
//! ```
//! extern crate micro_http;
//! use micro_http::{HttpServer, Response, StatusCode};
//!
//! let path_to_socket = "/tmp/example.sock";
//! std::fs::remove_file(path_to_socket).unwrap_or_default();
//!
//! // Start the server.
//! let mut server = HttpServer::new(path_to_socket).unwrap();
//! server.start_server().unwrap();
//!
//! // Connect a client to the server so it doesn't block in our example.
//! let mut socket = std::os::unix::net::UnixStream::connect(path_to_socket).unwrap();
//!
//! // Server loop processing requests.
//! loop {
//!     for request in server.requests().unwrap() {
//!         let response = request.process(|request| {
//!             // Your code here.
//!             Response::new(request.http_version(), StatusCode::NoContent)
//!         });
//!         server.respond(response);
//!     }
//!     // Break this example loop.
//!     break;
//! }
//! ```
//!

mod common;
mod connection;
mod request;
mod response;
mod server;
use common::ascii;
use common::headers;

pub use connection::{ConnectionError, HttpConnection};
pub use request::{Request, RequestError};
pub use response::{Response, StatusCode};
pub use server::{HttpServer, ServerError, ServerRequest, ServerResponse};

pub use common::headers::{Headers, MediaType};
pub use common::{Body, Method, Version};
<|endoftext|>


<commit_before>//! Support for storing Rust data in Erlang terms.
//!
//! A NIF resource allows you to safely store Rust structs in a term, and therefore keep it across
//! NIF calls. The struct will be automatically dropped when the BEAM GC decides that there are no
//! more references to the resource.

use std::mem;
use std::ptr;
use std::ops::Deref;
use std::marker::PhantomData;

use super::{ NifTerm, NifEnv, NifError, NifEncoder, NifDecoder, NifResult };
use ::wrapper::nif_interface::{ NIF_RESOURCE_TYPE, MUTABLE_NIF_RESOURCE_HANDLE, NIF_ENV, NifResourceFlags };
use ::wrapper::nif_interface::{ c_void };

/// Re-export a type used by the `resource_struct_init!` macro.
#[doc(hidden)]
pub use ::wrapper::nif_interface::NIF_RESOURCE_FLAGS;

/// The NifResourceType struct contains a  NIF_RESOURCE_TYPE and a phantom reference to the type it
/// is for. It serves as a holder for the information needed to interact with the Erlang VM about
/// the resource type.
///
/// This is usually stored in an implementation of NifResourceTypeProvider.
#[doc(hidden)]
pub struct NifResourceType<T> {
    pub res: NIF_RESOURCE_TYPE,
    pub struct_type: PhantomData<T>,
}

/// This trait gets implemented for the type we want to put into a resource when
/// resource_struct_init! is called on it. It provides the NifResourceType.
///
/// In most cases the user should not have to worry about this.
#[doc(hidden)]
pub trait NifResourceTypeProvider: Sized + Send + Sync + 'static {
    fn get_type() -> &'static NifResourceType<Self>;
}

impl<T> NifEncoder for ResourceArc<T> where T: NifResourceTypeProvider {
    fn encode<'a>(&self, env: NifEnv<'a>) -> NifTerm<'a> {
        self.as_term(env)
    }
}
impl<'a, T> NifDecoder<'a> for ResourceArc<T> where T: NifResourceTypeProvider + 'a {
    fn decode(term: NifTerm<'a>) -> NifResult<Self> {
        ResourceArc::from_term(term)
    }
}

/// Drop a T that lives in an Erlang resource. (erlang_nif-sys requires us to declare this
/// function safe, but it is of course thoroughly unsafe!)
extern "C" fn resource_destructor<T>(_env: NIF_ENV, handle: MUTABLE_NIF_RESOURCE_HANDLE) {
    unsafe {
        let aligned = align_alloced_mem_for_struct::<T>(handle);
        let res = aligned as *mut T;
        ptr::read(res);
    }
}

/// This is the function that gets called from resource_struct_init! in on_load to create a new
/// resource type.
///
/// # Panics
///
/// Panics if `name` isn't null-terminated.
#[doc(hidden)]
pub fn open_struct_resource_type<'a, T: NifResourceTypeProvider>(env: NifEnv<'a>, name: &str,
                                 flags: NifResourceFlags) -> Option<NifResourceType<T>> {
    let res: Option<NIF_RESOURCE_TYPE> = unsafe {
        ::wrapper::resource::open_resource_type(env.as_c_arg(), name.as_bytes(), Some(resource_destructor::<T>), flags)
    };
    if res.is_some() {
        Some(NifResourceType {
            res: res.unwrap(),
            struct_type: PhantomData,
        })
    } else {
        None
    }
}

fn get_alloc_size_struct<T>() -> usize {
    mem::size_of::<T>() + mem::align_of::<T>()
}

/// Given a pointer `ptr` to an allocation of `get_alloc_size_struct::<T>()` bytes, return the
/// first aligned pointer within the allocation where a `T` may be stored.
/// Unsafe: `ptr` must point to a large enough allocation and not be null.
unsafe fn align_alloced_mem_for_struct<T>(ptr: *const c_void) -> *const c_void {
    let offset = mem::align_of::<T>() - ((ptr as usize) % mem::align_of::<T>());
    ptr.offset(offset as isize)
}

/// This is the struct that holds a reference to a resource. It increments the refcounter for the
/// resource instance on creation, and decrements when dropped.
pub struct ResourceArc<T> where T: NifResourceTypeProvider {
    raw: *const c_void,
    inner: *mut T,
}

// Safe because T is `Sync` and `Send`.
unsafe impl<T> Send for ResourceArc<T> where T: NifResourceTypeProvider {}
unsafe impl<T> Sync for ResourceArc<T> where T: NifResourceTypeProvider {}

impl<T> ResourceArc<T> where T: NifResourceTypeProvider {
    /// Makes a new ResourceArc from the given type. Note that the type must have
    /// NifResourceTypeProvider implemented for it. See module documentation for info on this.
    pub fn new(data: T) -> Self {
        let alloc_size = get_alloc_size_struct::<T>();
        let mem_raw = unsafe { ::wrapper::resource::alloc_resource(T::get_type().res, alloc_size) };
        let aligned_mem = unsafe { align_alloced_mem_for_struct::<T>(mem_raw) as *mut T };

        unsafe { ptr::write(aligned_mem, data) };

        ResourceArc {
            raw: mem_raw,
            inner: aligned_mem,
        }
    }

    fn from_term(term: NifTerm) -> Result<Self, NifError> {
        let res_resource = match unsafe { ::wrapper::resource::get_resource(term.get_env().as_c_arg(), term.as_c_arg(), T::get_type().res) } {
            Some(res) => res,
            None => return Err(NifError::BadArg),
        };
        unsafe { ::wrapper::resource::keep_resource(res_resource); }
        let casted_ptr = unsafe { align_alloced_mem_for_struct::<T>(res_resource) as *mut T };
        Ok(ResourceArc {
            raw: res_resource,
            inner: casted_ptr,
        })
    }

    fn as_term<'a>(&self, env: NifEnv<'a>) -> NifTerm<'a> {
        unsafe { NifTerm::new(env, ::wrapper::resource::make_resource(env.as_c_arg(), self.raw)) }
    }

    fn as_c_arg(&mut self) -> *const c_void {
        self.raw
    }

    fn inner(&self) -> &T {
        unsafe { &*self.inner }
    }
}

impl<T> Deref for ResourceArc<T> where T: NifResourceTypeProvider {
    type Target = T;

    fn deref(&self) -> &T {
        self.inner()
    }
}

impl<T> Clone for ResourceArc<T> where T: NifResourceTypeProvider {
    /// Cloning a `ResourceArc` simply increments the reference count for the
    /// resource. The `T` value is not cloned.
    fn clone(&self) -> Self {
        unsafe { ::wrapper::resource::keep_resource(self.raw); }
        ResourceArc {
            raw: self.raw,
            inner: self.inner,
        }
    }
}

impl<T> Drop for ResourceArc<T> where T: NifResourceTypeProvider {
    /// When a `ResourceArc` is dropped, the reference count is decremented. If
    /// there are no other references to the resource, the `T` value is dropped.
    ///
    /// However, note that in general, the Rust value in a resource is dropped
    /// at an unpredictable time: whenever the VM decides to do garbage
    /// collection.
    fn drop(&mut self) {
        unsafe { ::wrapper::nif_interface::enif_release_resource(self.as_c_arg()) };
    }
}

#[macro_export]
macro_rules! resource_struct_init {
    ($struct_name:ident, $env: ident) => {
        {
            static mut STRUCT_TYPE: Option<$crate::resource::NifResourceType<$struct_name>> = None;

            let temp_struct_type =
                match $crate::resource::open_struct_resource_type::<$struct_name>(
                    $env,
                    concat!(stringify!($struct_name), "\x00"),
                    $crate::resource::NIF_RESOURCE_FLAGS::ERL_NIF_RT_CREATE
                    ) {
                    Some(inner) => inner,
                    None => {
                        println!("Failure in creating resource type");
                        return false;
                    }
                };
            unsafe { STRUCT_TYPE = Some(temp_struct_type) };

            impl $crate::resource::NifResourceTypeProvider for $struct_name {
                fn get_type() -> &'static $crate::resource::NifResourceType<Self> {
                    unsafe { &STRUCT_TYPE }.as_ref().unwrap()
                }
            }
        }
    }
}
<commit_msg>Documentation for `ResourceArc`.<commit_after>//! Support for storing Rust data in Erlang terms.
//!
//! A NIF resource allows you to safely store Rust structs in a term, and therefore keep it across
//! NIF calls. The struct will be automatically dropped when the BEAM GC decides that there are no
//! more references to the resource.

use std::mem;
use std::ptr;
use std::ops::Deref;
use std::marker::PhantomData;

use super::{ NifTerm, NifEnv, NifError, NifEncoder, NifDecoder, NifResult };
use ::wrapper::nif_interface::{ NIF_RESOURCE_TYPE, MUTABLE_NIF_RESOURCE_HANDLE, NIF_ENV, NifResourceFlags };
use ::wrapper::nif_interface::{ c_void };

/// Re-export a type used by the `resource_struct_init!` macro.
#[doc(hidden)]
pub use ::wrapper::nif_interface::NIF_RESOURCE_FLAGS;

/// The NifResourceType struct contains a  NIF_RESOURCE_TYPE and a phantom reference to the type it
/// is for. It serves as a holder for the information needed to interact with the Erlang VM about
/// the resource type.
///
/// This is usually stored in an implementation of NifResourceTypeProvider.
#[doc(hidden)]
pub struct NifResourceType<T> {
    pub res: NIF_RESOURCE_TYPE,
    pub struct_type: PhantomData<T>,
}

/// This trait gets implemented for the type we want to put into a resource when
/// resource_struct_init! is called on it. It provides the NifResourceType.
///
/// In most cases the user should not have to worry about this.
#[doc(hidden)]
pub trait NifResourceTypeProvider: Sized + Send + Sync + 'static {
    fn get_type() -> &'static NifResourceType<Self>;
}

impl<T> NifEncoder for ResourceArc<T> where T: NifResourceTypeProvider {
    fn encode<'a>(&self, env: NifEnv<'a>) -> NifTerm<'a> {
        self.as_term(env)
    }
}
impl<'a, T> NifDecoder<'a> for ResourceArc<T> where T: NifResourceTypeProvider + 'a {
    fn decode(term: NifTerm<'a>) -> NifResult<Self> {
        ResourceArc::from_term(term)
    }
}

/// Drop a T that lives in an Erlang resource. (erlang_nif-sys requires us to declare this
/// function safe, but it is of course thoroughly unsafe!)
extern "C" fn resource_destructor<T>(_env: NIF_ENV, handle: MUTABLE_NIF_RESOURCE_HANDLE) {
    unsafe {
        let aligned = align_alloced_mem_for_struct::<T>(handle);
        let res = aligned as *mut T;
        ptr::read(res);
    }
}

/// This is the function that gets called from resource_struct_init! in on_load to create a new
/// resource type.
///
/// # Panics
///
/// Panics if `name` isn't null-terminated.
#[doc(hidden)]
pub fn open_struct_resource_type<'a, T: NifResourceTypeProvider>(env: NifEnv<'a>, name: &str,
                                 flags: NifResourceFlags) -> Option<NifResourceType<T>> {
    let res: Option<NIF_RESOURCE_TYPE> = unsafe {
        ::wrapper::resource::open_resource_type(env.as_c_arg(), name.as_bytes(), Some(resource_destructor::<T>), flags)
    };
    if res.is_some() {
        Some(NifResourceType {
            res: res.unwrap(),
            struct_type: PhantomData,
        })
    } else {
        None
    }
}

fn get_alloc_size_struct<T>() -> usize {
    mem::size_of::<T>() + mem::align_of::<T>()
}

/// Given a pointer `ptr` to an allocation of `get_alloc_size_struct::<T>()` bytes, return the
/// first aligned pointer within the allocation where a `T` may be stored.
/// Unsafe: `ptr` must point to a large enough allocation and not be null.
unsafe fn align_alloced_mem_for_struct<T>(ptr: *const c_void) -> *const c_void {
    let offset = mem::align_of::<T>() - ((ptr as usize) % mem::align_of::<T>());
    ptr.offset(offset as isize)
}

/// A reference to a resource of type `T`.
///
/// This type is like `std::sync::Arc`: it provides thread-safe, reference-counted storage for Rust
/// data that can be shared across threads. Data stored this way is immutable by default. If you
/// need to modify data in a resource, use a `std::sync::Mutex` or `RwLock`.
///
/// Rust code and Erlang code can both have references to the same resource at the same time.  Rust
/// code uses `ResourceArc`; in Erlang, a reference to a resource is a kind of term.  You can
/// convert back and forth between the two using `NifEncoder` and `NifDecoder`.
pub struct ResourceArc<T> where T: NifResourceTypeProvider {
    raw: *const c_void,
    inner: *mut T,
}

// Safe because T is `Sync` and `Send`.
unsafe impl<T> Send for ResourceArc<T> where T: NifResourceTypeProvider {}
unsafe impl<T> Sync for ResourceArc<T> where T: NifResourceTypeProvider {}

impl<T> ResourceArc<T> where T: NifResourceTypeProvider {
    /// Makes a new ResourceArc from the given type. Note that the type must have
    /// NifResourceTypeProvider implemented for it. See module documentation for info on this.
    pub fn new(data: T) -> Self {
        let alloc_size = get_alloc_size_struct::<T>();
        let mem_raw = unsafe { ::wrapper::resource::alloc_resource(T::get_type().res, alloc_size) };
        let aligned_mem = unsafe { align_alloced_mem_for_struct::<T>(mem_raw) as *mut T };

        unsafe { ptr::write(aligned_mem, data) };

        ResourceArc {
            raw: mem_raw,
            inner: aligned_mem,
        }
    }

    fn from_term(term: NifTerm) -> Result<Self, NifError> {
        let res_resource = match unsafe { ::wrapper::resource::get_resource(term.get_env().as_c_arg(), term.as_c_arg(), T::get_type().res) } {
            Some(res) => res,
            None => return Err(NifError::BadArg),
        };
        unsafe { ::wrapper::resource::keep_resource(res_resource); }
        let casted_ptr = unsafe { align_alloced_mem_for_struct::<T>(res_resource) as *mut T };
        Ok(ResourceArc {
            raw: res_resource,
            inner: casted_ptr,
        })
    }

    fn as_term<'a>(&self, env: NifEnv<'a>) -> NifTerm<'a> {
        unsafe { NifTerm::new(env, ::wrapper::resource::make_resource(env.as_c_arg(), self.raw)) }
    }

    fn as_c_arg(&mut self) -> *const c_void {
        self.raw
    }

    fn inner(&self) -> &T {
        unsafe { &*self.inner }
    }
}

impl<T> Deref for ResourceArc<T> where T: NifResourceTypeProvider {
    type Target = T;

    fn deref(&self) -> &T {
        self.inner()
    }
}

impl<T> Clone for ResourceArc<T> where T: NifResourceTypeProvider {
    /// Cloning a `ResourceArc` simply increments the reference count for the
    /// resource. The `T` value is not cloned.
    fn clone(&self) -> Self {
        unsafe { ::wrapper::resource::keep_resource(self.raw); }
        ResourceArc {
            raw: self.raw,
            inner: self.inner,
        }
    }
}

impl<T> Drop for ResourceArc<T> where T: NifResourceTypeProvider {
    /// When a `ResourceArc` is dropped, the reference count is decremented. If
    /// there are no other references to the resource, the `T` value is dropped.
    ///
    /// However, note that in general, the Rust value in a resource is dropped
    /// at an unpredictable time: whenever the VM decides to do garbage
    /// collection.
    fn drop(&mut self) {
        unsafe { ::wrapper::nif_interface::enif_release_resource(self.as_c_arg()) };
    }
}

#[macro_export]
macro_rules! resource_struct_init {
    ($struct_name:ident, $env: ident) => {
        {
            static mut STRUCT_TYPE: Option<$crate::resource::NifResourceType<$struct_name>> = None;

            let temp_struct_type =
                match $crate::resource::open_struct_resource_type::<$struct_name>(
                    $env,
                    concat!(stringify!($struct_name), "\x00"),
                    $crate::resource::NIF_RESOURCE_FLAGS::ERL_NIF_RT_CREATE
                    ) {
                    Some(inner) => inner,
                    None => {
                        println!("Failure in creating resource type");
                        return false;
                    }
                };
            unsafe { STRUCT_TYPE = Some(temp_struct_type) };

            impl $crate::resource::NifResourceTypeProvider for $struct_name {
                fn get_type() -> &'static $crate::resource::NifResourceType<Self> {
                    unsafe { &STRUCT_TYPE }.as_ref().unwrap()
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Seat global utilities
//!
//! This module provides you with utilities for handling the seat globals
//! and the associated input wayland objects.
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! ```
//! # extern crate wayland_server;
//! # #[macro_use] extern crate smithay;
//!
//! use smithay::seat::Seat;
//!
//! # fn main(){
//! # let (_display, mut event_loop) = wayland_server::create_display();
//! // insert the seat:
//! let (seat_state_token, seat_global) = Seat::new(
//!     &mut event_loop,
//!     "seat-0".into(), // the name of the seat, will be advertize to clients
//!     None /* insert a logger here*/
//! );
//! # }
//! ```
//!
//! ### Run usage
//!
//! Once the seat is initialized, you can add capabilities to it.
//!
//! Currently, only pointer and keyboard capabilities are supported by
//! smithay.
//!
//! You can add these capabilities via methods of the `Seat` struct that was
//! inserted in the event loop, that you can retreive via its token:
//!
//! ```
//! # extern crate wayland_server;
//! # #[macro_use] extern crate smithay;
//! #
//! # use smithay::seat::Seat;
//! #
//! # fn main(){
//! # let (_display, mut event_loop) = wayland_server::create_display();
//! # let (seat_state_token, seat_global) = Seat::new(
//! #     &mut event_loop,
//! #     "seat-0".into(), // the name of the seat, will be advertize to clients
//! #     None /* insert a logger here*/
//! # );
//! let pointer_handle = event_loop.state().get_mut(&seat_state_token).add_pointer();
//! # }
//! ```
//!
//! These handles can be cloned and sent accross thread, so you can keep one around
//! in your event-handling code to forward inputs to your clients.

mod keyboard;
mod pointer;

pub use self::keyboard::{Error as KeyboardError, KeyboardHandle};
pub use self::pointer::PointerHandle;
use wayland_server::{Client, EventLoop, EventLoopHandle, Global, Liveness, Resource, StateToken};
use wayland_server::protocol::{wl_keyboard, wl_pointer, wl_seat};

/// Internal data of a seat global
///
/// This struct gives you access to the control of the
/// capabilities of the associated seat.
///
/// It is directly inserted in the event loop by its `new` method.
///
/// See module-level documentation for details of use.
pub struct Seat {
    log: ::slog::Logger,
    name: String,
    pointer: Option<PointerHandle>,
    keyboard: Option<KeyboardHandle>,
    known_seats: Vec<wl_seat::WlSeat>,
}

impl Seat {
    /// Create a new seat global
    ///
    /// A new seat global is created with given name and inserted
    /// into this event loop.
    ///
    /// You are provided with the state token to retrieve it (allowing
    /// you to add or remove capabilities from it), and the global handle,
    /// in case you want to remove it.
    pub fn new<L>(evl: &mut EventLoop, name: String, logger: L)
                  -> (StateToken<Seat>, Global<wl_seat::WlSeat, StateToken<Seat>>)
    where
        L: Into<Option<::slog::Logger>>,
    {
        let log = ::slog_or_stdlog(logger);
        let seat = Seat {
            log: log.new(o!("smithay_module" => "seat_handler")),
            name: name,
            pointer: None,
            keyboard: None,
            known_seats: Vec::new(),
        };
        let token = evl.state().insert(seat);
        // TODO: support version 5 (axis)
        let global = evl.register_global(4, seat_global_bind, token.clone());
        (token, global)
    }

    /// Adds the pointer capability to this seat
    ///
    /// You are provided a `PointerHandle`, which allows you to send input events
    /// to this keyboard. This handle can be cloned and sent accross threads.
    ///
    /// Calling this method on a seat that already has a pointer capability
    /// will overwrite it, and will be seen by the clients as if the
    /// mouse was unplugged and a new one was plugged.
    pub fn add_pointer(&mut self) -> PointerHandle {
        let pointer = self::pointer::create_pointer_handler();
        if self.pointer.is_some() {
            // there is already a pointer, remove it and notify the clients
            // of the change
            self.pointer = None;
            let caps = self.compute_caps();
            for seat in &self.known_seats {
                seat.capabilities(caps);
            }
        }
        self.pointer = Some(pointer.clone());
        let caps = self.compute_caps();
        for seat in &self.known_seats {
            seat.capabilities(caps);
        }
        pointer
    }

    /// Remove the pointer capability from this seat
    ///
    /// Clients will be appropriately notified.
    pub fn remove_pointer(&mut self) {
        if self.pointer.is_some() {
            self.pointer = None;
            let caps = self.compute_caps();
            for seat in &self.known_seats {
                seat.capabilities(caps);
            }
        }
    }

    /// Adds the keyboard capability to this seat
    ///
    /// You are provided a `KbdHandle`, which allows you to send input events
    /// to this keyboard. This handle can be cloned and sent accross threads.
    ///
    /// You also provide a Model/Layout/Variant/Options specification of the
    /// keymap to be used for this keyboard, as well as any repeat-info that
    /// will be forwarded to the clients.
    ///
    /// Calling this method on a seat that already has a keyboard capability
    /// will overwrite it, and will be seen by the clients as if the
    /// keyboard was unplugged and a new one was plugged.
    pub fn add_keyboard(&mut self, model: &str, layout: &str, variant: &str, options: Option<String>,
                        repeat_delay: i32, repeat_rate: i32)
                        -> Result<KeyboardHandle, KeyboardError> {
        let keyboard = self::keyboard::create_keyboard_handler(
            "evdev", // we need this one
            model,
            layout,
            variant,
            options,
            repeat_delay,
            repeat_rate,
            self.log.clone(),
        )?;
        if self.keyboard.is_some() {
            // there is already a keyboard, remove it and notify the clients
            // of the change
            self.keyboard = None;
            let caps = self.compute_caps();
            for seat in &self.known_seats {
                seat.capabilities(caps);
            }
        }
        self.keyboard = Some(keyboard.clone());
        let caps = self.compute_caps();
        for seat in &self.known_seats {
            seat.capabilities(caps);
        }
        Ok(keyboard)
    }

    /// Remove the keyboard capability from this seat
    ///
    /// Clients will be appropriately notified.
    pub fn remove_keyboard(&mut self) {
        if self.keyboard.is_some() {
            self.keyboard = None;
            let caps = self.compute_caps();
            for seat in &self.known_seats {
                seat.capabilities(caps);
            }
        }
    }

    /// Cleanup internal states from old resources
    ///
    /// Deletes all remnnant of ressources from clients that
    /// are now disconnected.
    ///
    /// It can be wise to run this from time to time.
    pub fn cleanup(&mut self) {
        if let Some(ref pointer) = self.pointer {
            pointer.cleanup_old_pointers();
        }
        if let Some(ref kbd) = self.keyboard {
            kbd.cleanup_old_kbds();
        }
        self.known_seats.retain(|s| s.status() == Liveness::Alive);
    }

    fn compute_caps(&self) -> wl_seat::Capability {
        let mut caps = wl_seat::Capability::empty();
        if self.pointer.is_some() {
            caps |= wl_seat::Pointer;
        }
        if self.keyboard.is_some() {
            caps |= wl_seat::Keyboard;
        }
        caps
    }
}

fn seat_global_bind(evlh: &mut EventLoopHandle, token: &mut StateToken<Seat>, _: &Client,
                    seat: wl_seat::WlSeat) {
    evlh.register(&seat, seat_implementation(), token.clone(), None);
    let seat_mgr = evlh.state().get_mut(token);
    seat.name(seat_mgr.name.clone());
    seat.capabilities(seat_mgr.compute_caps());
    seat_mgr.known_seats.push(seat);
}

fn seat_implementation() -> wl_seat::Implementation<StateToken<Seat>> {
    wl_seat::Implementation {
        get_pointer: |evlh, token, _, _, pointer| {
            evlh.register(&pointer, pointer_implementation(), (), None);
            if let Some(ref ptr_handle) = evlh.state().get(token).pointer {
                ptr_handle.new_pointer(pointer);
            } else {
                // we should send a protocol error... but the protocol does not allow
                // us, so this pointer will just remain inactive ¯\_(ツ)_/¯
            }
        },
        get_keyboard: |evlh, token, _, _, keyboard| {
            evlh.register(&keyboard, keyboard_implementation(), (), None);
            if let Some(ref kbd_handle) = evlh.state().get(token).keyboard {
                kbd_handle.new_kbd(keyboard);
            } else {
                // same, should error but cant
            }
        },
        get_touch: |_evlh, _token, _, _, _touch| {
            // TODO
        },
        release: |_, _, _, _| {},
    }
}

fn pointer_implementation() -> wl_pointer::Implementation<()> {
    wl_pointer::Implementation {
        set_cursor: |_, _, _, _, _, _, _, _| {},
        release: |_, _, _, _| {},
    }
}

fn keyboard_implementation() -> wl_keyboard::Implementation<()> {
    wl_keyboard::Implementation {
        release: |_, _, _, _| {},
    }
}
<commit_msg>seat: increase logger metadata<commit_after>//! Seat global utilities
//!
//! This module provides you with utilities for handling the seat globals
//! and the associated input wayland objects.
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! ```
//! # extern crate wayland_server;
//! # #[macro_use] extern crate smithay;
//!
//! use smithay::seat::Seat;
//!
//! # fn main(){
//! # let (_display, mut event_loop) = wayland_server::create_display();
//! // insert the seat:
//! let (seat_state_token, seat_global) = Seat::new(
//!     &mut event_loop,
//!     "seat-0".into(), // the name of the seat, will be advertize to clients
//!     None /* insert a logger here*/
//! );
//! # }
//! ```
//!
//! ### Run usage
//!
//! Once the seat is initialized, you can add capabilities to it.
//!
//! Currently, only pointer and keyboard capabilities are supported by
//! smithay.
//!
//! You can add these capabilities via methods of the `Seat` struct that was
//! inserted in the event loop, that you can retreive via its token:
//!
//! ```
//! # extern crate wayland_server;
//! # #[macro_use] extern crate smithay;
//! #
//! # use smithay::seat::Seat;
//! #
//! # fn main(){
//! # let (_display, mut event_loop) = wayland_server::create_display();
//! # let (seat_state_token, seat_global) = Seat::new(
//! #     &mut event_loop,
//! #     "seat-0".into(), // the name of the seat, will be advertize to clients
//! #     None /* insert a logger here*/
//! # );
//! let pointer_handle = event_loop.state().get_mut(&seat_state_token).add_pointer();
//! # }
//! ```
//!
//! These handles can be cloned and sent accross thread, so you can keep one around
//! in your event-handling code to forward inputs to your clients.

mod keyboard;
mod pointer;

pub use self::keyboard::{Error as KeyboardError, KeyboardHandle};
pub use self::pointer::PointerHandle;
use wayland_server::{Client, EventLoop, EventLoopHandle, Global, Liveness, Resource, StateToken};
use wayland_server::protocol::{wl_keyboard, wl_pointer, wl_seat};

/// Internal data of a seat global
///
/// This struct gives you access to the control of the
/// capabilities of the associated seat.
///
/// It is directly inserted in the event loop by its `new` method.
///
/// See module-level documentation for details of use.
pub struct Seat {
    log: ::slog::Logger,
    name: String,
    pointer: Option<PointerHandle>,
    keyboard: Option<KeyboardHandle>,
    known_seats: Vec<wl_seat::WlSeat>,
}

impl Seat {
    /// Create a new seat global
    ///
    /// A new seat global is created with given name and inserted
    /// into this event loop.
    ///
    /// You are provided with the state token to retrieve it (allowing
    /// you to add or remove capabilities from it), and the global handle,
    /// in case you want to remove it.
    pub fn new<L>(evl: &mut EventLoop, name: String, logger: L)
                  -> (StateToken<Seat>, Global<wl_seat::WlSeat, StateToken<Seat>>)
    where
        L: Into<Option<::slog::Logger>>,
    {
        let log = ::slog_or_stdlog(logger);
        let seat = Seat {
            log: log.new(o!("smithay_module" => "seat_handler", "seat_name" => name.clone())),
            name: name,
            pointer: None,
            keyboard: None,
            known_seats: Vec::new(),
        };
        let token = evl.state().insert(seat);
        // TODO: support version 5 (axis)
        let global = evl.register_global(4, seat_global_bind, token.clone());
        (token, global)
    }

    /// Adds the pointer capability to this seat
    ///
    /// You are provided a `PointerHandle`, which allows you to send input events
    /// to this keyboard. This handle can be cloned and sent accross threads.
    ///
    /// Calling this method on a seat that already has a pointer capability
    /// will overwrite it, and will be seen by the clients as if the
    /// mouse was unplugged and a new one was plugged.
    pub fn add_pointer(&mut self) -> PointerHandle {
        let pointer = self::pointer::create_pointer_handler();
        if self.pointer.is_some() {
            // there is already a pointer, remove it and notify the clients
            // of the change
            self.pointer = None;
            let caps = self.compute_caps();
            for seat in &self.known_seats {
                seat.capabilities(caps);
            }
        }
        self.pointer = Some(pointer.clone());
        let caps = self.compute_caps();
        for seat in &self.known_seats {
            seat.capabilities(caps);
        }
        pointer
    }

    /// Remove the pointer capability from this seat
    ///
    /// Clients will be appropriately notified.
    pub fn remove_pointer(&mut self) {
        if self.pointer.is_some() {
            self.pointer = None;
            let caps = self.compute_caps();
            for seat in &self.known_seats {
                seat.capabilities(caps);
            }
        }
    }

    /// Adds the keyboard capability to this seat
    ///
    /// You are provided a `KbdHandle`, which allows you to send input events
    /// to this keyboard. This handle can be cloned and sent accross threads.
    ///
    /// You also provide a Model/Layout/Variant/Options specification of the
    /// keymap to be used for this keyboard, as well as any repeat-info that
    /// will be forwarded to the clients.
    ///
    /// Calling this method on a seat that already has a keyboard capability
    /// will overwrite it, and will be seen by the clients as if the
    /// keyboard was unplugged and a new one was plugged.
    pub fn add_keyboard(&mut self, model: &str, layout: &str, variant: &str, options: Option<String>,
                        repeat_delay: i32, repeat_rate: i32)
                        -> Result<KeyboardHandle, KeyboardError> {
        let keyboard = self::keyboard::create_keyboard_handler(
            "evdev", // we need this one
            model,
            layout,
            variant,
            options,
            repeat_delay,
            repeat_rate,
            self.log.clone(),
        )?;
        if self.keyboard.is_some() {
            // there is already a keyboard, remove it and notify the clients
            // of the change
            self.keyboard = None;
            let caps = self.compute_caps();
            for seat in &self.known_seats {
                seat.capabilities(caps);
            }
        }
        self.keyboard = Some(keyboard.clone());
        let caps = self.compute_caps();
        for seat in &self.known_seats {
            seat.capabilities(caps);
        }
        Ok(keyboard)
    }

    /// Remove the keyboard capability from this seat
    ///
    /// Clients will be appropriately notified.
    pub fn remove_keyboard(&mut self) {
        if self.keyboard.is_some() {
            self.keyboard = None;
            let caps = self.compute_caps();
            for seat in &self.known_seats {
                seat.capabilities(caps);
            }
        }
    }

    /// Cleanup internal states from old resources
    ///
    /// Deletes all remnnant of ressources from clients that
    /// are now disconnected.
    ///
    /// It can be wise to run this from time to time.
    pub fn cleanup(&mut self) {
        if let Some(ref pointer) = self.pointer {
            pointer.cleanup_old_pointers();
        }
        if let Some(ref kbd) = self.keyboard {
            kbd.cleanup_old_kbds();
        }
        self.known_seats.retain(|s| s.status() == Liveness::Alive);
    }

    fn compute_caps(&self) -> wl_seat::Capability {
        let mut caps = wl_seat::Capability::empty();
        if self.pointer.is_some() {
            caps |= wl_seat::Pointer;
        }
        if self.keyboard.is_some() {
            caps |= wl_seat::Keyboard;
        }
        caps
    }
}

fn seat_global_bind(evlh: &mut EventLoopHandle, token: &mut StateToken<Seat>, _: &Client,
                    seat: wl_seat::WlSeat) {
    evlh.register(&seat, seat_implementation(), token.clone(), None);
    let seat_mgr = evlh.state().get_mut(token);
    seat.name(seat_mgr.name.clone());
    seat.capabilities(seat_mgr.compute_caps());
    seat_mgr.known_seats.push(seat);
}

fn seat_implementation() -> wl_seat::Implementation<StateToken<Seat>> {
    wl_seat::Implementation {
        get_pointer: |evlh, token, _, _, pointer| {
            evlh.register(&pointer, pointer_implementation(), (), None);
            if let Some(ref ptr_handle) = evlh.state().get(token).pointer {
                ptr_handle.new_pointer(pointer);
            } else {
                // we should send a protocol error... but the protocol does not allow
                // us, so this pointer will just remain inactive ¯\_(ツ)_/¯
            }
        },
        get_keyboard: |evlh, token, _, _, keyboard| {
            evlh.register(&keyboard, keyboard_implementation(), (), None);
            if let Some(ref kbd_handle) = evlh.state().get(token).keyboard {
                kbd_handle.new_kbd(keyboard);
            } else {
                // same, should error but cant
            }
        },
        get_touch: |_evlh, _token, _, _, _touch| {
            // TODO
        },
        release: |_, _, _, _| {},
    }
}

fn pointer_implementation() -> wl_pointer::Implementation<()> {
    wl_pointer::Implementation {
        set_cursor: |_, _, _, _, _, _, _, _| {},
        release: |_, _, _, _| {},
    }
}

fn keyboard_implementation() -> wl_keyboard::Implementation<()> {
    wl_keyboard::Implementation {
        release: |_, _, _, _| {},
    }
}
<|endoftext|>


<commit_before>use config::{Config, File, FileFormat};
use failure::ResultExt;
use serde::Deserialize;
use slog_scope::warn;
use std::path::PathBuf;

use crate::Error;

#[derive(Debug, Clone, Deserialize)]
pub struct StreetExclusion {
    pub highways: Option<Vec<String>>,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Street {
    pub exclusion: StreetExclusion,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Poi {}

#[derive(Debug, Clone, Deserialize)]
pub struct Admin {}

#[derive(Debug, Clone, Deserialize)]
pub struct Settings {
    pub street: Option<Street>,
    pub poi: Option<Poi>,
    pub admin: Option<Admin>,
}

impl Settings {
    pub fn new(config_dir: &Option<PathBuf>, name: &Option<String>) -> Result<Self, Error> {
        let mut config = Config::new();
        let config_dir = config_dir.clone();
        match config_dir {
            Some(mut dir) => {
                dir.push("default");
                // Start off by merging in the "default" configuration file

                if let Some(path) = dir.to_str() {
                    config.merge(File::with_name(path)).with_context(|e| {
                        format!(
                            "Could not merge default configuration from file {}: {}",
                            path, e
                        )
                    })?;
                } else {
                    return Err(failure::err_msg(format!(
                        "Could not read default settings in '{}'",
                        dir.display()
                    )));
                }

                // If we provided a special configuration, merge it.
                if let Some(name) = name {
                    dir.pop(); // remove the default
                    let name_path = dir.join(name);

                    if let Some(path) = name_path.to_str() {
                        config
                            .merge(File::with_name(path).required(true))
                            .with_context(|e| {
                                format!(
                                    "Could not merge {} configuration in file {}: {}",
                                    name, path, e
                                )
                            })?;
                    } else {
                        return Err(failure::err_msg(format!(
                            "Could not read {} settings in '{}'",
                            name,
                            name_path.display()
                        )));
                    }
                }
            }
            None => {
                if name.is_some() {
                    // If the user set the 'settings' at the command line, he should
                    // also have used the 'config_dir' option. So we issue a warning,
                    // and leave with an error because the expected configuration can
                    // not be read.
                    warn!("settings option used without the 'config_dir' option. Please set the config directory with --config-dir.");
                    return Err(failure::err_msg(String::from(
                        "Could not build program settings",
                    )));
                }
                config
                    .merge(File::from_str(
                        include_str!("../config/default.toml"),
                        FileFormat::Toml,
                    ))
                    .with_context(|e| {
                        format!(
                            "Could not merge default configuration from file at compile time: {}",
                            e
                        )
                    })?;
            }
        }
        // You can deserialize (and thus freeze) the entire configuration as
        config.try_into().map_err(|e| {
            failure::err_msg(format!(
                "Could not generate settings from configuration: {}",
                e
            ))
        })
    }
}
<commit_msg>Log which configuration is used<commit_after>use config::{Config, File, FileFormat};
use failure::ResultExt;
use serde::Deserialize;
use slog_scope::{info, warn};
use std::path::PathBuf;

use crate::Error;

#[derive(Debug, Clone, Deserialize)]
pub struct StreetExclusion {
    pub highways: Option<Vec<String>>,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Street {
    pub exclusion: StreetExclusion,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Poi {}

#[derive(Debug, Clone, Deserialize)]
pub struct Admin {}

#[derive(Debug, Clone, Deserialize)]
pub struct Settings {
    pub street: Option<Street>,
    pub poi: Option<Poi>,
    pub admin: Option<Admin>,
}

impl Settings {
    pub fn new(config_dir: &Option<PathBuf>, name: &Option<String>) -> Result<Self, Error> {
        let mut config = Config::new();
        let config_dir = config_dir.clone();
        match config_dir {
            Some(mut dir) => {
                dir.push("default");
                // Start off by merging in the "default" configuration file

                if let Some(path) = dir.to_str() {
                    info!("using configuration from {}", path);
                    config.merge(File::with_name(path)).with_context(|e| {
                        format!(
                            "Could not merge default configuration from file {}: {}",
                            path, e
                        )
                    })?;
                } else {
                    return Err(failure::err_msg(format!(
                        "Could not read default settings in '{}'",
                        dir.display()
                    )));
                }

                dir.pop(); // remove the default
                           // If we provided a special configuration, merge it.
                if let Some(name) = name {
                    dir.push(name);

                    if let Some(path) = dir.to_str() {
                        info!("using configuration from {}", path);
                        config
                            .merge(File::with_name(path).required(true))
                            .with_context(|e| {
                                format!(
                                    "Could not merge {} configuration in file {}: {}",
                                    name, path, e
                                )
                            })?;
                    } else {
                        return Err(failure::err_msg(format!(
                            "Could not read configuration for '{}'",
                            name,
                        )));
                    }
                    dir.pop();
                }
            }
            None => {
                if name.is_some() {
                    // If the user set the 'settings' at the command line, he should
                    // also have used the 'config_dir' option. So we issue a warning,
                    // and leave with an error because the expected configuration can
                    // not be read.
                    warn!("settings option used without the 'config_dir' option. Please set the config directory with --config-dir.");
                    return Err(failure::err_msg(String::from(
                        "Could not build program settings",
                    )));
                }
                config
                    .merge(File::from_str(
                        include_str!("../config/default.toml"),
                        FileFormat::Toml,
                    ))
                    .with_context(|e| {
                        format!(
                            "Could not merge default configuration from file at compile time: {}",
                            e
                        )
                    })?;
            }
        }
        // You can deserialize (and thus freeze) the entire configuration as
        config.try_into().map_err(|e| {
            failure::err_msg(format!(
                "Could not generate settings from configuration: {}",
                e
            ))
        })
    }
}
<|endoftext|>