<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use chrono::Utc;
use once_cell::sync::Lazy;
use serde::Serialize;
use serde_json::Value;
use std::{
    collections::HashMap,
    env,
    fs::{File, OpenOptions},
    io,
    io::Write as IoWrite,
    str::FromStr,
    sync::{
        atomic::{AtomicUsize, Ordering},
        mpsc::{self, Receiver, SyncSender},
    },
    thread,
};

pub trait StructLogSink: Sync {
    fn send(&self, entry: StructuredLogEntry);
}

// This is poor's man AtomicReference from crossbeam
// It have few unsafe lines, but does not require extra dependency
static NOP: NopStructLog = NopStructLog {};
static mut STRUCT_LOGGER: &'static dyn StructLogSink = &NOP;
static STRUCT_LOGGER_STATE: AtomicUsize = AtomicUsize::new(UNINITIALIZED);
const UNINITIALIZED: usize = 0;

const INITIALIZING: usize = 1;
const INITIALIZED: usize = 2;

// severity level - lower is worse
const SEVERITY_CRITICAL: usize = 1;
const SEVERITY_WARNING: usize = 2;

#[derive(Default, Serialize)]
pub struct StructuredLogEntry {
    #[serde(skip_serializing_if = "Option::is_none")]
    log: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pattern: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    name: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    module: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    location: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    git_rev: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    timestamp: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    severity: Option<usize>,
    #[serde(skip_serializing_if = "HashMap::is_empty")]
    data: HashMap<&'static str, Value>,
}

#[must_use = "use StructuredLogEntry::send to send structured log"]
impl StructuredLogEntry {
    pub fn new_unnamed() -> Self {
        let mut ret = Self::default();
        ret.timestamp = Some(Utc::now().format("%F %T").to_string());
        ret
    }

    pub fn new_named(name: &'static str) -> Self {
        let mut ret = Self::default();
        ret.name = Some(name);
        ret.timestamp = Some(Utc::now().format("%F %T").to_string());
        ret
    }

    pub fn critical(mut self) -> Self {
        self.severity = Some(SEVERITY_CRITICAL);
        self
    }

    pub fn warning(mut self) -> Self {
        self.severity = Some(SEVERITY_WARNING);
        self
    }

    pub fn json_data(mut self, key: &'static str, value: Value) -> Self {
        self.data.insert(key, value);
        self
    }

    pub fn data<D: Serialize>(mut self, key: &'static str, value: D) -> Self {
        self.data.insert(
            key,
            serde_json::to_value(value).expect("Failed to serialize StructuredLogEntry key"),
        );
        self
    }

    #[doc(hidden)] // set from macro
    pub fn log(&mut self, log: String) -> &mut Self {
        self.log = Some(log);
        self
    }

    #[doc(hidden)] // set from macro
    pub fn pattern(&mut self, pattern: &'static str) -> &mut Self {
        self.pattern = Some(pattern);
        self
    }

    #[doc(hidden)] // set from macro
    pub fn module(&mut self, module: &'static str) -> &mut Self {
        self.module = Some(module);
        self
    }

    #[doc(hidden)] // set from macro
    pub fn location(&mut self, location: &'static str) -> &mut Self {
        self.location = Some(location);
        self
    }

    #[doc(hidden)] // set from macro
    pub fn git_rev(&mut self, git_rev: Option<&'static str>) -> &mut Self {
        self.git_rev = git_rev;
        self
    }

    #[doc(hidden)] // set from macro
    pub fn data_mutref<D: Serialize>(&mut self, key: &'static str, value: D) -> &mut Self {
        self.data.insert(
            key,
            serde_json::to_value(value).expect("Failed to serialize StructuredLogEntry key"),
        );
        self
    }

    // Use send_struct_log! macro instead of this method to populate extra meta information such as git rev and module name
    #[doc(hidden)]
    pub fn send(self) {
        struct_logger().send(self);
    }
}

// This is exact copy of similar function in log crate
/// Sets structured logger
pub fn set_struct_logger(logger: &'static dyn StructLogSink) -> Result<(), ()> {
    unsafe {
        match STRUCT_LOGGER_STATE.compare_and_swap(UNINITIALIZED, INITIALIZING, Ordering::SeqCst) {
            UNINITIALIZED => {
                STRUCT_LOGGER = logger;
                STRUCT_LOGGER_STATE.store(INITIALIZED, Ordering::SeqCst);
                Ok(())
            }
            INITIALIZING => {
                while STRUCT_LOGGER_STATE.load(Ordering::SeqCst) == INITIALIZING {}
                Err(())
            }
            _ => Err(()),
        }
    }
}

static STRUCT_LOG_LEVEL: Lazy<log::Level> = Lazy::new(|| {
    let level = env::var("STRUCT_LOG_LEVEL").unwrap_or_else(|_| "debug".to_string());
    log::Level::from_str(&level).expect("Failed to parse log level")
});

/// Checks if structured logging is enabled for level
pub fn struct_logger_enabled(level: log::Level) -> bool {
    struct_logger_set() && level <= *STRUCT_LOG_LEVEL
}

/// Checks if structured logging is enabled
pub fn struct_logger_set() -> bool {
    STRUCT_LOGGER_STATE.load(Ordering::SeqCst) == INITIALIZED
}

/// Initializes struct logger from STRUCT_LOG_FILE env var
/// Can only be called once
pub fn init_struct_log_from_env() -> Result<(), InitFileLoggerError> {
    if let Ok(file) = env::var("STRUCT_LOG_FILE") {
        init_file_struct_log(file)
    } else {
        Ok(())
    }
}

/// Initializes struct logger sink that writes to specified file
/// Can only be called once
pub fn init_file_struct_log(file_path: String) -> Result<(), InitFileLoggerError> {
    let logger = FileStructLog::start_new(file_path).map_err(InitFileLoggerError::IoError)?;
    let logger = Box::leak(Box::new(logger));
    set_struct_logger(logger).map_err(|_| InitFileLoggerError::StructLoggerAlreadySet)
}

/// Initialize struct logger sink that prints all structured logs to stdout
/// Can only be called once
pub fn init_println_struct_log() -> Result<(), ()> {
    let logger = PrintStructLog {};
    let logger = Box::leak(Box::new(logger));
    set_struct_logger(logger)
}

#[derive(Debug)]
pub enum InitFileLoggerError {
    IoError(io::Error),
    StructLoggerAlreadySet,
}

// This is exact copy of similar function in log crate
fn struct_logger() -> &'static dyn StructLogSink {
    unsafe {
        if STRUCT_LOGGER_STATE.load(Ordering::SeqCst) != INITIALIZED {
            &NOP
        } else {
            STRUCT_LOGGER
        }
    }
}

struct NopStructLog {}

impl StructLogSink for NopStructLog {
    fn send(&self, _entry: StructuredLogEntry) {}
}

struct PrintStructLog {}

impl StructLogSink for PrintStructLog {
    fn send(&self, entry: StructuredLogEntry) {
        println!("{}", serde_json::to_string(&entry).unwrap());
    }
}

/// Sink that prints all structured logs to specified file
struct FileStructLog {
    sender: SyncSender<StructuredLogEntry>,
}

impl FileStructLog {
    /// Creates new FileStructLog and starts async thread to write results
    pub fn start_new(file_path: String) -> io::Result<Self> {
        let (sender, receiver) = mpsc::sync_channel(1_024);
        let file = OpenOptions::new()
            .append(true)
            .create(true)
            .open(file_path)?;
        let sink_thread = FileStructLogThread { receiver, file };
        thread::spawn(move || sink_thread.run());
        Ok(Self { sender })
    }
}

impl StructLogSink for FileStructLog {
    fn send(&self, entry: StructuredLogEntry) {
        if let Err(e) = self.sender.try_send(entry) {
            // Use log crate macro to avoid generation of structured log in this case
            // Otherwise we will have infinite loop
            log::error!("Failed to send structured log: {}", e);
        }
    }
}

struct FileStructLogThread {
    receiver: Receiver<StructuredLogEntry>,
    file: File,
}

impl FileStructLogThread {
    pub fn run(mut self) {
        for entry in self.receiver {
            let json = match serde_json::to_value(entry) {
                Err(e) => {
                    log::error!("Failed to serialize struct log entry: {}", e);
                    continue;
                }
                Ok(json) => json,
            };
            if let Err(e) = writeln!(&mut self.file, "{}", json) {
                log::error!("Failed to write struct log entry: {}", e);
            }
        }
    }
}
<commit_msg>[logging] Add option to stream structure logs through UDP<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use chrono::Utc;
use once_cell::sync::Lazy;
use serde::Serialize;
use serde_json::Value;
use std::{
    collections::HashMap,
    env,
    fs::{File, OpenOptions},
    io,
    io::Write as IoWrite,
    net::UdpSocket,
    str::FromStr,
    sync::{
        atomic::{AtomicUsize, Ordering},
        mpsc::{self, Receiver, SyncSender},
    },
    thread,
};

pub trait StructLogSink: Sync {
    fn send(&self, entry: StructuredLogEntry);
}

// This is poor's man AtomicReference from crossbeam
// It have few unsafe lines, but does not require extra dependency
static NOP: NopStructLog = NopStructLog {};
static mut STRUCT_LOGGER: &'static dyn StructLogSink = &NOP;
static STRUCT_LOGGER_STATE: AtomicUsize = AtomicUsize::new(UNINITIALIZED);
const UNINITIALIZED: usize = 0;

const INITIALIZING: usize = 1;
const INITIALIZED: usize = 2;

// severity level - lower is worse
const SEVERITY_CRITICAL: usize = 1;
const SEVERITY_WARNING: usize = 2;

#[derive(Default, Serialize)]
pub struct StructuredLogEntry {
    #[serde(skip_serializing_if = "Option::is_none")]
    log: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pattern: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    name: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    module: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    location: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    git_rev: Option<&'static str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    timestamp: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    severity: Option<usize>,
    #[serde(skip_serializing_if = "HashMap::is_empty")]
    data: HashMap<&'static str, Value>,
}

#[must_use = "use StructuredLogEntry::send to send structured log"]
impl StructuredLogEntry {
    pub fn new_unnamed() -> Self {
        let mut ret = Self::default();
        ret.timestamp = Some(Utc::now().format("%F %T").to_string());
        ret
    }

    pub fn new_named(name: &'static str) -> Self {
        let mut ret = Self::default();
        ret.name = Some(name);
        ret.timestamp = Some(Utc::now().format("%F %T").to_string());
        ret
    }

    pub fn critical(mut self) -> Self {
        self.severity = Some(SEVERITY_CRITICAL);
        self
    }

    pub fn warning(mut self) -> Self {
        self.severity = Some(SEVERITY_WARNING);
        self
    }

    pub fn json_data(mut self, key: &'static str, value: Value) -> Self {
        self.data.insert(key, value);
        self
    }

    pub fn data<D: Serialize>(mut self, key: &'static str, value: D) -> Self {
        self.data.insert(
            key,
            serde_json::to_value(value).expect("Failed to serialize StructuredLogEntry key"),
        );
        self
    }

    #[doc(hidden)] // set from macro
    pub fn log(&mut self, log: String) -> &mut Self {
        self.log = Some(log);
        self
    }

    #[doc(hidden)] // set from macro
    pub fn pattern(&mut self, pattern: &'static str) -> &mut Self {
        self.pattern = Some(pattern);
        self
    }

    #[doc(hidden)] // set from macro
    pub fn module(&mut self, module: &'static str) -> &mut Self {
        self.module = Some(module);
        self
    }

    #[doc(hidden)] // set from macro
    pub fn location(&mut self, location: &'static str) -> &mut Self {
        self.location = Some(location);
        self
    }

    #[doc(hidden)] // set from macro
    pub fn git_rev(&mut self, git_rev: Option<&'static str>) -> &mut Self {
        self.git_rev = git_rev;
        self
    }

    #[doc(hidden)] // set from macro
    pub fn data_mutref<D: Serialize>(&mut self, key: &'static str, value: D) -> &mut Self {
        self.data.insert(
            key,
            serde_json::to_value(value).expect("Failed to serialize StructuredLogEntry key"),
        );
        self
    }

    // Use send_struct_log! macro instead of this method to populate extra meta information such as git rev and module name
    #[doc(hidden)]
    pub fn send(self) {
        struct_logger().send(self);
    }
}

// This is exact copy of similar function in log crate
/// Sets structured logger
pub fn set_struct_logger(logger: &'static dyn StructLogSink) -> Result<(), ()> {
    unsafe {
        match STRUCT_LOGGER_STATE.compare_and_swap(UNINITIALIZED, INITIALIZING, Ordering::SeqCst) {
            UNINITIALIZED => {
                STRUCT_LOGGER = logger;
                STRUCT_LOGGER_STATE.store(INITIALIZED, Ordering::SeqCst);
                Ok(())
            }
            INITIALIZING => {
                while STRUCT_LOGGER_STATE.load(Ordering::SeqCst) == INITIALIZING {}
                Err(())
            }
            _ => Err(()),
        }
    }
}

static STRUCT_LOG_LEVEL: Lazy<log::Level> = Lazy::new(|| {
    let level = env::var("STRUCT_LOG_LEVEL").unwrap_or_else(|_| "debug".to_string());
    log::Level::from_str(&level).expect("Failed to parse log level")
});

/// Checks if structured logging is enabled for level
pub fn struct_logger_enabled(level: log::Level) -> bool {
    struct_logger_set() && level <= *STRUCT_LOG_LEVEL
}

/// Checks if structured logging is enabled
pub fn struct_logger_set() -> bool {
    STRUCT_LOGGER_STATE.load(Ordering::SeqCst) == INITIALIZED
}

/// Initializes struct logger from STRUCT_LOG_FILE env var
/// If STRUCT_LOG_FILE is set, STRUCT_LOG_UDP_ADDR will be ignored
/// Can only be called once
pub fn init_struct_log_from_env() -> Result<(), InitLoggerError> {
    if let Ok(file) = env::var("STRUCT_LOG_FILE") {
        init_file_struct_log(file)
    } else if let Ok(udp_address) = env::var("STRUCT_LOG_UDP_ADDR") {
        init_udp_struct_log(udp_address)
    } else {
        Ok(())
    }
}

/// Initializes struct logger sink that writes to specified file
/// Can only be called once
pub fn init_file_struct_log(file_path: String) -> Result<(), InitLoggerError> {
    let logger = FileStructLog::start_new(file_path).map_err(InitLoggerError::IoError)?;
    let logger = Box::leak(Box::new(logger));
    set_struct_logger(logger).map_err(|_| InitLoggerError::StructLoggerAlreadySet)
}

/// Initializes struct logger sink that stream logs through UDP protocol
/// Can only be called once
pub fn init_udp_struct_log(udp_address: String) -> Result<(), InitLoggerError> {
    let logger = UDPStructLog::start_new(udp_address).map_err(InitLoggerError::IoError)?;
    let logger = Box::leak(Box::new(logger));
    set_struct_logger(logger).map_err(|_| InitLoggerError::StructLoggerAlreadySet)
}

/// Initialize struct logger sink that prints all structured logs to stdout
/// Can only be called once
pub fn init_println_struct_log() -> Result<(), ()> {
    let logger = PrintStructLog {};
    let logger = Box::leak(Box::new(logger));
    set_struct_logger(logger)
}

#[derive(Debug)]
pub enum InitLoggerError {
    IoError(io::Error),
    StructLoggerAlreadySet,
}

// This is exact copy of similar function in log crate
fn struct_logger() -> &'static dyn StructLogSink {
    unsafe {
        if STRUCT_LOGGER_STATE.load(Ordering::SeqCst) != INITIALIZED {
            &NOP
        } else {
            STRUCT_LOGGER
        }
    }
}

struct NopStructLog {}

impl StructLogSink for NopStructLog {
    fn send(&self, _entry: StructuredLogEntry) {}
}

struct PrintStructLog {}

impl StructLogSink for PrintStructLog {
    fn send(&self, entry: StructuredLogEntry) {
        println!("{}", serde_json::to_string(&entry).unwrap());
    }
}

/// Sink that prints all structured logs to specified file
struct FileStructLog {
    sender: SyncSender<StructuredLogEntry>,
}

impl FileStructLog {
    /// Creates new FileStructLog and starts async thread to write results
    pub fn start_new(file_path: String) -> io::Result<Self> {
        let (sender, receiver) = mpsc::sync_channel(1_024);
        let file = OpenOptions::new()
            .append(true)
            .create(true)
            .open(file_path)?;
        let sink_thread = FileStructLogThread { receiver, file };
        thread::spawn(move || sink_thread.run());
        Ok(Self { sender })
    }
}

impl StructLogSink for FileStructLog {
    fn send(&self, entry: StructuredLogEntry) {
        if let Err(e) = self.sender.try_send(entry) {
            // Use log crate macro to avoid generation of structured log in this case
            // Otherwise we will have infinite loop
            log::error!("Failed to send structured log: {}", e);
        }
    }
}

struct FileStructLogThread {
    receiver: Receiver<StructuredLogEntry>,
    file: File,
}

impl FileStructLogThread {
    pub fn run(mut self) {
        for entry in self.receiver {
            let json = match serde_json::to_value(entry) {
                Err(e) => {
                    log::error!("Failed to serialize struct log entry: {}", e);
                    continue;
                }
                Ok(json) => json,
            };
            if let Err(e) = writeln!(&mut self.file, "{}", json) {
                log::error!("Failed to write struct log entry: {}", e);
            }
        }
    }
}

/// Sink that streams all structured logs to an address through UDP protocol
struct UDPStructLog {
    sender: SyncSender<StructuredLogEntry>,
}

impl UDPStructLog {
    /// Creates new UDPStructLog and starts async thread to send results
    pub fn start_new(udp_address: String) -> io::Result<Self> {
        let (sender, receiver) = mpsc::sync_channel(1_024);
        let socket = UdpSocket::bind(udp_address.clone()).expect("couldn't bind to address");
        let sink_thread = UDPStructLogThread {
            receiver,
            socket,
            udp_address,
        };
        thread::spawn(move || sink_thread.run());
        Ok(Self { sender })
    }
}

impl StructLogSink for UDPStructLog {
    fn send(&self, entry: StructuredLogEntry) {
        if let Err(e) = self.sender.try_send(entry) {
            // Use log crate macro to avoid generation of structured log in this case
            // Otherwise we will have infinite loop
            log::error!("Failed to send structured log: {}", e);
        }
    }
}

struct UDPStructLogThread {
    receiver: Receiver<StructuredLogEntry>,
    socket: UdpSocket,
    udp_address: String,
}

impl UDPStructLogThread {
    pub fn run(self) {
        for entry in self.receiver {
            let json = match serde_json::to_value(entry) {
                Err(e) => {
                    log::error!("Failed to serialize struct log entry: {}", e);
                    continue;
                }
                Ok(json) => json,
            };
            match self
                .socket
                .send_to(json.to_string().as_bytes(), self.udp_address.clone())
            {
                Ok(_) => {
                    continue;
                }
                Err(e) => {
                    println!("Error while sending data to socket: {}", e);
                    break;
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Initialization logic for a generic instance of the `Allocate` channel allocation trait.

use std::thread;
#[cfg(feature = "getopts")]
use std::io::BufRead;
#[cfg(feature = "getopts")]
use getopts;
use std::sync::Arc;

use std::any::Any;

use crate::allocator::thread::ThreadBuilder;
use crate::allocator::{AllocateBuilder, Process, Generic, GenericBuilder};
use crate::allocator::zero_copy::initialize::initialize_networking;

use crate::logging::{CommunicationSetup, CommunicationEvent};
use logging_core::Logger;

/// Possible configurations for the communication infrastructure.
pub enum Configuration {
    /// Use one thread.
    Thread,
    /// Use one process with an indicated number of threads.
    Process(usize),
    /// Expect multiple processes.
    Cluster {
        /// Number of per-process worker threads
        threads: usize,
        /// Identity of this process
        process: usize,
        /// Addresses of all processes
        addresses: Vec<String>,
        /// Verbosely report connection process
        report: bool,
        /// Closure to create a new logger for a communication thread
        log_fn: Box<Fn(CommunicationSetup) -> Option<Logger<CommunicationEvent, CommunicationSetup>> + Send + Sync>,
    }
}

#[cfg(feature = "getopts")]
impl Configuration {

    /// Returns a `getopts::Options` struct that can be used to print
    /// usage information in higher-level systems.
    pub fn options() -> getopts::Options {
        let mut opts = getopts::Options::new();
        opts.optopt("w", "threads", "number of per-process worker threads", "NUM");
        opts.optopt("p", "process", "identity of this process", "IDX");
        opts.optopt("n", "processes", "number of processes", "NUM");
        opts.optopt("h", "hostfile", "text file whose lines are process addresses", "FILE");
        opts.optflag("r", "report", "reports connection progress");

        opts
    }

    /// Constructs a new configuration by parsing supplied text arguments.
    ///
    /// Most commonly, this uses `std::env::Args()` as the supplied iterator.
    pub fn from_args<I: Iterator<Item=String>>(args: I) -> Result<Configuration,String> {
        let opts = Configuration::options();

        opts.parse(args)
            .map_err(|e| format!("{:?}", e))
            .map(|matches| {

            // let mut config = Configuration::new(1, 0, Vec::new());
            let threads = matches.opt_str("w").map(|x| x.parse().unwrap_or(1)).unwrap_or(1);
            let process = matches.opt_str("p").map(|x| x.parse().unwrap_or(0)).unwrap_or(0);
            let processes = matches.opt_str("n").map(|x| x.parse().unwrap_or(1)).unwrap_or(1);
            let report = matches.opt_present("report");

            assert!(process < processes);

            if processes > 1 {
                let mut addresses = Vec::new();
                if let Some(hosts) = matches.opt_str("h") {
                    let reader = ::std::io::BufReader::new(::std::fs::File::open(hosts.clone()).unwrap());
                    for x in reader.lines().take(processes) {
                        addresses.push(x.unwrap());
                    }
                    if addresses.len() < processes {
                        panic!("could only read {} addresses from {}, but -n: {}", addresses.len(), hosts, processes);
                    }
                }
                else {
                    for index in 0..processes {
                        addresses.push(format!("localhost:{}", 2101 + index));
                    }
                }

                assert!(processes == addresses.len());
                Configuration::Cluster {
                    threads,
                    process,
                    addresses,
                    report,
                    log_fn: Box::new( | _ | None),
                }
            }
            else if threads > 1 { Configuration::Process(threads) }
            else { Configuration::Thread }
        })
    }

    /// Attempts to assemble the described communication infrastructure.
    pub fn try_build(self) -> Result<(Vec<GenericBuilder>, Box<Any>), String> {
        match self {
            Configuration::Thread => {
                Ok((vec![GenericBuilder::Thread(ThreadBuilder)], Box::new(())))
            },
            Configuration::Process(threads) => {
                Ok((Process::new_vector(threads).into_iter().map(|x| GenericBuilder::Process(x)).collect(), Box::new(())))
            },
            Configuration::Cluster { threads, process, addresses, report, log_fn } => {
                match initialize_networking(addresses, process, threads, report, log_fn) {
                    Ok((stuff, guard)) => {
                        Ok((stuff.into_iter().map(|x| GenericBuilder::ZeroCopy(x)).collect(), Box::new(guard)))
                    },
                    Err(err) => Err(format!("failed to initialize networking: {}", err))
                }
            },
        }
    }
}

/// Initializes communication and executes a distributed computation.
///
/// This method allocates an `allocator::Generic` for each thread, spawns local worker threads,
/// and invokes the supplied function with the allocator.
/// The method returns a `WorkerGuards<T>` which can be `join`ed to retrieve the return values
/// (or errors) of the workers.
///
///
/// # Examples
/// ```
/// use timely_communication::Allocate;
///
/// // configure for two threads, just one process.
/// let config = timely_communication::Configuration::Process(2);
///
/// // initializes communication, spawns workers
/// let guards = timely_communication::initialize(config, |mut allocator| {
///     println!("worker {} started", allocator.index());
///
///     // allocates pair of senders list and one receiver.
///     let (mut senders, mut receiver) = allocator.allocate(0);
///
///     // send typed data along each channel
///     use timely_communication::Message;
///     senders[0].send(Message::from_typed(format!("hello, {}", 0)));
///     senders[1].send(Message::from_typed(format!("hello, {}", 1)));
///
///     // no support for termination notification,
///     // we have to count down ourselves.
///     let mut expecting = 2;
///     while expecting > 0 {
///         allocator.receive();
///         if let Some(message) = receiver.recv() {
///             use std::ops::Deref;
///             println!("worker {}: received: <{}>", allocator.index(), message.deref());
///             expecting -= 1;
///         }
///         allocator.release();
///     }
///
///     // optionally, return something
///     allocator.index()
/// });
///
/// // computation runs until guards are joined or dropped.
/// if let Ok(guards) = guards {
///     for guard in guards.join() {
///         println!("result: {:?}", guard);
///     }
/// }
/// else { println!("error in computation"); }
/// ```
///
/// The should produce output like:
///
/// ```ignore
/// worker 0 started
/// worker 1 started
/// worker 0: received: <hello, 0>
/// worker 1: received: <hello, 1>
/// worker 0: received: <hello, 0>
/// worker 1: received: <hello, 1>
/// result: Ok(0)
/// result: Ok(1)
/// ```
pub fn initialize<T:Send+'static, F: Fn(Generic)->T+Send+Sync+'static>(
    config: Configuration,
    func: F,
) -> Result<WorkerGuards<T>,String> {
    let (allocators, others) = config.try_build()?;
    initialize_from(allocators, others, func)
}

/// Initializes computation and runs a distributed computation.
///
/// This version of `initialize` allows you to explicitly specify the allocators that
/// you want to use, by providing an explicit list of allocator builders. Additionally,
/// you provide `others`, a `Box<Any>` which will be held by the resulting worker guard
/// and dropped when it is dropped, which allows you to join communication threads.
///
/// # Examples
/// ```
/// use timely_communication::Allocate;
///
/// // configure for two threads, just one process.
/// let builders = timely_communication::allocator::process::Process::new_vector(2);
///
/// // initializes communication, spawns workers
/// let guards = timely_communication::initialize_from(builders, Box::new(()), |mut allocator| {
///     println!("worker {} started", allocator.index());
///
///     // allocates pair of senders list and one receiver.
///     let (mut senders, mut receiver) = allocator.allocate(0);
///
///     // send typed data along each channel
///     use timely_communication::Message;
///     senders[0].send(Message::from_typed(format!("hello, {}", 0)));
///     senders[1].send(Message::from_typed(format!("hello, {}", 1)));
///
///     // no support for termination notification,
///     // we have to count down ourselves.
///     let mut expecting = 2;
///     while expecting > 0 {
///         allocator.receive();
///         if let Some(message) = receiver.recv() {
///             use std::ops::Deref;
///             println!("worker {}: received: <{}>", allocator.index(), message.deref());
///             expecting -= 1;
///         }
///         allocator.release();
///     }
///
///     // optionally, return something
///     allocator.index()
/// });
///
/// // computation runs until guards are joined or dropped.
/// if let Ok(guards) = guards {
///     for guard in guards.join() {
///         println!("result: {:?}", guard);
///     }
/// }
/// else { println!("error in computation"); }
/// ```
pub fn initialize_from<A, T, F>(
    builders: Vec<A>,
    _others: Box<Any>,
    func: F,
) -> Result<WorkerGuards<T>,String>
where
    A: AllocateBuilder+'static,
    T: Send+'static,
    F: Fn(<A as AllocateBuilder>::Allocator)->T+Send+Sync+'static
{
    let logic = Arc::new(func);
    let mut guards = Vec::new();
    for (index, builder) in builders.into_iter().enumerate() {
        let clone = logic.clone();
        guards.push(thread::Builder::new()
                            .name(format!("worker thread {}", index))
                            .spawn(move || {
                                let communicator = builder.build();
                                (*clone)(communicator)
                            })
                            .map_err(|e| format!("{:?}", e))?);
    }

    Ok(WorkerGuards { guards, _others })
}

/// Maintains `JoinHandle`s for worker threads.
pub struct WorkerGuards<T:Send+'static> {
    guards: Vec<::std::thread::JoinHandle<T>>,
    _others: Box<Any>,
}

impl<T:Send+'static> WorkerGuards<T> {
    /// Waits on the worker threads and returns the results they produce.
    pub fn join(mut self) -> Vec<Result<T, String>> {
        self.guards.drain(..)
                   .map(|guard| guard.join().map_err(|e| format!("{:?}", e)))
                   .collect()
    }
}

impl<T:Send+'static> Drop for WorkerGuards<T> {
    fn drop(&mut self) {
        for guard in self.guards.drain(..) {
            guard.join().expect("Worker panic");
        }
        // println!("WORKER THREADS JOINED");
    }
}
<commit_msg>expose worker guards<commit_after>//! Initialization logic for a generic instance of the `Allocate` channel allocation trait.

use std::thread;
#[cfg(feature = "getopts")]
use std::io::BufRead;
#[cfg(feature = "getopts")]
use getopts;
use std::sync::Arc;

use std::any::Any;

use crate::allocator::thread::ThreadBuilder;
use crate::allocator::{AllocateBuilder, Process, Generic, GenericBuilder};
use crate::allocator::zero_copy::initialize::initialize_networking;

use crate::logging::{CommunicationSetup, CommunicationEvent};
use logging_core::Logger;

/// Possible configurations for the communication infrastructure.
pub enum Configuration {
    /// Use one thread.
    Thread,
    /// Use one process with an indicated number of threads.
    Process(usize),
    /// Expect multiple processes.
    Cluster {
        /// Number of per-process worker threads
        threads: usize,
        /// Identity of this process
        process: usize,
        /// Addresses of all processes
        addresses: Vec<String>,
        /// Verbosely report connection process
        report: bool,
        /// Closure to create a new logger for a communication thread
        log_fn: Box<Fn(CommunicationSetup) -> Option<Logger<CommunicationEvent, CommunicationSetup>> + Send + Sync>,
    }
}

#[cfg(feature = "getopts")]
impl Configuration {

    /// Returns a `getopts::Options` struct that can be used to print
    /// usage information in higher-level systems.
    pub fn options() -> getopts::Options {
        let mut opts = getopts::Options::new();
        opts.optopt("w", "threads", "number of per-process worker threads", "NUM");
        opts.optopt("p", "process", "identity of this process", "IDX");
        opts.optopt("n", "processes", "number of processes", "NUM");
        opts.optopt("h", "hostfile", "text file whose lines are process addresses", "FILE");
        opts.optflag("r", "report", "reports connection progress");

        opts
    }

    /// Constructs a new configuration by parsing supplied text arguments.
    ///
    /// Most commonly, this uses `std::env::Args()` as the supplied iterator.
    pub fn from_args<I: Iterator<Item=String>>(args: I) -> Result<Configuration,String> {
        let opts = Configuration::options();

        opts.parse(args)
            .map_err(|e| format!("{:?}", e))
            .map(|matches| {

            // let mut config = Configuration::new(1, 0, Vec::new());
            let threads = matches.opt_str("w").map(|x| x.parse().unwrap_or(1)).unwrap_or(1);
            let process = matches.opt_str("p").map(|x| x.parse().unwrap_or(0)).unwrap_or(0);
            let processes = matches.opt_str("n").map(|x| x.parse().unwrap_or(1)).unwrap_or(1);
            let report = matches.opt_present("report");

            assert!(process < processes);

            if processes > 1 {
                let mut addresses = Vec::new();
                if let Some(hosts) = matches.opt_str("h") {
                    let reader = ::std::io::BufReader::new(::std::fs::File::open(hosts.clone()).unwrap());
                    for x in reader.lines().take(processes) {
                        addresses.push(x.unwrap());
                    }
                    if addresses.len() < processes {
                        panic!("could only read {} addresses from {}, but -n: {}", addresses.len(), hosts, processes);
                    }
                }
                else {
                    for index in 0..processes {
                        addresses.push(format!("localhost:{}", 2101 + index));
                    }
                }

                assert!(processes == addresses.len());
                Configuration::Cluster {
                    threads,
                    process,
                    addresses,
                    report,
                    log_fn: Box::new( | _ | None),
                }
            }
            else if threads > 1 { Configuration::Process(threads) }
            else { Configuration::Thread }
        })
    }

    /// Attempts to assemble the described communication infrastructure.
    pub fn try_build(self) -> Result<(Vec<GenericBuilder>, Box<Any>), String> {
        match self {
            Configuration::Thread => {
                Ok((vec![GenericBuilder::Thread(ThreadBuilder)], Box::new(())))
            },
            Configuration::Process(threads) => {
                Ok((Process::new_vector(threads).into_iter().map(|x| GenericBuilder::Process(x)).collect(), Box::new(())))
            },
            Configuration::Cluster { threads, process, addresses, report, log_fn } => {
                match initialize_networking(addresses, process, threads, report, log_fn) {
                    Ok((stuff, guard)) => {
                        Ok((stuff.into_iter().map(|x| GenericBuilder::ZeroCopy(x)).collect(), Box::new(guard)))
                    },
                    Err(err) => Err(format!("failed to initialize networking: {}", err))
                }
            },
        }
    }
}

/// Initializes communication and executes a distributed computation.
///
/// This method allocates an `allocator::Generic` for each thread, spawns local worker threads,
/// and invokes the supplied function with the allocator.
/// The method returns a `WorkerGuards<T>` which can be `join`ed to retrieve the return values
/// (or errors) of the workers.
///
///
/// # Examples
/// ```
/// use timely_communication::Allocate;
///
/// // configure for two threads, just one process.
/// let config = timely_communication::Configuration::Process(2);
///
/// // initializes communication, spawns workers
/// let guards = timely_communication::initialize(config, |mut allocator| {
///     println!("worker {} started", allocator.index());
///
///     // allocates pair of senders list and one receiver.
///     let (mut senders, mut receiver) = allocator.allocate(0);
///
///     // send typed data along each channel
///     use timely_communication::Message;
///     senders[0].send(Message::from_typed(format!("hello, {}", 0)));
///     senders[1].send(Message::from_typed(format!("hello, {}", 1)));
///
///     // no support for termination notification,
///     // we have to count down ourselves.
///     let mut expecting = 2;
///     while expecting > 0 {
///         allocator.receive();
///         if let Some(message) = receiver.recv() {
///             use std::ops::Deref;
///             println!("worker {}: received: <{}>", allocator.index(), message.deref());
///             expecting -= 1;
///         }
///         allocator.release();
///     }
///
///     // optionally, return something
///     allocator.index()
/// });
///
/// // computation runs until guards are joined or dropped.
/// if let Ok(guards) = guards {
///     for guard in guards.join() {
///         println!("result: {:?}", guard);
///     }
/// }
/// else { println!("error in computation"); }
/// ```
///
/// The should produce output like:
///
/// ```ignore
/// worker 0 started
/// worker 1 started
/// worker 0: received: <hello, 0>
/// worker 1: received: <hello, 1>
/// worker 0: received: <hello, 0>
/// worker 1: received: <hello, 1>
/// result: Ok(0)
/// result: Ok(1)
/// ```
pub fn initialize<T:Send+'static, F: Fn(Generic)->T+Send+Sync+'static>(
    config: Configuration,
    func: F,
) -> Result<WorkerGuards<T>,String> {
    let (allocators, others) = config.try_build()?;
    initialize_from(allocators, others, func)
}

/// Initializes computation and runs a distributed computation.
///
/// This version of `initialize` allows you to explicitly specify the allocators that
/// you want to use, by providing an explicit list of allocator builders. Additionally,
/// you provide `others`, a `Box<Any>` which will be held by the resulting worker guard
/// and dropped when it is dropped, which allows you to join communication threads.
///
/// # Examples
/// ```
/// use timely_communication::Allocate;
///
/// // configure for two threads, just one process.
/// let builders = timely_communication::allocator::process::Process::new_vector(2);
///
/// // initializes communication, spawns workers
/// let guards = timely_communication::initialize_from(builders, Box::new(()), |mut allocator| {
///     println!("worker {} started", allocator.index());
///
///     // allocates pair of senders list and one receiver.
///     let (mut senders, mut receiver) = allocator.allocate(0);
///
///     // send typed data along each channel
///     use timely_communication::Message;
///     senders[0].send(Message::from_typed(format!("hello, {}", 0)));
///     senders[1].send(Message::from_typed(format!("hello, {}", 1)));
///
///     // no support for termination notification,
///     // we have to count down ourselves.
///     let mut expecting = 2;
///     while expecting > 0 {
///         allocator.receive();
///         if let Some(message) = receiver.recv() {
///             use std::ops::Deref;
///             println!("worker {}: received: <{}>", allocator.index(), message.deref());
///             expecting -= 1;
///         }
///         allocator.release();
///     }
///
///     // optionally, return something
///     allocator.index()
/// });
///
/// // computation runs until guards are joined or dropped.
/// if let Ok(guards) = guards {
///     for guard in guards.join() {
///         println!("result: {:?}", guard);
///     }
/// }
/// else { println!("error in computation"); }
/// ```
pub fn initialize_from<A, T, F>(
    builders: Vec<A>,
    _others: Box<Any>,
    func: F,
) -> Result<WorkerGuards<T>,String>
where
    A: AllocateBuilder+'static,
    T: Send+'static,
    F: Fn(<A as AllocateBuilder>::Allocator)->T+Send+Sync+'static
{
    let logic = Arc::new(func);
    let mut guards = Vec::new();
    for (index, builder) in builders.into_iter().enumerate() {
        let clone = logic.clone();
        guards.push(thread::Builder::new()
                            .name(format!("worker thread {}", index))
                            .spawn(move || {
                                let communicator = builder.build();
                                (*clone)(communicator)
                            })
                            .map_err(|e| format!("{:?}", e))?);
    }

    Ok(WorkerGuards { guards, _others })
}

/// Maintains `JoinHandle`s for worker threads.
pub struct WorkerGuards<T:Send+'static> {
    guards: Vec<::std::thread::JoinHandle<T>>,
    _others: Box<Any>,
}

impl<T:Send+'static> WorkerGuards<T> {

    /// Returns the number of guards.
    pub fn len(&self) -> usize { self.guards.len() }

    /// Returns a reference to the indexed guard.
    pub fn guard(&self, index: usize) -> &std::thread::JoinHandle<T> {
        &self.guards[index]
    }

    /// Waits on the worker threads and returns the results they produce.
    pub fn join(mut self) -> Vec<Result<T, String>> {
        self.guards
            .drain(..)
            .map(|guard| guard.join().map_err(|e| format!("{:?}", e)))
            .collect()
    }
}

impl<T:Send+'static> Drop for WorkerGuards<T> {
    fn drop(&mut self) {
        for guard in self.guards.drain(..) {
            guard.join().expect("Worker panic");
        }
        // println!("WORKER THREADS JOINED");
    }
}
<|endoftext|>


<commit_before>//! Utilities for parsing and formatting RFC 3339 timestamps.
//!
//! The [`Timestamp`] newtype wraps `chrono::DateTime<Utc>` or `time::OffsetDateTime` if the `time`
//! feature is enabled.
//!
//! # Formatting
//! ```
//! # use serenity::model::id::GuildId;
//! # use serenity::model::Timestamp;
//! #
//! let timestamp: Timestamp = GuildId(175928847299117063).created_at();
//! assert_eq!(timestamp.unix_timestamp(), 1462015105);
//! assert_eq!(timestamp.to_string(), "2016-04-30T11:18:25.796Z");
//! ```
//!
//! # Parsing RFC 3339 string
//! ```
//! # use serenity::model::Timestamp;
//! #
//! let timestamp = Timestamp::parse("2016-04-30T11:18:25Z").unwrap();
//! let timestamp = Timestamp::parse("2016-04-30T11:18:25+00:00").unwrap();
//! let timestamp = Timestamp::parse("2016-04-30T11:18:25.796Z").unwrap();
//!
//! let timestamp: Timestamp = "2016-04-30T11:18:25Z".parse().unwrap();
//! let timestamp: Timestamp = "2016-04-30T11:18:25+00:00".parse().unwrap();
//! let timestamp: Timestamp = "2016-04-30T11:18:25.796Z".parse().unwrap();
//!
//! assert!(Timestamp::parse("2016-04-30T11:18:25").is_err());
//! assert!(Timestamp::parse("2016-04-30T11:18").is_err());
//! ```

use std::fmt;
use std::str::FromStr;

use serde::{Deserialize, Serialize};

/// Discord's epoch starts at "2015-01-01T00:00:00+00:00"
const DISCORD_EPOCH: u64 = 1_420_070_400_000;

cfg_if::cfg_if! {
    if #[cfg(all(feature = "chrono", not(feature = "time")))] {
        use chrono::{DateTime, NaiveDateTime, ParseError as InnerError, SecondsFormat, TimeZone, Utc};

        /// Representation of a Unix timestamp.
        ///
        /// The struct implements the `std::fmt::Display` trait to format the underlying type as
        /// an RFC 3339 date and string such as `2016-04-30T11:18:25.796Z`.
        ///
        /// ```
        /// # use serenity::model::id::GuildId;
        /// # use serenity::model::Timestamp;
        /// #
        /// let timestamp: Timestamp = GuildId(175928847299117063).created_at();
        /// assert_eq!(timestamp.unix_timestamp(), 1462015105);
        /// assert_eq!(timestamp.to_string(), "2016-04-30T11:18:25.796Z");
        /// ```
        #[derive(Debug, Clone, Copy, Eq, PartialEq, Deserialize, Serialize, Ord, PartialOrd)]
        #[serde(transparent)]
        pub struct Timestamp(DateTime<Utc>);

        impl Timestamp {
            pub(crate) fn from_discord_id(id: u64) -> Timestamp {
                Self(Utc.timestamp_millis(((id >> 22) + DISCORD_EPOCH) as i64))
            }

            /// Create a new `Timestamp` with the current date and time in UTC.
            #[must_use]
            pub fn now() -> Self {
                Self(Utc::now())
            }

            /// Create a new `Timestamp` from a UNIX timestamp.
            ///
            /// # Errors
            ///
            /// Returns `Err` if the value is invalid.
            pub fn from_unix_timestamp(secs: i64) -> Result<Self, InvalidTimestamp> {
                let dt = NaiveDateTime::from_timestamp_opt(secs, 0).ok_or(InvalidTimestamp)?;
                Ok(Self(DateTime::from_utc(dt, Utc)))
            }

            /// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC
            #[must_use]
            pub fn unix_timestamp(&self) -> i64 {
                self.0.timestamp()
            }

            /// Parse a timestamp from an RFC 3339 date and time string.
            ///
            /// # Examples
            /// ```
            /// # use serenity::model::Timestamp;
            /// #
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25Z").unwrap();
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25+00:00").unwrap();
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25.796Z").unwrap();
            ///
            /// assert!(Timestamp::parse("2016-04-30T11:18:25").is_err());
            /// assert!(Timestamp::parse("2016-04-30T11:18").is_err());
            /// ```
            ///
            /// # Errors
            ///
            /// Returns `Err` if the string is not a valid RFC 3339 date and time string.
            pub fn parse(input: &str) -> Result<Timestamp, ParseError> {
                DateTime::parse_from_rfc3339(input).map(|d| Self(d.with_timezone(&Utc))).map_err(ParseError)
            }
        }

        impl fmt::Display for Timestamp {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                let s = self.0.to_rfc3339_opts(SecondsFormat::Millis, true);
                f.write_str(&s)
            }
        }
    } else {
        use dep_time::format_description::well_known::Rfc3339;
        use dep_time::serde::rfc3339;
        use dep_time::{Duration, OffsetDateTime};
        use dep_time::error::Parse as InnerError;

        /// Representation of a Unix timestamp.
        ///
        /// The struct implements the `std::fmt::Display` trait to format the underlying type as
        /// an RFC 3339 date and string such as `2016-04-30T11:18:25.796Z`.
        ///
        /// ```
        /// # use serenity::model::id::GuildId;
        /// # use serenity::model::Timestamp;
        /// #
        /// let timestamp: Timestamp = GuildId(175928847299117063).created_at();
        /// assert_eq!(timestamp.unix_timestamp(), 1462015105);
        /// assert_eq!(timestamp.to_string(), "2016-04-30T11:18:25.796Z");
        /// ```
        #[derive(Debug, Clone, Copy, Eq, PartialEq, Deserialize, Serialize, Ord, PartialOrd)]
        #[serde(transparent)]
        pub struct Timestamp(#[serde(with = "rfc3339")] OffsetDateTime);

        impl Timestamp {
            pub(crate) fn from_discord_id(id: u64) -> Timestamp {
                let ns = Duration::milliseconds(((id >> 22) + DISCORD_EPOCH) as i64).whole_nanoseconds();
                // This can't fail because of the bit shifting
                // `(u64::MAX >> 22) + DISCORD_EPOCH` = 5818116911103 = "Wed May 15 2154 07:35:11 GMT+0000"
                Self(OffsetDateTime::from_unix_timestamp_nanos(ns).expect("can't fail"))
            }

            /// Create a new `Timestamp` with the current date and time in UTC.
            #[must_use]
            pub fn now() -> Self {
                Self(OffsetDateTime::now_utc())
            }

            /// Create a new `Timestamp` from a UNIX timestamp.
            ///
            /// # Errors
            ///
            /// Returns `Err` if the value is invalid. The valid range of the value may vary depending on
            /// the feature flags enabled (`time` with `large-dates`).
            pub fn from_unix_timestamp(secs: i64) -> Result<Self, InvalidTimestamp> {
                let dt = OffsetDateTime::from_unix_timestamp(secs).map_err(|_| InvalidTimestamp)?;
                Ok(Self(dt))
            }

            /// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC
            #[must_use]
            pub fn unix_timestamp(&self) -> i64 {
                self.0.unix_timestamp()
            }

            /// Parse a timestamp from an RFC 3339 date and time string.
            ///
            /// # Examples
            /// ```
            /// # use serenity::model::Timestamp;
            /// #
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25Z").unwrap();
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25+00:00").unwrap();
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25.796Z").unwrap();
            ///
            /// assert!(Timestamp::parse("2016-04-30T11:18:25").is_err());
            /// assert!(Timestamp::parse("2016-04-30T11:18").is_err());
            /// ```
            ///
            /// # Errors
            ///
            /// Returns `Err` if the string is not a valid RFC 3339 date and time string.
            pub fn parse(input: &str) -> Result<Timestamp, ParseError> {
                OffsetDateTime::parse(input, &Rfc3339).map(Self).map_err(ParseError)
            }
        }

        impl fmt::Display for Timestamp {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                let s = self.0.format(&Rfc3339).map_err(|_| fmt::Error)?;
                f.write_str(&s)
            }
        }
    }
}

cfg_if::cfg_if! {
    if #[cfg(feature = "time")] {
        impl std::ops::Deref for Timestamp {
            type Target = OffsetDateTime;

            fn deref(&self) -> &Self::Target {
                &self.0
            }
        }

        impl From<OffsetDateTime> for Timestamp {
            fn from(dt: OffsetDateTime) -> Self {
                Self(dt)
            }
        }
    } else if #[cfg(feature = "chrono")] {
        impl std::ops::Deref for Timestamp {
            type Target = DateTime<Utc>;

            fn deref(&self) -> &Self::Target {
                &self.0
            }
        }

        impl<Tz: TimeZone> From<DateTime<Tz>> for Timestamp {
            fn from(dt: DateTime<Tz>) -> Self {
                Self(dt.with_timezone(&Utc))
            }
        }
    }
}

#[derive(Debug)]
pub struct InvalidTimestamp;

impl std::error::Error for InvalidTimestamp {}

impl fmt::Display for InvalidTimestamp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("invalid UNIX timestamp value")
    }
}

/// Signifies the failure to parse the `Timestamp` from an RFC 3339 string.
#[derive(Debug)]
pub struct ParseError(InnerError);

impl std::error::Error for ParseError {}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

impl FromStr for Timestamp {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Timestamp::parse(s)
    }
}

impl From<String> for Timestamp {
    /// Parses an RFC 3339 date and time string such as `2016-04-30T11:18:25.796Z`.
    ///
    /// Panics on invalid value.
    fn from(s: String) -> Self {
        #[allow(clippy::unwrap_used)]
        Timestamp::parse(&s).unwrap()
    }
}

impl<'a> From<&'a str> for Timestamp {
    /// Parses an RFC 3339 date and time string such as `2016-04-30T11:18:25.796Z`.
    ///
    /// Panics on invalid value.
    fn from(s: &'a str) -> Self {
        #[allow(clippy::unwrap_used)]
        Timestamp::parse(s).unwrap()
    }
}

impl From<&Timestamp> for Timestamp {
    fn from(ts: &Timestamp) -> Self {
        *ts
    }
}

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

    #[test]
    fn from_unix_timestamp() {
        let timestamp = Timestamp::from_unix_timestamp(1462015105).unwrap();
        assert_eq!(timestamp.unix_timestamp(), 1462015105);
        if cfg!(all(feature = "chrono", not(feature = "time"))) {
            assert_eq!(timestamp.to_string(), "2016-04-30T11:18:25.000Z");
        } else {
            assert_eq!(timestamp.to_string(), "2016-04-30T11:18:25Z");
        }
    }
}
<commit_msg>Make `Timestamp` usable regardless of the `chrono` or `time` features (#2076)<commit_after>//! Utilities for parsing and formatting RFC 3339 timestamps.
//!
//! The [`Timestamp`] newtype wraps `chrono::DateTime<Utc>` or `time::OffsetDateTime` if the `time`
//! feature is enabled.
//!
//! # Formatting
//! ```
//! # use serenity::model::id::GuildId;
//! # use serenity::model::Timestamp;
//! #
//! let timestamp: Timestamp = GuildId(175928847299117063).created_at();
//! assert_eq!(timestamp.unix_timestamp(), 1462015105);
//! assert_eq!(timestamp.to_string(), "2016-04-30T11:18:25.796Z");
//! ```
//!
//! # Parsing RFC 3339 string
//! ```
//! # use serenity::model::Timestamp;
//! #
//! let timestamp = Timestamp::parse("2016-04-30T11:18:25Z").unwrap();
//! let timestamp = Timestamp::parse("2016-04-30T11:18:25+00:00").unwrap();
//! let timestamp = Timestamp::parse("2016-04-30T11:18:25.796Z").unwrap();
//!
//! let timestamp: Timestamp = "2016-04-30T11:18:25Z".parse().unwrap();
//! let timestamp: Timestamp = "2016-04-30T11:18:25+00:00".parse().unwrap();
//! let timestamp: Timestamp = "2016-04-30T11:18:25.796Z".parse().unwrap();
//!
//! assert!(Timestamp::parse("2016-04-30T11:18:25").is_err());
//! assert!(Timestamp::parse("2016-04-30T11:18").is_err());
//! ```

use std::fmt;
use std::str::FromStr;

use serde::{Deserialize, Serialize};

/// Discord's epoch starts at "2015-01-01T00:00:00+00:00"
const DISCORD_EPOCH: u64 = 1_420_070_400_000;

cfg_if::cfg_if! {
    if #[cfg(all(feature = "chrono", not(feature = "time")))] {
        use chrono::{DateTime, NaiveDateTime, ParseError as InnerError, SecondsFormat, TimeZone, Utc};

        /// Representation of a Unix timestamp.
        ///
        /// The struct implements the `std::fmt::Display` trait to format the underlying type as
        /// an RFC 3339 date and string such as `2016-04-30T11:18:25.796Z`.
        ///
        /// ```
        /// # use serenity::model::id::GuildId;
        /// # use serenity::model::Timestamp;
        /// #
        /// let timestamp: Timestamp = GuildId(175928847299117063).created_at();
        /// assert_eq!(timestamp.unix_timestamp(), 1462015105);
        /// assert_eq!(timestamp.to_string(), "2016-04-30T11:18:25.796Z");
        /// ```
        #[derive(Debug, Clone, Copy, Eq, PartialEq, Deserialize, Serialize, Ord, PartialOrd)]
        #[serde(transparent)]
        pub struct Timestamp(DateTime<Utc>);

        impl Timestamp {
            pub(crate) fn from_discord_id(id: u64) -> Timestamp {
                Self(Utc.timestamp_millis(((id >> 22) + DISCORD_EPOCH) as i64))
            }

            /// Create a new `Timestamp` with the current date and time in UTC.
            #[must_use]
            pub fn now() -> Self {
                Self(Utc::now())
            }

            /// Create a new `Timestamp` from a UNIX timestamp.
            ///
            /// # Errors
            ///
            /// Returns `Err` if the value is invalid.
            pub fn from_unix_timestamp(secs: i64) -> Result<Self, InvalidTimestamp> {
                let dt = NaiveDateTime::from_timestamp_opt(secs, 0).ok_or(InvalidTimestamp)?;
                Ok(Self(DateTime::from_utc(dt, Utc)))
            }

            /// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC
            #[must_use]
            pub fn unix_timestamp(&self) -> i64 {
                self.0.timestamp()
            }

            /// Parse a timestamp from an RFC 3339 date and time string.
            ///
            /// # Examples
            /// ```
            /// # use serenity::model::Timestamp;
            /// #
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25Z").unwrap();
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25+00:00").unwrap();
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25.796Z").unwrap();
            ///
            /// assert!(Timestamp::parse("2016-04-30T11:18:25").is_err());
            /// assert!(Timestamp::parse("2016-04-30T11:18").is_err());
            /// ```
            ///
            /// # Errors
            ///
            /// Returns `Err` if the string is not a valid RFC 3339 date and time string.
            pub fn parse(input: &str) -> Result<Timestamp, ParseError> {
                DateTime::parse_from_rfc3339(input).map(|d| Self(d.with_timezone(&Utc))).map_err(ParseError)
            }
        }

        impl fmt::Display for Timestamp {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                let s = self.0.to_rfc3339_opts(SecondsFormat::Millis, true);
                f.write_str(&s)
            }
        }

        impl std::ops::Deref for Timestamp {
            type Target = DateTime<Utc>;

            fn deref(&self) -> &Self::Target {
                &self.0
            }
        }

        impl<Tz: TimeZone> From<DateTime<Tz>> for Timestamp {
            fn from(dt: DateTime<Tz>) -> Self {
                Self(dt.with_timezone(&Utc))
            }
        }
    } else {
        use dep_time::format_description::well_known::Rfc3339;
        use dep_time::serde::rfc3339;
        use dep_time::{Duration, OffsetDateTime};
        use dep_time::error::Parse as InnerError;

        /// Representation of a Unix timestamp.
        ///
        /// The struct implements the `std::fmt::Display` trait to format the underlying type as
        /// an RFC 3339 date and string such as `2016-04-30T11:18:25.796Z`.
        ///
        /// ```
        /// # use serenity::model::id::GuildId;
        /// # use serenity::model::Timestamp;
        /// #
        /// let timestamp: Timestamp = GuildId(175928847299117063).created_at();
        /// assert_eq!(timestamp.unix_timestamp(), 1462015105);
        /// assert_eq!(timestamp.to_string(), "2016-04-30T11:18:25.796Z");
        /// ```
        #[derive(Debug, Clone, Copy, Eq, PartialEq, Deserialize, Serialize, Ord, PartialOrd)]
        #[serde(transparent)]
        pub struct Timestamp(#[serde(with = "rfc3339")] OffsetDateTime);

        impl Timestamp {
            pub(crate) fn from_discord_id(id: u64) -> Timestamp {
                let ns = Duration::milliseconds(((id >> 22) + DISCORD_EPOCH) as i64).whole_nanoseconds();
                // This can't fail because of the bit shifting
                // `(u64::MAX >> 22) + DISCORD_EPOCH` = 5818116911103 = "Wed May 15 2154 07:35:11 GMT+0000"
                Self(OffsetDateTime::from_unix_timestamp_nanos(ns).expect("can't fail"))
            }

            /// Create a new `Timestamp` with the current date and time in UTC.
            #[must_use]
            pub fn now() -> Self {
                Self(OffsetDateTime::now_utc())
            }

            /// Create a new `Timestamp` from a UNIX timestamp.
            ///
            /// # Errors
            ///
            /// Returns `Err` if the value is invalid. The valid range of the value may vary depending on
            /// the feature flags enabled (`time` with `large-dates`).
            pub fn from_unix_timestamp(secs: i64) -> Result<Self, InvalidTimestamp> {
                let dt = OffsetDateTime::from_unix_timestamp(secs).map_err(|_| InvalidTimestamp)?;
                Ok(Self(dt))
            }

            /// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC
            #[must_use]
            pub fn unix_timestamp(&self) -> i64 {
                self.0.unix_timestamp()
            }

            /// Parse a timestamp from an RFC 3339 date and time string.
            ///
            /// # Examples
            /// ```
            /// # use serenity::model::Timestamp;
            /// #
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25Z").unwrap();
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25+00:00").unwrap();
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25.796Z").unwrap();
            ///
            /// assert!(Timestamp::parse("2016-04-30T11:18:25").is_err());
            /// assert!(Timestamp::parse("2016-04-30T11:18").is_err());
            /// ```
            ///
            /// # Errors
            ///
            /// Returns `Err` if the string is not a valid RFC 3339 date and time string.
            pub fn parse(input: &str) -> Result<Timestamp, ParseError> {
                OffsetDateTime::parse(input, &Rfc3339).map(Self).map_err(ParseError)
            }
        }

        impl fmt::Display for Timestamp {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                let s = self.0.format(&Rfc3339).map_err(|_| fmt::Error)?;
                f.write_str(&s)
            }
        }

        impl std::ops::Deref for Timestamp {
            type Target = OffsetDateTime;

            fn deref(&self) -> &Self::Target {
                &self.0
            }
        }

        impl From<OffsetDateTime> for Timestamp {
            fn from(dt: OffsetDateTime) -> Self {
                Self(dt)
            }
        }
    }
}

#[derive(Debug)]
pub struct InvalidTimestamp;

impl std::error::Error for InvalidTimestamp {}

impl fmt::Display for InvalidTimestamp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("invalid UNIX timestamp value")
    }
}

/// Signifies the failure to parse the `Timestamp` from an RFC 3339 string.
#[derive(Debug)]
pub struct ParseError(InnerError);

impl std::error::Error for ParseError {}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

impl FromStr for Timestamp {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Timestamp::parse(s)
    }
}

impl From<String> for Timestamp {
    /// Parses an RFC 3339 date and time string such as `2016-04-30T11:18:25.796Z`.
    ///
    /// Panics on invalid value.
    fn from(s: String) -> Self {
        #[allow(clippy::unwrap_used)]
        Timestamp::parse(&s).unwrap()
    }
}

impl<'a> From<&'a str> for Timestamp {
    /// Parses an RFC 3339 date and time string such as `2016-04-30T11:18:25.796Z`.
    ///
    /// Panics on invalid value.
    fn from(s: &'a str) -> Self {
        #[allow(clippy::unwrap_used)]
        Timestamp::parse(s).unwrap()
    }
}

impl From<&Timestamp> for Timestamp {
    fn from(ts: &Timestamp) -> Self {
        *ts
    }
}

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

    #[test]
    fn from_unix_timestamp() {
        let timestamp = Timestamp::from_unix_timestamp(1462015105).unwrap();
        assert_eq!(timestamp.unix_timestamp(), 1462015105);
        if cfg!(all(feature = "chrono", not(feature = "time"))) {
            assert_eq!(timestamp.to_string(), "2016-04-30T11:18:25.000Z");
        } else {
            assert_eq!(timestamp.to_string(), "2016-04-30T11:18:25Z");
        }
    }
}
<|endoftext|>


<commit_before>use super::StatusTracker;
use crate::msg::actionlib_msgs::GoalStatusArray;
use crate::static_messages::MUTEX_LOCK_FAIL;
use crate::{Action, GoalBody};
use std::collections::BTreeMap;
use std::sync::{Arc, Mutex};

type Tracker<T> = Arc<Mutex<StatusTracker<GoalBody<T>>>>;

pub struct StatusList<T: Action> {
    timeout: i64,
    items: BTreeMap<String, Tracker<T>>,
    publisher: rosrust::Publisher<GoalStatusArray>,
}

impl<T: Action> StatusList<T> {
    pub fn new(timeout: i64, publisher: rosrust::Publisher<GoalStatusArray>) -> Self {
        Self {
            timeout,
            items: BTreeMap::new(),
            publisher,
        }
    }

    pub fn to_status_array(&mut self) -> GoalStatusArray {
        let now = rosrust::now();
        let now_nanos = now.nanos();
        let dead_keys = self
            .items
            .iter()
            .filter_map(|(key, tracker)| {
                let tracker = tracker.lock().expect(MUTEX_LOCK_FAIL);
                let destruction_time = tracker.destruction_time()?;
                if destruction_time.nanos() + self.timeout > now_nanos {
                    return None;
                }
                rosrust::ros_debug!(
                    "Item {} with destruction time of {} being removed from list.  Now = {}",
                    tracker.goal_id().id,
                    destruction_time.seconds(),
                    now.seconds()
                );
                Some(key)
            })
            .cloned()
            .collect::<Vec<_>>();
        for key in dead_keys {
            self.items.remove(&key);
        }

        let status_list = self
            .items
            .values()
            .map(|tracker| tracker.lock().expect(MUTEX_LOCK_FAIL).to_status().into())
            .collect();
        GoalStatusArray {
            header: Default::default(),
            status_list,
        }
    }

    pub fn publish(&mut self) -> rosrust::error::Result<()> {
        let mut data = self.to_status_array();
        if !rosrust::is_ok() {
            return Ok(());
        }
        data.header.stamp = rosrust::now();
        self.publisher.send(data)
    }

    pub fn insert(&mut self, key: String, tracker: Tracker<T>) {
        self.items.insert(key, tracker);
    }

    pub fn get(&self, id: &str) -> Option<Tracker<T>> {
        self.items.get(id).cloned()
    }

    pub fn values(&self) -> impl Iterator<Item = &Tracker<T>> {
        self.items.values()
    }
}
<commit_msg>Slightly refactor status list<commit_after>use super::StatusTracker;
use crate::msg::actionlib_msgs::GoalStatusArray;
use crate::static_messages::MUTEX_LOCK_FAIL;
use crate::{Action, GoalBody};
use std::collections::BTreeMap;
use std::sync::{Arc, Mutex};

type Tracker<T> = Arc<Mutex<StatusTracker<GoalBody<T>>>>;

pub struct StatusList<T: Action> {
    timeout: i64,
    items: BTreeMap<String, Tracker<T>>,
    publisher: rosrust::Publisher<GoalStatusArray>,
}

impl<T: Action> StatusList<T> {
    pub fn new(timeout: i64, publisher: rosrust::Publisher<GoalStatusArray>) -> Self {
        Self {
            timeout,
            items: BTreeMap::new(),
            publisher,
        }
    }

    fn remove_dead_keys(&mut self) {
        let now = rosrust::now();
        let now_nanos = now.nanos();
        let dead_keys = self
            .items
            .iter()
            .filter_map(|(key, tracker)| {
                let tracker = tracker.lock().expect(MUTEX_LOCK_FAIL);
                let destruction_time = tracker.destruction_time()?;
                if destruction_time.nanos() + self.timeout > now_nanos {
                    return None;
                }
                rosrust::ros_debug!(
                    "Item {} with destruction time of {} being removed from list. Now = {}",
                    tracker.goal_id().id,
                    destruction_time.seconds(),
                    now.seconds()
                );
                Some(key)
            })
            .cloned()
            .collect::<Vec<_>>();
        for key in dead_keys {
            self.items.remove(&key);
        }
    }

    fn to_status_array(&self) -> GoalStatusArray {
        let status_list = self
            .items
            .values()
            .map(|tracker| tracker.lock().expect(MUTEX_LOCK_FAIL).to_status().into())
            .collect();
        GoalStatusArray {
            header: Default::default(),
            status_list,
        }
    }

    pub fn publish(&mut self) -> rosrust::error::Result<()> {
        self.remove_dead_keys();
        let mut data = self.to_status_array();
        if !rosrust::is_ok() {
            return Ok(());
        }
        data.header.stamp = rosrust::now();
        self.publisher.send(data)
    }

    pub fn insert(&mut self, key: String, tracker: Tracker<T>) {
        self.items.insert(key, tracker);
    }

    pub fn get(&self, id: &str) -> Option<Tracker<T>> {
        self.items.get(id).cloned()
    }

    pub fn values(&self) -> impl Iterator<Item = &Tracker<T>> {
        self.items.values()
    }
}
<|endoftext|>


<commit_before>#![feature(phase)]

#[phase(plugin)]
extern crate regex_macros;
extern crate regex;

use std::os;
use std::io::{BufferedStream, File, IoResult};
use std::from_str::FromStr;
use regex::Regex;
use std::num::from_int;

static SPACES: Regex = regex!("[ \t]+");

#[deriving(Show)]
enum Month {
    January = 1,
    February = 2,
    March = 3,
    April = 4,
    May = 5,
    June = 6,
    July = 7,
    August = 8,
    September = 9,
    October = 10,
    November = 11,
    December = 12,
}

#[deriving(Show, FromPrimitive)]
enum DayOfWeek {
    Sunday = 0,
    Monday = 1,
    Tuesday = 2,
    Wednesday = 3,
    Thursday = 4,
    Friday = 5,
    Saturday = 6,
}

#[deriving(Show)]
enum Period {
    Reboot,
    Daily,
    Weekly,
    Monthly,
    Yearly,
}

impl FromStr for Period {
    fn from_str(s: &str) -> Option<Period> {
        match s {
            "@reboot"  => Some(Reboot),
            "@daily"   => Some(Daily),
            "@weekly"  => Some(Weekly),
            "@monthly" => Some(Monthly),
            "@yearly"  => Some(Yearly),
            "@midnight" => Some(Daily),
            "@annually" => Some(Yearly),
            "1" => Some(Daily),
            "7" => Some(Weekly),
            _ => None,
        }
    }
}

impl FromStr for DayOfWeek {
    fn from_str(s: &str) -> Option<DayOfWeek> {
        match s.slice(0, 3).chars().map(|c| c.to_lowercase()).collect::<String>().as_slice() {
            "sun" => Some(Sunday),
            "mon" => Some(Monday),
            "tue" => Some(Tuesday),
            "wed" => Some(Wednesday),
            "thu" => Some(Thursday),
            "fri" => Some(Friday),
            "sat" => Some(Saturday),
            v => match from_str::<int>(v) {
                Some(n) => from_int::<DayOfWeek>(n % 7),
                None => None,
            },
        }
    }
}

#[deriving(Show)]
struct Minute(uint);

#[deriving(Show)]
struct Hour(uint);

#[deriving(Show)]
struct Day(uint);

#[deriving(Show)]
struct User(String);

#[deriving(Show)]
struct Command(String);

#[deriving(Show)]
enum CrontabEntry {
    Periodic(Vec<Minute>, Vec<Hour>, Vec<Day>, Vec<Month>, Vec<DayOfWeek>, User, Command),
    Monotonic(Period, User, Command),
}

trait RangeEntry<T> {
    fn everything(&self) -> Vec<T>;

    fn to_range(&self, s: &str) -> Vec<T> {
        let all = self.everything();
        if s == "*" {
            return all;
        }

        // TODO: split `s` by comma, detect and exapnd ranges, map to all
    }
}

impl CrontabEntry {
    fn new(parts: Vec<String>) -> Option<CrontabEntry> {
        match parts.len() {
            6 => {
                Some(Periodic(
                    vec![Minute(0)],
                    vec![Hour(0)],
                    vec![Day(0)],
                    vec![January],
                    vec![Sunday],
                    User("root".into_string()),
                    Command("command".into_string()),
                ))
            },
            7 => {
                Some(Periodic(
                    vec![Minute(0)],
                    vec![Hour(0)],
                    vec![Day(0)],
                    vec![January],
                    vec![Sunday],
                    User("root".into_string()),
                    Command("command".into_string()),
                ))
            }
            _ => None
        }
    }
}

struct CrontabIterator<'i> {
    lines: &'i mut Iterator<IoResult<String>>,
}

impl<'i> CrontabIterator<'i> {
    fn new(iter: &'i mut Iterator<IoResult<String>>) -> CrontabIterator<'i> {
        CrontabIterator {
            lines: iter
        }
    }
}

impl<'i> Iterator<CrontabEntry> for CrontabIterator<'i> {
    fn next(&mut self) -> Option<CrontabEntry> {
        loop {
            match self.lines.next() {
                None => return None,
                Some(Err(_)) => return None,

                Some(Ok(line)) => {
                    let line = line.as_slice().trim();

                    if line.starts_with("#") {
                        continue;
                    }

                    let parts = SPACES.splitn(line, 6).map(|p| p.into_string()).collect();

                    return Some(CrontabEntry::new(parts).unwrap());
                }
            }
        }
    }
}

fn main() {
    let args: Vec<String> = os::args();

    let file = File::open(&Path::new("/var/spool/cron/kstep")).unwrap();
    let mut buffer = BufferedStream::new(file);
    let mut lines = buffer.lines();
    let mut crontab = CrontabIterator::new(&mut lines);

    println!("target dir: {}", args.get(1));

    for line in crontab {
        println!("line: {}", line);
    }
}
<commit_msg>WIP: rust step 5<commit_after>#![feature(phase)]

#[phase(plugin)]
extern crate regex_macros;
extern crate regex;

use std::os;
use std::io::{BufferedStream, File, IoResult};
use std::from_str::FromStr;
use regex::Regex;
use std::num::from_int;

static SPACES: Regex = regex!("[ \t]+");

#[deriving(Show)]
enum Month {
    January = 1,
    February = 2,
    March = 3,
    April = 4,
    May = 5,
    June = 6,
    July = 7,
    August = 8,
    September = 9,
    October = 10,
    November = 11,
    December = 12,
}

#[deriving(Show, FromPrimitive)]
enum DayOfWeek {
    Sunday = 0,
    Monday = 1,
    Tuesday = 2,
    Wednesday = 3,
    Thursday = 4,
    Friday = 5,
    Saturday = 6,
}

#[deriving(Show)]
enum Period {
    Reboot,
    Daily,
    Weekly,
    Monthly,
    Yearly,
}

impl FromStr for Period {
    fn from_str(s: &str) -> Option<Period> {
        match s {
            "@reboot"  => Some(Reboot),
            "@daily"   => Some(Daily),
            "@weekly"  => Some(Weekly),
            "@monthly" => Some(Monthly),
            "@yearly"  => Some(Yearly),
            "@midnight" => Some(Daily),
            "@annually" => Some(Yearly),
            "1" => Some(Daily),
            "7" => Some(Weekly),
            _ => None,
        }
    }
}

impl FromStr for DayOfWeek {
    fn from_str(s: &str) -> Option<DayOfWeek> {
        match s.slice(0, 3).chars().map(|c| c.to_lowercase()).collect::<String>().as_slice() {
            "sun" => Some(Sunday),
            "mon" => Some(Monday),
            "tue" => Some(Tuesday),
            "wed" => Some(Wednesday),
            "thu" => Some(Thursday),
            "fri" => Some(Friday),
            "sat" => Some(Saturday),
            v => match from_str::<int>(v) {
                Some(n) => from_int::<DayOfWeek>(n % 7),
                None => None,
            },
        }
    }
}

#[deriving(Show)]
struct Minute(uint);

#[deriving(Show)]
struct Hour(uint);

#[deriving(Show)]
struct Day(uint);

// name, group, class
#[deriving(Show)]
struct User(String, Option<String>, Option<String>);

#[deriving(Show)]
struct Command(String);

#[deriving(Show)]
struct Delay(uint);

#[deriving(Show)]
struct JobId(String);

#[deriving(Show)]
enum CrontabEntry {
    Calendar(Vec<Minute>, Vec<Hour>, Vec<Day>, Vec<Month>, Vec<DayOfWeek>, Option<User>, Command),
    Monotonic(Period, Option<User>, Command),
    Anacron(Period, Delay, JobId, Command),
}

trait RangeEntry<T> {
    fn everything(&self) -> Vec<T>;

    fn to_range(&self, s: &str) -> Vec<T> {
        let all = self.everything();
        if s == "*" {
            return all;
        }

        // TODO: split `s` by comma, detect and exapnd ranges, map to all
    }
}

impl CrontabEntry {
    fn new(parts: Vec<String>) -> Option<CrontabEntry> {
        match parts.len() {
            6 => {
                Some(Calendar(
                    vec![Minute(0)],
                    vec![Hour(0)],
                    vec![Day(0)],
                    vec![January],
                    vec![Sunday],
                    Some(User("root".into_string(), None, None)),
                    Command("command".into_string()),
                ))
            },
            7 => {
                Some(Calendar(
                    vec![Minute(0)],
                    vec![Hour(0)],
                    vec![Day(0)],
                    vec![January],
                    vec![Sunday],
                    Some(User("root".into_string(), None, None)),
                    Command("command".into_string()),
                ))
            }
            _ => None
        }
    }
}

struct CrontabIterator<'i> {
    lines: &'i mut Iterator<IoResult<String>>,
}

impl<'i> CrontabIterator<'i> {
    fn new(iter: &'i mut Iterator<IoResult<String>>) -> CrontabIterator<'i> {
        CrontabIterator {
            lines: iter
        }
    }
}

impl<'i> Iterator<CrontabEntry> for CrontabIterator<'i> {
    fn next(&mut self) -> Option<CrontabEntry> {
        loop {
            match self.lines.next() {
                None => return None,
                Some(Err(_)) => return None,

                Some(Ok(line)) => {
                    let line = line.as_slice().trim();

                    if line.starts_with("#") {
                        continue;
                    }

                    let parts = SPACES.splitn(line, 6).map(|p| p.into_string()).collect();

                    return Some(CrontabEntry::new(parts).unwrap());
                }
            }
        }
    }
}

fn main() {
    let args: Vec<String> = os::args();

    let file = File::open(&Path::new("/var/spool/cron/kstep")).unwrap();
    let mut buffer = BufferedStream::new(file);
    let mut lines = buffer.lines();
    let mut crontab = CrontabIterator::new(&mut lines);

    println!("target dir: {}", args.get(1));

    for line in crontab {
        println!("line: {}", line);
    }
}
<|endoftext|>