<commit_before>// This file is part of the uutils coreutils package.
//
// (c) Anthony Deschamps <anthony.j.deschamps@gmail.com>
// (c) Sylvestre Ledru <sylvestre@debian.org>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (format) MMDDhhmm
// spell-checker:ignore (ToDO) DATEFILE

#[macro_use]
extern crate uucore;

use chrono::{DateTime, FixedOffset, Local, Offset, Utc};
#[cfg(windows)]
use chrono::{Datelike, Timelike};
use clap::{App, Arg};
#[cfg(all(unix, not(target_os = "macos")))]
use libc::{clock_settime, timespec, CLOCK_REALTIME};
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
#[cfg(windows)]
use winapi::{
    shared::minwindef::WORD,
    um::{minwinbase::SYSTEMTIME, sysinfoapi::SetSystemTime},
};

// Options
const DATE: &str = "date";
const HOURS: &str = "hours";
const MINUTES: &str = "minutes";
const SECONDS: &str = "seconds";
const HOUR: &str = "hour";
const MINUTE: &str = "minute";
const SECOND: &str = "second";
const NS: &str = "ns";

const NAME: &str = "date";
const VERSION: &str = env!("CARGO_PKG_VERSION");
const ABOUT: &str = "print or set the system date and time";

const OPT_DATE: &str = "date";
const OPT_FORMAT: &str = "format";
const OPT_FILE: &str = "file";
const OPT_DEBUG: &str = "debug";
const OPT_ISO_8601: &str = "iso-8601";
const OPT_RFC_EMAIL: &str = "rfc-email";
const OPT_RFC_3339: &str = "rfc-3339";
const OPT_SET: &str = "set";
const OPT_REFERENCE: &str = "reference";
const OPT_UNIVERSAL: &str = "universal";
const OPT_UNIVERSAL_2: &str = "utc";

// Help strings

static ISO_8601_HELP_STRING: &str = "output date/time in ISO 8601 format.
 FMT='date' for date only (the default),
 'hours', 'minutes', 'seconds', or 'ns'
 for date and time to the indicated precision.
 Example: 2006-08-14T02:34:56-06:00";

static RFC_5322_HELP_STRING: &str = "output date and time in RFC 5322 format.
 Example: Mon, 14 Aug 2006 02:34:56 -0600";

static RFC_3339_HELP_STRING: &str = "output date/time in RFC 3339 format.
 FMT='date', 'seconds', or 'ns'
 for date and time to the indicated precision.
 Example: 2006-08-14 02:34:56-06:00";

#[cfg(not(target_os = "macos"))]
static OPT_SET_HELP_STRING: &str = "set time described by STRING";
#[cfg(target_os = "macos")]
static OPT_SET_HELP_STRING: &str = "set time described by STRING (not available on mac yet)";

/// Settings for this program, parsed from the command line
struct Settings {
    utc: bool,
    format: Format,
    date_source: DateSource,
    set_to: Option<DateTime<FixedOffset>>,
}

/// Various ways of displaying the date
enum Format {
    Iso8601(Iso8601Format),
    Rfc5322,
    Rfc3339(Rfc3339Format),
    Custom(String),
    Default,
}

/// Various places that dates can come from
enum DateSource {
    Now,
    Custom(String),
    File(PathBuf),
}

enum Iso8601Format {
    Date,
    Hours,
    Minutes,
    Seconds,
    Ns,
}

impl<'a> From<&'a str> for Iso8601Format {
    fn from(s: &str) -> Self {
        match s {
            HOURS | HOUR => Iso8601Format::Hours,
            MINUTES | MINUTE => Iso8601Format::Minutes,
            SECONDS | SECOND => Iso8601Format::Seconds,
            NS => Iso8601Format::Ns,
            DATE => Iso8601Format::Date,
            // Should be caught by clap
            _ => panic!("Invalid format: {}", s),
        }
    }
}

enum Rfc3339Format {
    Date,
    Seconds,
    Ns,
}

impl<'a> From<&'a str> for Rfc3339Format {
    fn from(s: &str) -> Self {
        match s {
            DATE => Rfc3339Format::Date,
            SECONDS | SECOND => Rfc3339Format::Seconds,
            NS => Rfc3339Format::Ns,
            // Should be caught by clap
            _ => panic!("Invalid format: {}", s),
        }
    }
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let syntax = format!(
        "{0} [OPTION]... [+FORMAT]...
 {0} [OPTION]... [MMDDhhmm[[CC]YY][.ss]]",
        NAME
    );
    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&syntax[..])
        .arg(
            Arg::with_name(OPT_DATE)
                .short("d")
                .long(OPT_DATE)
                .takes_value(true)
                .help("display time described by STRING, not 'now'"),
        )
        .arg(
            Arg::with_name(OPT_FILE)
                .short("f")
                .long(OPT_FILE)
                .takes_value(true)
                .help("like --date; once for each line of DATEFILE"),
        )
        .arg(
            Arg::with_name(OPT_ISO_8601)
                .short("I")
                .long(OPT_ISO_8601)
                .takes_value(true)
                .help(ISO_8601_HELP_STRING),
        )
        .arg(
            Arg::with_name(OPT_RFC_EMAIL)
                .short("R")
                .long(OPT_RFC_EMAIL)
                .help(RFC_5322_HELP_STRING),
        )
        .arg(
            Arg::with_name(OPT_RFC_3339)
                .long(OPT_RFC_3339)
                .takes_value(true)
                .help(RFC_3339_HELP_STRING),
        )
        .arg(
            Arg::with_name(OPT_DEBUG)
                .long(OPT_DEBUG)
                .help("annotate the parsed date, and warn about questionable usage to stderr"),
        )
        .arg(
            Arg::with_name(OPT_REFERENCE)
                .short("r")
                .long(OPT_REFERENCE)
                .takes_value(true)
                .help("display the last modification time of FILE"),
        )
        .arg(
            Arg::with_name(OPT_SET)
                .short("s")
                .long(OPT_SET)
                .takes_value(true)
                .help(OPT_SET_HELP_STRING),
        )
        .arg(
            Arg::with_name(OPT_UNIVERSAL)
                .short("u")
                .long(OPT_UNIVERSAL)
                .alias(OPT_UNIVERSAL_2)
                .help("print or set Coordinated Universal Time (UTC)"),
        )
        .arg(Arg::with_name(OPT_FORMAT).multiple(false))
        .get_matches_from(args);

    let format = if let Some(form) = matches.value_of(OPT_FORMAT) {
        if !form.starts_with('+') {
            eprintln!("date: invalid date ‘{}’", form);
            return 1;
        }
        let form = form[1..].to_string();
        Format::Custom(form)
    } else if let Some(fmt) = matches
        .values_of(OPT_ISO_8601)
        .map(|mut iter| iter.next().unwrap_or(DATE).into())
    {
        Format::Iso8601(fmt)
    } else if matches.is_present(OPT_RFC_EMAIL) {
        Format::Rfc5322
    } else if let Some(fmt) = matches.value_of(OPT_RFC_3339).map(Into::into) {
        Format::Rfc3339(fmt)
    } else {
        Format::Default
    };

    let date_source = if let Some(date) = matches.value_of(OPT_DATE) {
        DateSource::Custom(date.into())
    } else if let Some(file) = matches.value_of(OPT_FILE) {
        DateSource::File(file.into())
    } else {
        DateSource::Now
    };

    let set_to = match matches.value_of(OPT_SET).map(parse_date) {
        None => None,
        Some(Err((input, _err))) => {
            eprintln!("date: invalid date ‘{}’", input);
            return 1;
        }
        Some(Ok(date)) => Some(date),
    };

    let settings = Settings {
        utc: matches.is_present(OPT_UNIVERSAL),
        format,
        date_source,
        set_to,
    };

    if let Some(date) = settings.set_to {
        // All set time functions expect UTC datetimes.
        let date: DateTime<Utc> = if settings.utc {
            date.with_timezone(&Utc)
        } else {
            date.into()
        };

        return set_system_datetime(date);
    } else {
        // Declare a file here because it needs to outlive the `dates` iterator.
        let file: File;

        // Get the current time, either in the local time zone or UTC.
        let now: DateTime<FixedOffset> = if settings.utc {
            let now = Utc::now();
            now.with_timezone(&now.offset().fix())
        } else {
            let now = Local::now();
            now.with_timezone(now.offset())
        };

        // Iterate over all dates - whether it's a single date or a file.
        let dates: Box<dyn Iterator<Item = _>> = match settings.date_source {
            DateSource::Custom(ref input) => {
                let date = parse_date(input.clone());
                let iter = std::iter::once(date);
                Box::new(iter)
            }
            DateSource::File(ref path) => {
                file = File::open(path).unwrap();
                let lines = BufReader::new(file).lines();
                let iter = lines.filter_map(Result::ok).map(parse_date);
                Box::new(iter)
            }
            DateSource::Now => {
                let iter = std::iter::once(Ok(now));
                Box::new(iter)
            }
        };

        let format_string = make_format_string(&settings);

        // Format all the dates
        for date in dates {
            match date {
                Ok(date) => {
                    // GNU `date` uses `%N` for nano seconds, however crate::chrono uses `%f`
                    let format_string = &format_string.replace("%N", "%f");
                    let formatted = date.format(format_string).to_string().replace("%f", "%N");
                    println!("{}", formatted);
                }
                Err((input, _err)) => {
                    println!("date: invalid date ‘{}’", input);
                }
            }
        }
    }

    0
}

/// Return the appropriate format string for the given settings.
fn make_format_string(settings: &Settings) -> &str {
    match settings.format {
        Format::Iso8601(ref fmt) => match *fmt {
            Iso8601Format::Date => "%F",
            Iso8601Format::Hours => "%FT%H%:z",
            Iso8601Format::Minutes => "%FT%H:%M%:z",
            Iso8601Format::Seconds => "%FT%T%:z",
            Iso8601Format::Ns => "%FT%T,%f%:z",
        },
        Format::Rfc5322 => "%a, %d %h %Y %T %z",
        Format::Rfc3339(ref fmt) => match *fmt {
            Rfc3339Format::Date => "%F",
            Rfc3339Format::Seconds => "%F %T%:z",
            Rfc3339Format::Ns => "%F %T.%f%:z",
        },
        Format::Custom(ref fmt) => fmt,
        Format::Default => "%c",
    }
}

/// Parse a `String` into a `DateTime`.
/// If it fails, return a tuple of the `String` along with its `ParseError`.
fn parse_date<S: AsRef<str> + Clone>(
    s: S,
) -> Result<DateTime<FixedOffset>, (String, chrono::format::ParseError)> {
    // TODO: The GNU date command can parse a wide variety of inputs.
    s.as_ref().parse().map_err(|e| (s.as_ref().into(), e))
}

#[cfg(not(any(unix, windows)))]
fn set_system_datetime(_date: DateTime<Utc>) -> i32 {
    unimplemented!("setting date not implemented (unsupported target)");
}

#[cfg(target_os = "macos")]
fn set_system_datetime(_date: DateTime<Utc>) -> i32 {
    eprintln!("date: setting the date is not supported by macOS");
    1
}

#[cfg(all(unix, not(target_os = "macos")))]
/// System call to set date (unix).
/// See here for more:
/// https://doc.rust-lang.org/libc/i686-unknown-linux-gnu/libc/fn.clock_settime.html
/// https://linux.die.net/man/3/clock_settime
/// https://www.gnu.org/software/libc/manual/html_node/Time-Types.html
fn set_system_datetime(date: DateTime<Utc>) -> i32 {
    let timespec = timespec {
        tv_sec: date.timestamp() as _,
        tv_nsec: date.timestamp_subsec_nanos() as _,
    };

    let result = unsafe { clock_settime(CLOCK_REALTIME, &timespec) };

    if result != 0 {
        let error = std::io::Error::last_os_error();
        eprintln!("date: cannot set date: {}", error);
        error.raw_os_error().unwrap()
    } else {
        0
    }
}

#[cfg(windows)]
/// System call to set date (Windows).
/// See here for more:
/// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-setsystemtime
/// https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime
fn set_system_datetime(date: DateTime<Utc>) -> i32 {
    let system_time = SYSTEMTIME {
        wYear: date.year() as WORD,
        wMonth: date.month() as WORD,
        // Ignored
        wDayOfWeek: 0,
        wDay: date.day() as WORD,
        wHour: date.hour() as WORD,
        wMinute: date.minute() as WORD,
        wSecond: date.second() as WORD,
        // TODO: be careful of leap seconds - valid range is [0, 999] - how to handle?
        wMilliseconds: ((date.nanosecond() / 1_000_000) % 1000) as WORD,
    };

    let result = unsafe { SetSystemTime(&system_time) };

    if result == 0 {
        let error = std::io::Error::last_os_error();
        eprintln!("date: cannot set date: {}", error);
        error.raw_os_error().unwrap()
    } else {
        0
    }
}
<commit_msg>refactor/date ~ polish spelling (comments, names, and exceptions)<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Anthony Deschamps <anthony.j.deschamps@gmail.com>
// (c) Sylvestre Ledru <sylvestre@debian.org>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (chrono) Datelike Timelike ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes

#[macro_use]
extern crate uucore;

use chrono::{DateTime, FixedOffset, Local, Offset, Utc};
#[cfg(windows)]
use chrono::{Datelike, Timelike};
use clap::{App, Arg};
#[cfg(all(unix, not(target_os = "macos")))]
use libc::{clock_settime, timespec, CLOCK_REALTIME};
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
#[cfg(windows)]
use winapi::{
    shared::minwindef::WORD,
    um::{minwinbase::SYSTEMTIME, sysinfoapi::SetSystemTime},
};

// Options
const DATE: &str = "date";
const HOURS: &str = "hours";
const MINUTES: &str = "minutes";
const SECONDS: &str = "seconds";
const HOUR: &str = "hour";
const MINUTE: &str = "minute";
const SECOND: &str = "second";
const NS: &str = "ns";

const NAME: &str = "date";
const VERSION: &str = env!("CARGO_PKG_VERSION");
const ABOUT: &str = "print or set the system date and time";

const OPT_DATE: &str = "date";
const OPT_FORMAT: &str = "format";
const OPT_FILE: &str = "file";
const OPT_DEBUG: &str = "debug";
const OPT_ISO_8601: &str = "iso-8601";
const OPT_RFC_EMAIL: &str = "rfc-email";
const OPT_RFC_3339: &str = "rfc-3339";
const OPT_SET: &str = "set";
const OPT_REFERENCE: &str = "reference";
const OPT_UNIVERSAL: &str = "universal";
const OPT_UNIVERSAL_2: &str = "utc";

// Help strings

static ISO_8601_HELP_STRING: &str = "output date/time in ISO 8601 format.
 FMT='date' for date only (the default),
 'hours', 'minutes', 'seconds', or 'ns'
 for date and time to the indicated precision.
 Example: 2006-08-14T02:34:56-06:00";

static RFC_5322_HELP_STRING: &str = "output date and time in RFC 5322 format.
 Example: Mon, 14 Aug 2006 02:34:56 -0600";

static RFC_3339_HELP_STRING: &str = "output date/time in RFC 3339 format.
 FMT='date', 'seconds', or 'ns'
 for date and time to the indicated precision.
 Example: 2006-08-14 02:34:56-06:00";

#[cfg(not(target_os = "macos"))]
static OPT_SET_HELP_STRING: &str = "set time described by STRING";
#[cfg(target_os = "macos")]
static OPT_SET_HELP_STRING: &str = "set time described by STRING (not available on mac yet)";

/// Settings for this program, parsed from the command line
struct Settings {
    utc: bool,
    format: Format,
    date_source: DateSource,
    set_to: Option<DateTime<FixedOffset>>,
}

/// Various ways of displaying the date
enum Format {
    Iso8601(Iso8601Format),
    Rfc5322,
    Rfc3339(Rfc3339Format),
    Custom(String),
    Default,
}

/// Various places that dates can come from
enum DateSource {
    Now,
    Custom(String),
    File(PathBuf),
}

enum Iso8601Format {
    Date,
    Hours,
    Minutes,
    Seconds,
    Ns,
}

impl<'a> From<&'a str> for Iso8601Format {
    fn from(s: &str) -> Self {
        match s {
            HOURS | HOUR => Iso8601Format::Hours,
            MINUTES | MINUTE => Iso8601Format::Minutes,
            SECONDS | SECOND => Iso8601Format::Seconds,
            NS => Iso8601Format::Ns,
            DATE => Iso8601Format::Date,
            // Should be caught by clap
            _ => panic!("Invalid format: {}", s),
        }
    }
}

enum Rfc3339Format {
    Date,
    Seconds,
    Ns,
}

impl<'a> From<&'a str> for Rfc3339Format {
    fn from(s: &str) -> Self {
        match s {
            DATE => Rfc3339Format::Date,
            SECONDS | SECOND => Rfc3339Format::Seconds,
            NS => Rfc3339Format::Ns,
            // Should be caught by clap
            _ => panic!("Invalid format: {}", s),
        }
    }
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let syntax = format!(
        "{0} [OPTION]... [+FORMAT]...
 {0} [OPTION]... [MMDDhhmm[[CC]YY][.ss]]",
        NAME
    );
    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&syntax[..])
        .arg(
            Arg::with_name(OPT_DATE)
                .short("d")
                .long(OPT_DATE)
                .takes_value(true)
                .help("display time described by STRING, not 'now'"),
        )
        .arg(
            Arg::with_name(OPT_FILE)
                .short("f")
                .long(OPT_FILE)
                .takes_value(true)
                .help("like --date; once for each line of DATEFILE"),
        )
        .arg(
            Arg::with_name(OPT_ISO_8601)
                .short("I")
                .long(OPT_ISO_8601)
                .takes_value(true)
                .help(ISO_8601_HELP_STRING),
        )
        .arg(
            Arg::with_name(OPT_RFC_EMAIL)
                .short("R")
                .long(OPT_RFC_EMAIL)
                .help(RFC_5322_HELP_STRING),
        )
        .arg(
            Arg::with_name(OPT_RFC_3339)
                .long(OPT_RFC_3339)
                .takes_value(true)
                .help(RFC_3339_HELP_STRING),
        )
        .arg(
            Arg::with_name(OPT_DEBUG)
                .long(OPT_DEBUG)
                .help("annotate the parsed date, and warn about questionable usage to stderr"),
        )
        .arg(
            Arg::with_name(OPT_REFERENCE)
                .short("r")
                .long(OPT_REFERENCE)
                .takes_value(true)
                .help("display the last modification time of FILE"),
        )
        .arg(
            Arg::with_name(OPT_SET)
                .short("s")
                .long(OPT_SET)
                .takes_value(true)
                .help(OPT_SET_HELP_STRING),
        )
        .arg(
            Arg::with_name(OPT_UNIVERSAL)
                .short("u")
                .long(OPT_UNIVERSAL)
                .alias(OPT_UNIVERSAL_2)
                .help("print or set Coordinated Universal Time (UTC)"),
        )
        .arg(Arg::with_name(OPT_FORMAT).multiple(false))
        .get_matches_from(args);

    let format = if let Some(form) = matches.value_of(OPT_FORMAT) {
        if !form.starts_with('+') {
            eprintln!("date: invalid date ‘{}’", form);
            return 1;
        }
        let form = form[1..].to_string();
        Format::Custom(form)
    } else if let Some(fmt) = matches
        .values_of(OPT_ISO_8601)
        .map(|mut iter| iter.next().unwrap_or(DATE).into())
    {
        Format::Iso8601(fmt)
    } else if matches.is_present(OPT_RFC_EMAIL) {
        Format::Rfc5322
    } else if let Some(fmt) = matches.value_of(OPT_RFC_3339).map(Into::into) {
        Format::Rfc3339(fmt)
    } else {
        Format::Default
    };

    let date_source = if let Some(date) = matches.value_of(OPT_DATE) {
        DateSource::Custom(date.into())
    } else if let Some(file) = matches.value_of(OPT_FILE) {
        DateSource::File(file.into())
    } else {
        DateSource::Now
    };

    let set_to = match matches.value_of(OPT_SET).map(parse_date) {
        None => None,
        Some(Err((input, _err))) => {
            eprintln!("date: invalid date ‘{}’", input);
            return 1;
        }
        Some(Ok(date)) => Some(date),
    };

    let settings = Settings {
        utc: matches.is_present(OPT_UNIVERSAL),
        format,
        date_source,
        set_to,
    };

    if let Some(date) = settings.set_to {
        // All set time functions expect UTC datetimes.
        let date: DateTime<Utc> = if settings.utc {
            date.with_timezone(&Utc)
        } else {
            date.into()
        };

        return set_system_datetime(date);
    } else {
        // Declare a file here because it needs to outlive the `dates` iterator.
        let file: File;

        // Get the current time, either in the local time zone or UTC.
        let now: DateTime<FixedOffset> = if settings.utc {
            let now = Utc::now();
            now.with_timezone(&now.offset().fix())
        } else {
            let now = Local::now();
            now.with_timezone(now.offset())
        };

        // Iterate over all dates - whether it's a single date or a file.
        let dates: Box<dyn Iterator<Item = _>> = match settings.date_source {
            DateSource::Custom(ref input) => {
                let date = parse_date(input.clone());
                let iter = std::iter::once(date);
                Box::new(iter)
            }
            DateSource::File(ref path) => {
                file = File::open(path).unwrap();
                let lines = BufReader::new(file).lines();
                let iter = lines.filter_map(Result::ok).map(parse_date);
                Box::new(iter)
            }
            DateSource::Now => {
                let iter = std::iter::once(Ok(now));
                Box::new(iter)
            }
        };

        let format_string = make_format_string(&settings);

        // Format all the dates
        for date in dates {
            match date {
                Ok(date) => {
                    // GNU `date` uses `%N` for nano seconds, however crate::chrono uses `%f`
                    let format_string = &format_string.replace("%N", "%f");
                    let formatted = date.format(format_string).to_string().replace("%f", "%N");
                    println!("{}", formatted);
                }
                Err((input, _err)) => {
                    println!("date: invalid date ‘{}’", input);
                }
            }
        }
    }

    0
}

/// Return the appropriate format string for the given settings.
fn make_format_string(settings: &Settings) -> &str {
    match settings.format {
        Format::Iso8601(ref fmt) => match *fmt {
            Iso8601Format::Date => "%F",
            Iso8601Format::Hours => "%FT%H%:z",
            Iso8601Format::Minutes => "%FT%H:%M%:z",
            Iso8601Format::Seconds => "%FT%T%:z",
            Iso8601Format::Ns => "%FT%T,%f%:z",
        },
        Format::Rfc5322 => "%a, %d %h %Y %T %z",
        Format::Rfc3339(ref fmt) => match *fmt {
            Rfc3339Format::Date => "%F",
            Rfc3339Format::Seconds => "%F %T%:z",
            Rfc3339Format::Ns => "%F %T.%f%:z",
        },
        Format::Custom(ref fmt) => fmt,
        Format::Default => "%c",
    }
}

/// Parse a `String` into a `DateTime`.
/// If it fails, return a tuple of the `String` along with its `ParseError`.
fn parse_date<S: AsRef<str> + Clone>(
    s: S,
) -> Result<DateTime<FixedOffset>, (String, chrono::format::ParseError)> {
    // TODO: The GNU date command can parse a wide variety of inputs.
    s.as_ref().parse().map_err(|e| (s.as_ref().into(), e))
}

#[cfg(not(any(unix, windows)))]
fn set_system_datetime(_date: DateTime<Utc>) -> i32 {
    unimplemented!("setting date not implemented (unsupported target)");
}

#[cfg(target_os = "macos")]
fn set_system_datetime(_date: DateTime<Utc>) -> i32 {
    eprintln!("date: setting the date is not supported by macOS");
    1
}

#[cfg(all(unix, not(target_os = "macos")))]
/// System call to set date (unix).
/// See here for more:
/// https://doc.rust-lang.org/libc/i686-unknown-linux-gnu/libc/fn.clock_settime.html
/// https://linux.die.net/man/3/clock_settime
/// https://www.gnu.org/software/libc/manual/html_node/Time-Types.html
fn set_system_datetime(date: DateTime<Utc>) -> i32 {
    let timespec = timespec {
        tv_sec: date.timestamp() as _,
        tv_nsec: date.timestamp_subsec_nanos() as _,
    };

    let result = unsafe { clock_settime(CLOCK_REALTIME, &timespec) };

    if result != 0 {
        let error = std::io::Error::last_os_error();
        eprintln!("date: cannot set date: {}", error);
        error.raw_os_error().unwrap()
    } else {
        0
    }
}

#[cfg(windows)]
/// System call to set date (Windows).
/// See here for more:
/// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-setsystemtime
/// https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime
fn set_system_datetime(date: DateTime<Utc>) -> i32 {
    let system_time = SYSTEMTIME {
        wYear: date.year() as WORD,
        wMonth: date.month() as WORD,
        // Ignored
        wDayOfWeek: 0,
        wDay: date.day() as WORD,
        wHour: date.hour() as WORD,
        wMinute: date.minute() as WORD,
        wSecond: date.second() as WORD,
        // TODO: be careful of leap seconds - valid range is [0, 999] - how to handle?
        wMilliseconds: ((date.nanosecond() / 1_000_000) % 1000) as WORD,
    };

    let result = unsafe { SetSystemTime(&system_time) };

    if result == 0 {
        let error = std::io::Error::last_os_error();
        eprintln!("date: cannot set date: {}", error);
        error.raw_os_error().unwrap()
    } else {
        0
    }
}
<|endoftext|>


<commit_before>use async::bufreader::AsyncBufReader;
use async::bufwriter::AsyncBufWriter;

use std::{cmp, io};
use std::io::{Read, Write};

use futures::{Async, Future, Poll};
use rand::{Rng, thread_rng};

pub fn ntoh(buf: &[u8]) -> u32 {
    ((buf[0] as u32) << 24) + ((buf[1] as u32) << 16) + ((buf[2] as u32) << 8) + (buf[3] as u32)
}

pub fn hton(n: u32) -> [u8; 4] {
    let mut buf = [0u8; 4];
    buf[0] = (n >> 24) as u8;
    buf[1] = ((n >> 16) & 0xff) as u8;
    buf[2] = ((n >> 8) & 0xff) as u8;
    buf[3] = (n & 0xff) as u8;

    buf
}

pub struct PacketWriteRequest {
    pub payload: Vec<u8>,
    pub flush: bool
}

pub trait AsyncPacketState: Future {
    fn wants_read(&self) -> bool {
        false
    }

    fn on_read(&mut self, _msg: &[u8]) -> Result<(), Self::Error> {
        Ok(())
    }

    fn write_packet(&self) -> Option<PacketWriteRequest> {
        None
    }

    fn on_flush(&mut self) -> Result<(), Self::Error> {
        Ok(())
    }
}

enum PacketReadState {
    Idle,
    ReadPayload(u32, u8),
}

enum PacketWriteState {
    Idle,
    WritePayload(PacketWriteRequest, u32, u8),
    Flush
}

pub struct AsyncPacketTransport<R: Read, W: Write, RNG, T> {
    rd: AsyncBufReader<R>,
    rd_st: PacketReadState,
    wr: AsyncBufWriter<W>,
    wr_st: PacketWriteState,
    rng: RNG,
    st: T,
}

impl <R: Read, W: Write, RNG, T> AsyncPacketTransport<R, W, RNG, T> {
    pub fn new(rd: AsyncBufReader<R>,
               wr: AsyncBufWriter<W>,
               rng: RNG,
               st: T) -> AsyncPacketTransport<R, W, RNG, T>
    {
        AsyncPacketTransport {
            rd: rd,
            rd_st: PacketReadState::Idle,
            wr: wr,
            wr_st: PacketWriteState::Idle,
            rng: rng,
            st: st
        }
    }
}

pub trait TransportError : From<io::Error> + From<()> {
    fn invalid_header() -> Self;
    fn panic(&'static str) -> Self;
}

macro_rules! try_add {
    ($a:expr, $b:expr) => {
        if let Some(x) = $a.checked_add($b) {
            x
        } else {
            return Err(().into());
        }
    }
}

macro_rules! try_sub {
    ($a:expr, $b:expr) => {
        if let Some(x) = $a.checked_sub($b) {
            x
        } else {
            return Err(().into());
        }
    }
}

pub fn compute_pad_len<R: Rng>(payload_len: usize, blk_size: usize, rng: &mut R) -> Result<(u32, u8), ()> {
    let min_unit = cmp::max(blk_size, 8);

    // maximum possible pkt_len = 5 byte header + payload_len + 255
    let pkt_upperbound = cmp::min(try_add!(payload_len, 5 + 255), try_add!(::std::u32::MAX as usize, 4));

    // pkt_len = 5 byte header + payload_len + pad_len
    // pad_len must be 4 bytes or larger
    // pkt_len must be 16 bytes or larger, and it must be multiple of max(blk_size, 8)
    let pkt_lowerbound = cmp::max(try_add!(payload_len, 5 + 4), cmp::max(min_unit, 16));

    let max_pkt_len = pkt_upperbound - (pkt_upperbound % min_unit);
    let min_pkt_len = try_add!(pkt_lowerbound, min_unit - 1) / min_unit * min_unit;

    let except_pad = try_add!(payload_len, 5);
    let max_pad_len = try_sub!(max_pkt_len, except_pad);
    let min_pad_len = try_sub!(min_pkt_len, except_pad);

    if 4 <= min_pad_len && min_pad_len <= max_pad_len && max_pad_len <= 255 {
        let pad_len =
            rng.gen_range(0, (max_pad_len - min_pad_len) / min_unit + 1) * min_unit + min_pad_len;
        let pkt_len = try_add!(pad_len, except_pad) - 4;
        Ok((pkt_len as u32, pad_len as u8))
    } else {
        Err(())
    }
}

impl <R, W, RNG, T> AsyncPacketTransport<R, W, RNG, T>
    where R: Read, W: Write, RNG: Rng, T: AsyncPacketState, T::Error: TransportError
{
    fn try_write(&mut self) -> Result<(), T::Error> {
        let next_state = match self.wr_st {
            PacketWriteState::Idle => {
                if let Some(req) = self.st.write_packet() {
                    let (pkt_len, pad_len) = try!(compute_pad_len(req.payload.len(), 0, &mut self.rng));
                    PacketWriteState::WritePayload(req, pkt_len, pad_len)
                } else {
                    return Ok(());
                }
            },
            PacketWriteState::WritePayload(ref req, pkt_len, pad_len) => {
                if pkt_len as usize != req.payload.len() + 1 + pad_len as usize {
                    return Err(T::Error::panic("pkt_len does not match"));
                }

                let async_res = try!(self.wr.nb_write(pkt_len as usize + 4, |buf| {
                    buf[0] = ((pkt_len >> 24) & 0xff) as u8;
                    buf[1] = ((pkt_len >> 16) & 0xff) as u8;
                    buf[2] = ((pkt_len >> 8) & 0xff) as u8;
                    buf[3] = (pkt_len & 0xff) as u8;
                    buf[4] = pad_len;
                    buf[5 .. 5 + req.payload.len()].copy_from_slice(&req.payload);

                    let mut rng = thread_rng();
                    rng.fill_bytes(&mut buf[5 + req.payload.len() ..]);
                }));

                if let Async::NotReady = async_res {
                    return Ok(());
                } else if req.flush {
                    PacketWriteState::Flush
                } else {
                    try!(self.st.on_flush());
                    PacketWriteState::Idle
                }
            },
            PacketWriteState::Flush => {
                if let Async::Ready(()) = try!(self.wr.nb_flush()) {
                    try!(self.st.on_flush());
                    PacketWriteState::Idle
                } else {
                    return Ok(());
                }
            }
        };

        self.wr_st = next_state;
        self.try_write()
    }

    fn try_read(&mut self) -> Result<(), T::Error> {
        let next_state = match self.rd_st {
            PacketReadState::Idle => {
                if !self.st.wants_read() {
                    return Ok(());
                }

                if let Async::Ready(buf) = try!(self.rd.nb_read_exact(5)) {
                    let pkt_len = ntoh(&buf[.. 4]);
                    let pad_len = buf[4];
                    if pkt_len < 16 || pkt_len < (pad_len as u32) + 1 {
                        return Err(T::Error::invalid_header());
                    }
                    PacketReadState::ReadPayload(pkt_len, pad_len)
                } else {
                    return Ok(());
                }
            },
            PacketReadState::ReadPayload(pkt_len, pad_len) => {
                if let Async::Ready(buf) = try!(self.rd.nb_read_exact(pkt_len as usize - 1)) {
                    let payload_len = pkt_len as usize - pad_len as usize - 1;
                    try!(self.st.on_read(&buf[..payload_len]));
                    PacketReadState::Idle
                } else {
                    return Ok(());
                }
            }
        };

        self.rd_st = next_state;
        self.try_read()
    }
}

impl <R, W, RNG, T, V, E> Future for AsyncPacketTransport<R, W, RNG, T>
    where R: Read, W: Write, RNG: Rng, T: AsyncPacketState + Future<Item=Option<V>, Error=E>, E: TransportError
{
    type Item = V;
    type Error = E;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        try!(self.try_write());
        try!(self.try_read());

        match try!(self.st.poll()) {
            Async::Ready(None) => self.poll(),
            Async::Ready(Some(x)) => Ok(Async::Ready(x)),
            Async::NotReady => Ok(Async::NotReady)
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use rand::thread_rng;

    #[test]
    fn test_compute_pad_len() {
        let mut rng = thread_rng();
        for payload_len in 1 .. 257 {
            if let Ok((pkt_len, pad_len)) = compute_pad_len(payload_len, 0, &mut rng) {
                assert_eq!(pkt_len % 8, 4);
                assert_eq!(pkt_len as usize, pad_len as usize + payload_len + 1);
            } else {
                panic!("compute_pad_len failed at payload_len = {}", payload_len);
            }
        }
    }
}
<commit_msg>Should accept pkt_len as small as 12 bytes<commit_after>use async::bufreader::AsyncBufReader;
use async::bufwriter::AsyncBufWriter;

use std::{cmp, io};
use std::io::{Read, Write};

use futures::{Async, Future, Poll};
use rand::{Rng, thread_rng};

pub fn ntoh(buf: &[u8]) -> u32 {
    ((buf[0] as u32) << 24) + ((buf[1] as u32) << 16) + ((buf[2] as u32) << 8) + (buf[3] as u32)
}

pub fn hton(n: u32) -> [u8; 4] {
    let mut buf = [0u8; 4];
    buf[0] = (n >> 24) as u8;
    buf[1] = ((n >> 16) & 0xff) as u8;
    buf[2] = ((n >> 8) & 0xff) as u8;
    buf[3] = (n & 0xff) as u8;

    buf
}

pub struct PacketWriteRequest {
    pub payload: Vec<u8>,
    pub flush: bool
}

pub trait AsyncPacketState: Future {
    fn wants_read(&self) -> bool {
        false
    }

    fn on_read(&mut self, _msg: &[u8]) -> Result<(), Self::Error> {
        Ok(())
    }

    fn write_packet(&self) -> Option<PacketWriteRequest> {
        None
    }

    fn on_flush(&mut self) -> Result<(), Self::Error> {
        Ok(())
    }
}

enum PacketReadState {
    Idle,
    ReadPayload(u32, u8),
}

enum PacketWriteState {
    Idle,
    WritePayload(PacketWriteRequest, u32, u8),
    Flush
}

pub struct AsyncPacketTransport<R: Read, W: Write, RNG, T> {
    rd: AsyncBufReader<R>,
    rd_st: PacketReadState,
    wr: AsyncBufWriter<W>,
    wr_st: PacketWriteState,
    rng: RNG,
    st: T,
}

impl <R: Read, W: Write, RNG, T> AsyncPacketTransport<R, W, RNG, T> {
    pub fn new(rd: AsyncBufReader<R>,
               wr: AsyncBufWriter<W>,
               rng: RNG,
               st: T) -> AsyncPacketTransport<R, W, RNG, T>
    {
        AsyncPacketTransport {
            rd: rd,
            rd_st: PacketReadState::Idle,
            wr: wr,
            wr_st: PacketWriteState::Idle,
            rng: rng,
            st: st
        }
    }
}

pub trait TransportError : From<io::Error> + From<()> {
    fn invalid_header() -> Self;
    fn panic(&'static str) -> Self;
}

macro_rules! try_add {
    ($a:expr, $b:expr) => {
        if let Some(x) = $a.checked_add($b) {
            x
        } else {
            return Err(().into());
        }
    }
}

macro_rules! try_sub {
    ($a:expr, $b:expr) => {
        if let Some(x) = $a.checked_sub($b) {
            x
        } else {
            return Err(().into());
        }
    }
}

pub fn compute_pad_len<R: Rng>(payload_len: usize, blk_size: usize, rng: &mut R) -> Result<(u32, u8), ()> {
    let min_unit = cmp::max(blk_size, 8);

    // maximum possible pkt_len = 5 byte header + payload_len + 255
    let pkt_upperbound = cmp::min(try_add!(payload_len, 5 + 255), try_add!(::std::u32::MAX as usize, 4));

    // pkt_len = 5 byte header + payload_len + pad_len
    // pad_len must be 4 bytes or larger
    // pkt_len must be 16 bytes or larger, and it must be multiple of max(blk_size, 8)
    let pkt_lowerbound = cmp::max(try_add!(payload_len, 5 + 4), cmp::max(min_unit, 16));

    let max_pkt_len = pkt_upperbound - (pkt_upperbound % min_unit);
    let min_pkt_len = try_add!(pkt_lowerbound, min_unit - 1) / min_unit * min_unit;

    let except_pad = try_add!(payload_len, 5);
    let max_pad_len = try_sub!(max_pkt_len, except_pad);
    let min_pad_len = try_sub!(min_pkt_len, except_pad);

    if 4 <= min_pad_len && min_pad_len <= max_pad_len && max_pad_len <= 255 {
        let pad_len =
            rng.gen_range(0, (max_pad_len - min_pad_len) / min_unit + 1) * min_unit + min_pad_len;
        let pkt_len = try_add!(pad_len, except_pad) - 4;
        Ok((pkt_len as u32, pad_len as u8))
    } else {
        Err(())
    }
}

impl <R, W, RNG, T> AsyncPacketTransport<R, W, RNG, T>
    where R: Read, W: Write, RNG: Rng, T: AsyncPacketState, T::Error: TransportError
{
    fn try_write(&mut self) -> Result<(), T::Error> {
        let next_state = match self.wr_st {
            PacketWriteState::Idle => {
                if let Some(req) = self.st.write_packet() {
                    let (pkt_len, pad_len) = try!(compute_pad_len(req.payload.len(), 0, &mut self.rng));
                    PacketWriteState::WritePayload(req, pkt_len, pad_len)
                } else {
                    return Ok(());
                }
            },
            PacketWriteState::WritePayload(ref req, pkt_len, pad_len) => {
                if pkt_len as usize != req.payload.len() + 1 + pad_len as usize {
                    return Err(T::Error::panic("pkt_len does not match"));
                }

                let async_res = try!(self.wr.nb_write(pkt_len as usize + 4, |buf| {
                    buf[0] = ((pkt_len >> 24) & 0xff) as u8;
                    buf[1] = ((pkt_len >> 16) & 0xff) as u8;
                    buf[2] = ((pkt_len >> 8) & 0xff) as u8;
                    buf[3] = (pkt_len & 0xff) as u8;
                    buf[4] = pad_len;
                    buf[5 .. 5 + req.payload.len()].copy_from_slice(&req.payload);

                    let mut rng = thread_rng();
                    rng.fill_bytes(&mut buf[5 + req.payload.len() ..]);
                }));

                if let Async::NotReady = async_res {
                    return Ok(());
                } else if req.flush {
                    PacketWriteState::Flush
                } else {
                    try!(self.st.on_flush());
                    PacketWriteState::Idle
                }
            },
            PacketWriteState::Flush => {
                if let Async::Ready(()) = try!(self.wr.nb_flush()) {
                    try!(self.st.on_flush());
                    PacketWriteState::Idle
                } else {
                    return Ok(());
                }
            }
        };

        self.wr_st = next_state;
        self.try_write()
    }

    fn try_read(&mut self) -> Result<(), T::Error> {
        let next_state = match self.rd_st {
            PacketReadState::Idle => {
                if !self.st.wants_read() {
                    return Ok(());
                }

                if let Async::Ready(buf) = try!(self.rd.nb_read_exact(5)) {
                    let pkt_len = ntoh(&buf[.. 4]);
                    let pad_len = buf[4];
                    if pkt_len < 12 || pkt_len < (pad_len as u32) + 1 {
                        println!("pkt_len: {}, pad_len: {}", pkt_len, pad_len);
                        return Err(T::Error::invalid_header());
                    }
                    PacketReadState::ReadPayload(pkt_len, pad_len)
                } else {
                    return Ok(());
                }
            },
            PacketReadState::ReadPayload(pkt_len, pad_len) => {
                if let Async::Ready(buf) = try!(self.rd.nb_read_exact(pkt_len as usize - 1)) {
                    let payload_len = pkt_len as usize - pad_len as usize - 1;
                    try!(self.st.on_read(&buf[..payload_len]));
                    PacketReadState::Idle
                } else {
                    return Ok(());
                }
            }
        };

        self.rd_st = next_state;
        self.try_read()
    }
}

impl <R, W, RNG, T, V, E> Future for AsyncPacketTransport<R, W, RNG, T>
    where R: Read, W: Write, RNG: Rng, T: AsyncPacketState + Future<Item=Option<V>, Error=E>, E: TransportError
{
    type Item = V;
    type Error = E;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        try!(self.try_write());
        try!(self.try_read());

        match try!(self.st.poll()) {
            Async::Ready(None) => self.poll(),
            Async::Ready(Some(x)) => Ok(Async::Ready(x)),
            Async::NotReady => Ok(Async::NotReady)
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use rand::thread_rng;

    #[test]
    fn test_compute_pad_len() {
        let mut rng = thread_rng();
        for payload_len in 1 .. 257 {
            if let Ok((pkt_len, pad_len)) = compute_pad_len(payload_len, 0, &mut rng) {
                assert!(pkt_len >= 12);
                assert_eq!(pkt_len % 8, 4);
                assert_eq!(pkt_len as usize, pad_len as usize + payload_len + 1);
            } else {
                panic!("compute_pad_len failed at payload_len = {}", payload_len);
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate glium;

use std::fs::File;
use std::io::Read;
use std::sync::Mutex;

use glium::{Display, DisplayBuild, Surface, Texture2d};
use glium::backend::glutin_backend::GlutinFacade as Window;
use glium::texture::RawImage2d;

lazy_static! {
    static ref PALETTE: [u8; 768] = {
        let mut _palette = [0; 768];
        let mut f = File::open("pak0/gfx/palette.lmp").unwrap();
        match f.read(&mut _palette) {
            Err(why) => panic!("{}", why),
            Ok(768) => _palette,
            _ => panic!("Bad read on pak0/gfx/palette.lmp"),
        }
    };
}

pub fn tex_from_indexed(window: &Window, indices: &[u8], width: u32, height: u32) -> Texture2d {
    if indices.len() != (width * height) as usize {
        panic!("Bad index list length: {}", indices.len());
    }

    let rgba: Vec<u8> = {
        let mut _rgba: Vec<u8> = Vec::with_capacity(4 * indices.len());
        for i in 0..indices.len() {
            if indices[i] != 0xff {
                for c in 0..3 {
                    _rgba.push(PALETTE[(3 * (indices[i] as usize) + c) as usize]);
                }
                _rgba.push(0xff);
            } else {
                for _ in 0..4 {
                    _rgba.push(0);
                }
            }
        }
        _rgba
    };

    let raw_image = RawImage2d::from_raw_rgba(rgba, (width, height));

    Texture2d::new(window, raw_image).unwrap()
}
<commit_msg>Clean up src/engine.rs<commit_after>extern crate glium;

use std::fs::File;
use std::io::Read;

use glium::Texture2d;
use glium::backend::glutin_backend::GlutinFacade as Window;
use glium::texture::RawImage2d;

lazy_static! {
    static ref PALETTE: [u8; 768] = {
        let mut _palette = [0; 768];
        let mut f = File::open("pak0/gfx/palette.lmp").unwrap();
        match f.read(&mut _palette) {
            Err(why) => panic!("{}", why),
            Ok(768) => _palette,
            _ => panic!("Bad read on pak0/gfx/palette.lmp"),
        }
    };
}

pub fn tex_from_indexed(window: &Window, indices: &[u8], width: u32, height: u32) -> Texture2d {
    if indices.len() != (width * height) as usize {
        panic!("Bad index list length: {}", indices.len());
    }

    let rgba: Vec<u8> = {
        let mut _rgba: Vec<u8> = Vec::with_capacity(4 * indices.len());
        for i in 0..indices.len() {
            if indices[i] != 0xff {
                for c in 0..3 {
                    _rgba.push(PALETTE[(3 * (indices[i] as usize) + c) as usize]);
                }
                _rgba.push(0xff);
            } else {
                for _ in 0..4 {
                    _rgba.push(0);
                }
            }
        }
        _rgba
    };

    let raw_image = RawImage2d::from_raw_rgba(rgba, (width, height));

    Texture2d::new(window, raw_image).unwrap()
}
<|endoftext|>


<commit_before>use std::error::Error as StdError;
use std::fmt;
use std::io;
use std::str;

use serde_json;

#[derive(Debug)]
pub enum BodyErrorCause {
    Utf8Error(str::Utf8Error),
    IoError(io::Error),
    JsonError(serde_json::Error),
}

#[derive(Debug)]
pub struct BodyError {
    pub detail: String,
    pub cause: BodyErrorCause
}

impl StdError for BodyError {
    fn description(&self) -> &str {
        &self.detail[..]
    }
}

impl fmt::Display for BodyError {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        self.description().fmt(formatter)
    }
}
<commit_msg>Implement Error::cause() for BodyError<commit_after>use std::error::Error as StdError;
use std::fmt;
use std::io;
use std::str;

use serde_json;

#[derive(Debug)]
pub enum BodyErrorCause {
    Utf8Error(str::Utf8Error),
    IoError(io::Error),
    JsonError(serde_json::Error),
}

#[derive(Debug)]
pub struct BodyError {
    pub detail: String,
    pub cause: BodyErrorCause
}

impl StdError for BodyError {
    fn description(&self) -> &str {
        &self.detail[..]
    }

    fn cause(&self) -> Option<&StdError> {
        use BodyErrorCause::*;

        match self.cause {
            Utf8Error(ref err) => Some(err),
            IoError(ref err) => Some(err),
            JsonError(ref err) => Some(err),
        }
    }
}

impl fmt::Display for BodyError {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        self.description().fmt(formatter)
    }
}
<|endoftext|>


<commit_before>extern crate serde_json;
extern crate time;
extern crate uuid;
extern crate url;

use controller::EventInfo;
use log::LogLevelFilter;
use logger::MetricsLoggerFactory;
use logger::MetricsLogger;
use std::collections::VecDeque;
use url::percent_encoding;
use url::percent_encoding::SIMPLE_ENCODE_SET;

#[cfg(not(test))]
use config::Config;
#[cfg(not(test))]
use self::uuid::Uuid;
#[cfg(not(test))]
use self::serde_json::Value;

#[allow(non_upper_case_globals)]
// Shortcut to MetricsLoggerFactory function that gets the logger instance.
const logger: fn() -> &'static MetricsLogger = MetricsLoggerFactory::get_logger;

const MAX_EVENT_SIZE: usize = 20;
#[cfg(not(test))]
const KEY_CID: &'static str = "cid";

define_encode_set! {
    /// This encode set is used in the URL parser for query strings.
    pub GOOGLE_ENCODE_SET = [SIMPLE_ENCODE_SET] | {' ', '!', '$', ')', '/'}
}

pub struct Events {
    event_storage: VecDeque<String>,
    event_info: EventInfo,
    client_id: String,
}

impl Events {
    pub fn new(event_info: EventInfo) -> Events {
        Events {
            event_storage: VecDeque::with_capacity(20),
            event_info: event_info,
            client_id: get_client_id(),
        }
    }

    pub fn insert_event(&mut self,
                        event_category: &str,
                        event_action: &str,
                        event_label: &str,
                        event_value: u64)
                        -> bool {

        let event_string = format!("v=1&t=event&tid=UA-77033033-1&cid={0}&ec={1}&ea={2}&el={3}&ev={4}&an={5}&av={6}&ul={7}&cd1={8}&cd2={9}&cd3={10}&cd4={11}&cd5={12}&cd6={13}&cd7={14}",
                                   self.encode_value(self.client_id.clone()),
                                   self.encode_value(event_category.to_string()),
                                   self.encode_value(event_action.to_string()),
                                   self.encode_value(event_label.to_string()),
                                   event_value,
                                   self.encode_value(self.event_info.app_name.clone()),
                                   self.encode_value(self.event_info.app_version.clone()),
                                   self.encode_value(self.event_info.locale.clone()),
                                   self.encode_value(self.event_info.os.clone()),
                                   self.encode_value(self.event_info.os_version.clone()),
                                   self.encode_value(self.event_info.device.clone()),
                                   self.encode_value(self.event_info.arch.clone()),
                                   self.encode_value(self.event_info.app_platform.clone()),
                                   self.encode_value(self.event_info.app_build_id.clone()),
                                   self.encode_value(get_time_string()));
        logger().log(LogLevelFilter::Debug,
                     format!("Inserted event: {}", event_string).as_str());
        self.event_storage.push_back(event_string);

        true
    }
        pub fn insert_floating_point_event(&mut self,
                                           event_category: &str,
                                           event_action: &str,
                                           event_label: &str,
                                           event_value: f64)
                                           -> bool {

            let event_string = format!("v=1&t=event&tid=UA-77033033-1&cid={0}&ec={1}&ea={2}&el={3}&ev={4}&an={5}&av={6}&ul={7}&cd1={8}&cd2={9}&cd3={10}&cd4={11}&cd5={12}&cd6={13}&cd7={14}&cd8={15}",
                                       self.encode_value(self.client_id.clone()),
                                       self.encode_value(event_category.to_string()),
                                       self.encode_value(event_action.to_string()),
                                       self.encode_value(event_label.to_string()),
                                       1,
                                       self.encode_value(self.event_info.app_name.clone()),
                                       self.encode_value(self.event_info.app_version.clone()),
                                       self.encode_value(self.event_info.locale.clone()),
                                       self.encode_value(self.event_info.os.clone()),
                                       self.encode_value(self.event_info.os_version.clone()),
                                       self.encode_value(self.event_info.device.clone()),
                                       self.encode_value(self.event_info.arch.clone()),
                                       self.encode_value(self.event_info.app_platform.clone()),
                                       self.encode_value(self.event_info.app_build_id.clone()),
                                       self.encode_value(get_time_string()),
                                       event_value);
            logger().log(LogLevelFilter::Debug,
                         format!("Inserted event: {}", event_string).as_str());
            self.event_storage.push_back(event_string);

            true
    }
    fn encode_value(&self, value: String) -> String {
        let mut value_encoded = String::new();
        let value_vec = value.into_bytes();
        let mut bs = percent_encoding::percent_encode(&value_vec, GOOGLE_ENCODE_SET);

        loop {
            match bs.next() {
                Some(bs) => {
                    value_encoded.push_str(bs);
                }
                None => {
                    break;
                }
            }
        }
        value_encoded
    }

    pub fn is_time_to_send(&mut self) -> bool {
        self.event_storage.len() >= MAX_EVENT_SIZE
    }

    pub fn is_empty(&mut self) -> bool {
        self.event_storage.is_empty()
    }

    pub fn get_events_as_body(&mut self) -> String {
        let mut body = String::new();
        let mut i: usize = 0;
        while i < MAX_EVENT_SIZE {
            let val: Option<String> = self.event_storage.pop_front();
            match val {
                Some(v) => {
                    body.push_str(&v);
                    body.push_str("\n");
                }
                None => {
                    break;
                }
            }
            i = i + 1;
        }
        body
    }
}

#[cfg(not(test))]
fn get_client_id() -> String {
    let mut cid = String::new();
    let mut cfg = Config::new();
    if cfg.init("cid.dat") {
        let val: Option<Value> = cfg.get(KEY_CID);
        match val {
            Some(_) => cid.push_str(&cfg.get_string(KEY_CID).to_string()),
            None => panic!("Error: no cid written"),
        }
    } else {
        cid.push_str(&Uuid::new_v4().to_hyphenated_string().to_string());
        let json = format!("{{\"{0}\":\"{1}\"}}", KEY_CID, cid);
        cfg.create_and_write_json("cid.dat", &json);
    }
    cid
}

#[cfg(test)]
fn get_client_id() -> String {
    "9eccb690-93aa-4513-835a-9a4f0f0e2a71".to_string()
}

#[cfg(not(test))]
fn get_time_string() -> String {
    let ts = time::now_utc();
    let time_string = format!("{0:4}-{1:02}-{2:02} {3:02}:{4:02}:{5:02}",
                              ts.tm_year + 1900,
                              ts.tm_mon + 1,
                              ts.tm_mday,
                              ts.tm_hour,
                              ts.tm_min,
                              ts.tm_sec);
    time_string
}

#[cfg(test)]
fn get_time_string() -> String {
    "2016-05-25 22:36:57".to_string()
}

#[cfg(not(feature = "integration"))]
#[cfg(test)]
describe! events_functionality {
    before_each {
        use controller::EventInfo;

        let event_info = EventInfo::new(
                    "en-us",
                    "linux",
                    "1.2",
                    "RPi/2",
                    "iot_app",
                    "1.0",
                    "default",
                    "20160320123456",
                    "rust test",
                    "arm");
        let mut ev = Events::new(event_info);
        ev.client_id = "9eccb690-93aa-4513-835a-9a4f0f0e2a71".to_string();
    }
    it "should insert an event" {
        ev.insert_event("category", "action", "label", 1);
        assert_eq!(ev.event_storage.len(), 1);
    }

    it "should format an event properly" {
        let formatted_event = "v=1&t=event&tid=UA-77033033-1&cid=9eccb690-93aa-4513-835a-9a4f0f0e2a71&ec=category&ea=action\
                                &el=label&ev=1&an=iot_app&av=1.0&ul=en-us&cd1=linux&cd2=1.2&cd3=RPi%2F2&cd4=arm&cd5=rust%20test&cd6=20160320123456\
                                &cd7=2016-05-25%2022:36:57";
        ev.insert_event("category", "action", "label", 1);
        assert_eq!(formatted_event, ev.event_storage.pop_front().unwrap());
    }

    it "should return true if there are more than MAX_EVENT_SIZE" {
        for _ in 0..21 {
            ev.insert_event("category", "action", "label", 1);
        }
        assert_eq!(ev.is_time_to_send(), true);
    }

    it "should return true if there are exactly MAX_EVENT_SIZE" {
        for _ in 0..20 {
            ev.insert_event("category", "action", "label", 1);
        }
        assert_eq!(ev.is_time_to_send(), true);
    }

    it "should return true if there are less than MAX_EVENT_SIZE" {
        for _ in 0..19 {
            ev.insert_event("category", "action", "label", 1);
        }
        assert_eq!(ev.is_time_to_send(), false);
    }

    it "is_empty should return false if there are events" {
        for _ in 0..19 {
            ev.insert_event("category", "action", "label", 1);
        }
        assert_eq!(ev.is_empty(), false);
    }

    it "is_empty should return true if storage is empty" {
        assert_eq!(ev.is_empty(), true);
    }

    it "should format the body correctly for one event" {
        let formatted_body = "v=1&t=event&tid=UA-77033033-1&cid=9eccb690-93aa-4513-835a-9a4f0f0e2a71&ec=category&ea=action\
                                &el=label&ev=1&an=iot_app&av=1.0&ul=en-us&cd1=linux&cd2=1.2&cd3=RPi%2F2&cd4=arm&cd5=rust%20test&cd6=20160320123456\
                                &cd7=2016-05-25%2022:36:57\n";
        ev.insert_event("category", "action", "label", 1);
        let body = ev.get_events_as_body();
        assert_eq!(body, formatted_body);
    }

    it "should format the body correctly for multiple events" {
        let formatted_body = "v=1&t=event&tid=UA-77033033-1&cid=9eccb690-93aa-4513-835a-9a4f0f0e2a71&ec=category&ea=action\
                                  &el=label&ev=1&an=iot_app&av=1.0&ul=en-us&cd1=linux&cd2=1.2&cd3=RPi%2F2&cd4=arm&cd5=rust%20test\
                                  &cd6=20160320123456&cd7=2016-05-25%2022:36:57\n\
                              v=1&t=event&tid=UA-77033033-1&cid=9eccb690-93aa-4513-835a-9a4f0f0e2a71&ec=category&ea=action\
                                  &el=label&ev=1&an=iot_app&av=1.0&ul=en-us&cd1=linux&cd2=1.2&cd3=RPi%2F2\
                                  &cd4=arm&cd5=rust%20test&cd6=20160320123456&cd7=2016-05-25%2022:36:57\n";
        ev.insert_event("category", "action", "label", 1);
        ev.insert_event("category", "action", "label", 1);
        let body = ev.get_events_as_body();
        assert_eq!(body, formatted_body);
    }
describe! events_functionality {
   it "should format the body correctly for one floating point event" {
       let formatted_body = "v=1&t=event&tid=UA-77033033-1&cid=9eccb690-93aa-4513-835a-9a4f0f0e2a71&ec=category&ea=action\
                                &el=label&ev=1&an=iot_app&av=1.0&ul=en-us&cd1=linux&cd2=1.2&cd3=RPi%2F2&cd4=arm&cd5=rust%20test&cd6=20160320123456\
                                &cd7=2016-05-25%2022:36:57&cd8=1.0\n";
        ev.insert_event("category", "action", "label", 1.0);
        ev.insert_event("category", "action", "label", 1.0);
        let body = ev.get_events_as_body();
        assert_eq!(body, formatted_body);
   }
 }
}
<commit_msg>fix change in event rs<commit_after>extern crate serde_json;
extern crate time;
extern crate uuid;
extern crate url;

use controller::EventInfo;
use log::LogLevelFilter;
use logger::MetricsLoggerFactory;
use logger::MetricsLogger;
use std::collections::VecDeque;
use url::percent_encoding;
use url::percent_encoding::SIMPLE_ENCODE_SET;

#[cfg(not(test))]
use config::Config;
#[cfg(not(test))]
use self::uuid::Uuid;
#[cfg(not(test))]
use self::serde_json::Value;

#[allow(non_upper_case_globals)]
// Shortcut to MetricsLoggerFactory function that gets the logger instance.
const logger: fn() -> &'static MetricsLogger = MetricsLoggerFactory::get_logger;

const MAX_EVENT_SIZE: usize = 20;
#[cfg(not(test))]
const KEY_CID: &'static str = "cid";

define_encode_set! {
    /// This encode set is used in the URL parser for query strings.
    pub GOOGLE_ENCODE_SET = [SIMPLE_ENCODE_SET] | {' ', '!', '$', ')', '/'}
}

pub struct Events {
    event_storage: VecDeque<String>,
    event_info: EventInfo,
    client_id: String,
}

impl Events {
    pub fn new(event_info: EventInfo) -> Events {
        Events {
            event_storage: VecDeque::with_capacity(20),
            event_info: event_info,
            client_id: get_client_id(),
        }
    }

    pub fn insert_event(&mut self,
                        event_category: &str,
                        event_action: &str,
                        event_label: &str,
                        event_value: u64)
                        -> bool {

        let event_string = format!("v=1&t=event&tid=UA-77033033-1&cid={0}&ec={1}&ea={2}&el={3}&ev={4}&an={5}&av={6}&ul={7}&cd1={8}&cd2={9}&cd3={10}&cd4={11}&cd5={12}&cd6={13}&cd7={14}",
                                   self.encode_value(self.client_id.clone()),
                                   self.encode_value(event_category.to_string()),
                                   self.encode_value(event_action.to_string()),
                                   self.encode_value(event_label.to_string()),
                                   event_value,
                                   self.encode_value(self.event_info.app_name.clone()),
                                   self.encode_value(self.event_info.app_version.clone()),
                                   self.encode_value(self.event_info.locale.clone()),
                                   self.encode_value(self.event_info.os.clone()),
                                   self.encode_value(self.event_info.os_version.clone()),
                                   self.encode_value(self.event_info.device.clone()),
                                   self.encode_value(self.event_info.arch.clone()),
                                   self.encode_value(self.event_info.app_platform.clone()),
                                   self.encode_value(self.event_info.app_build_id.clone()),
                                   self.encode_value(get_time_string()));
        logger().log(LogLevelFilter::Debug,
                     format!("Inserted event: {}", event_string).as_str());
        self.event_storage.push_back(event_string);

        true
    }
        pub fn insert_floating_point_event(&mut self,
                                           event_category: &str,
                                           event_action: &str,
                                           event_label: &str,
                                           event_value: f64)
                                           -> bool {

            let event_string = format!("v=1&t=event&tid=UA-77033033-1&cid={0}&ec={1}&ea={2}&el={3}&ev={4}&an={5}&av={6}&ul={7}&cd1={8}&cd2={9}&cd3={10}&cd4={11}&cd5={12}&cd6={13}&cd7={14}&cd8={15:.1}",
                                       self.encode_value(self.client_id.clone()),
                                       self.encode_value(event_category.to_string()),
                                       self.encode_value(event_action.to_string()),
                                       self.encode_value(event_label.to_string()),
                                       1,
                                       self.encode_value(self.event_info.app_name.clone()),
                                       self.encode_value(self.event_info.app_version.clone()),
                                       self.encode_value(self.event_info.locale.clone()),
                                       self.encode_value(self.event_info.os.clone()),
                                       self.encode_value(self.event_info.os_version.clone()),
                                       self.encode_value(self.event_info.device.clone()),
                                       self.encode_value(self.event_info.arch.clone()),
                                       self.encode_value(self.event_info.app_platform.clone()),
                                       self.encode_value(self.event_info.app_build_id.clone()),
                                       self.encode_value(get_time_string()),
                                       event_value);
            logger().log(LogLevelFilter::Debug,
                         format!("Inserted event: {}", event_string).as_str());
            self.event_storage.push_back(event_string);

            true
    }
    fn encode_value(&self, value: String) -> String {
        let mut value_encoded = String::new();
        let value_vec = value.into_bytes();
        let mut bs = percent_encoding::percent_encode(&value_vec, GOOGLE_ENCODE_SET);

        loop {
            match bs.next() {
                Some(bs) => {
                    value_encoded.push_str(bs);
                }
                None => {
                    break;
                }
            }
        }
        value_encoded
    }

    pub fn is_time_to_send(&mut self) -> bool {
        self.event_storage.len() >= MAX_EVENT_SIZE
    }

    pub fn is_empty(&mut self) -> bool {
        self.event_storage.is_empty()
    }

    pub fn get_events_as_body(&mut self) -> String {
        let mut body = String::new();
        let mut i: usize = 0;
        while i < MAX_EVENT_SIZE {
            let val: Option<String> = self.event_storage.pop_front();
            match val {
                Some(v) => {
                    body.push_str(&v);
                    body.push_str("\n");
                }
                None => {
                    break;
                }
            }
            i = i + 1;
        }
        body
    }
}

#[cfg(not(test))]
fn get_client_id() -> String {
    let mut cid = String::new();
    let mut cfg = Config::new();
    if cfg.init("cid.dat") {
        let val: Option<Value> = cfg.get(KEY_CID);
        match val {
            Some(_) => cid.push_str(&cfg.get_string(KEY_CID).to_string()),
            None => panic!("Error: no cid written"),
        }
    } else {
        cid.push_str(&Uuid::new_v4().to_hyphenated_string().to_string());
        let json = format!("{{\"{0}\":\"{1}\"}}", KEY_CID, cid);
        cfg.create_and_write_json("cid.dat", &json);
    }
    cid
}

#[cfg(test)]
fn get_client_id() -> String {
    "9eccb690-93aa-4513-835a-9a4f0f0e2a71".to_string()
}

#[cfg(not(test))]
fn get_time_string() -> String {
    let ts = time::now_utc();
    let time_string = format!("{0:4}-{1:02}-{2:02} {3:02}:{4:02}:{5:02}",
                              ts.tm_year + 1900,
                              ts.tm_mon + 1,
                              ts.tm_mday,
                              ts.tm_hour,
                              ts.tm_min,
                              ts.tm_sec);
    time_string
}

#[cfg(test)]
fn get_time_string() -> String {
    "2016-05-25 22:36:57".to_string()
}

#[cfg(not(feature = "integration"))]
#[cfg(test)]
describe! events_functionality {
    before_each {
        use controller::EventInfo;

        let event_info = EventInfo::new(
                    "en-us",
                    "linux",
                    "1.2",
                    "RPi/2",
                    "iot_app",
                    "1.0",
                    "default",
                    "20160320123456",
                    "rust test",
                    "arm");
        let mut ev = Events::new(event_info);
        ev.client_id = "9eccb690-93aa-4513-835a-9a4f0f0e2a71".to_string();
    }
    it "should insert an event" {
        ev.insert_event("category", "action", "label", 1);
        assert_eq!(ev.event_storage.len(), 1);
    }

    it "should format an event properly" {
        let formatted_event = "v=1&t=event&tid=UA-77033033-1&cid=9eccb690-93aa-4513-835a-9a4f0f0e2a71&ec=category&ea=action\
                                &el=label&ev=1&an=iot_app&av=1.0&ul=en-us&cd1=linux&cd2=1.2&cd3=RPi%2F2&cd4=arm&cd5=rust%20test&cd6=20160320123456\
                                &cd7=2016-05-25%2022:36:57";
        ev.insert_event("category", "action", "label", 1);
        assert_eq!(formatted_event, ev.event_storage.pop_front().unwrap());
    }

    it "should return true if there are more than MAX_EVENT_SIZE" {
        for _ in 0..21 {
            ev.insert_event("category", "action", "label", 1);
        }
        assert_eq!(ev.is_time_to_send(), true);
    }

    it "should return true if there are exactly MAX_EVENT_SIZE" {
        for _ in 0..20 {
            ev.insert_event("category", "action", "label", 1);
        }
        assert_eq!(ev.is_time_to_send(), true);
    }

    it "should return true if there are less than MAX_EVENT_SIZE" {
        for _ in 0..19 {
            ev.insert_event("category", "action", "label", 1);
        }
        assert_eq!(ev.is_time_to_send(), false);
    }

    it "is_empty should return false if there are events" {
        for _ in 0..19 {
            ev.insert_event("category", "action", "label", 1);
        }
        assert_eq!(ev.is_empty(), false);
    }

    it "is_empty should return true if storage is empty" {
        assert_eq!(ev.is_empty(), true);
    }

    it "should format the body correctly for one event" {
        let formatted_body = "v=1&t=event&tid=UA-77033033-1&cid=9eccb690-93aa-4513-835a-9a4f0f0e2a71&ec=category&ea=action\
                                &el=label&ev=1&an=iot_app&av=1.0&ul=en-us&cd1=linux&cd2=1.2&cd3=RPi%2F2&cd4=arm&cd5=rust%20test&cd6=20160320123456\
                                &cd7=2016-05-25%2022:36:57\n";
        ev.insert_event("category", "action", "label", 1);
        let body = ev.get_events_as_body();
        assert_eq!(body, formatted_body);
    }

    it "should format the body correctly for multiple events" {
        let formatted_body = "v=1&t=event&tid=UA-77033033-1&cid=9eccb690-93aa-4513-835a-9a4f0f0e2a71&ec=category&ea=action\
                                  &el=label&ev=1&an=iot_app&av=1.0&ul=en-us&cd1=linux&cd2=1.2&cd3=RPi%2F2&cd4=arm&cd5=rust%20test\
                                  &cd6=20160320123456&cd7=2016-05-25%2022:36:57\n\
                              v=1&t=event&tid=UA-77033033-1&cid=9eccb690-93aa-4513-835a-9a4f0f0e2a71&ec=category&ea=action\
                                  &el=label&ev=1&an=iot_app&av=1.0&ul=en-us&cd1=linux&cd2=1.2&cd3=RPi%2F2\
                                  &cd4=arm&cd5=rust%20test&cd6=20160320123456&cd7=2016-05-25%2022:36:57\n";
        ev.insert_event("category", "action", "label", 1);
        ev.insert_event("category", "action", "label", 1);
        let body = ev.get_events_as_body();
        assert_eq!(body, formatted_body);
    }
describe! events_functionality {
   it "should format the body correctly for one floating point event" {
       let formatted_body = "v=1&t=event&tid=UA-77033033-1&cid=9eccb690-93aa-4513-835a-9a4f0f0e2a71&ec=category&ea=action\
                                &el=label&ev=1&an=iot_app&av=1.0&ul=en-us&cd1=linux&cd2=1.2&cd3=RPi%2F2&cd4=arm&cd5=rust%20test&cd6=20160320123456\
                                &cd7=2016-05-25%2022:36:57&cd8=1.0\n";
        ev.insert_floating_point_event("category", "action", "label", 1.0);
        let body = ev.get_events_as_body();
        assert_eq!(body, formatted_body);
   }
 }
}
<|endoftext|>


<commit_before>// Implements http://rosettacode.org/wiki/Events
//
// Rust uses condition variables (Condvars) for asynchronous event processing.  Each Mutex has a
// list of zero or more Condvars, which are essentially events that the task may wait on or signal
// when it holds the mutex.  When a task begins to wait on a condvar, it enters a waiting state
// until the condvar is signaled by another task, at which point it resumes processing.  Thus, each
// condvar represents an event on which a task may wait.  The one subtlety is that condvar signals
// are only received if there is actually a task waiting on the signal--see the below program for
// an example of how this may be achieved in practice.

extern crate sync;
extern crate time;

use std::io::timer::Timer;
use std::sync::Arc;
use std::time::duration::Duration;
use sync::raw::Mutex;

// Helper states to demonstrate the control flow of the program.
#[deriving(PartialEq, Show)]

// Given a duration to wait before sending an event from one process to another, returns the
// elapsed time before the event was actually sent.
fn handle_event(duration: Duration) -> Duration {
    // Create a Mutex.  By default a Mutex is created with a single condition variable (condvar_id
    // 0) but it can be created with an arbitrary number using Mutex::new_with_condvars();
    let mutex = Arc::new(Mutex::new());
    let mutex_ = mutex.clone();
    let mut timer = Timer::new().unwrap();
    let start = time::precise_time_ns();
    // Lock the mutex
    let guard = mutex.lock();
    // Start our secondary task (which will signal our waiting main task)
    spawn(proc() {
        // Lock the mutex
        let guard = mutex_.lock();
        // Sleep for `duration`.
        timer.sleep(duration);
        // Signal the waiting mutex (equivalent to guard.cond.signal_on(0)).
        // One can also signal to all tasks on the waiting mutex with broadcast (broadcast_on(0)).
        //
        // Note that if no tasks are actually waiting yet (which is possible, if we got the lock
        // before the other task), then this might report that it failed to wake up any tasks.
        // That is why the mutex was locked before the task was spawned--we know we cannot possibly
        // get past the mutex at the top of the task until the wait() statement below is reached.
        guard.cond.signal();
        // Although we signaled the waiting mutex, it will not awaken until this guard is dropped.
    });
    // Wait for the event state to be set to signaled (equivalent to guard.cond.wait_on(0)).
    guard.cond.wait();
    // Should be done signaling (i.e. we've waited for `duration`).
    let end = time::precise_time_ns();
    // When the guard exits scope, the condvar is reset.
    drop(guard);
    // Return the elapsed time
    Duration::nanoseconds((end - start) as i64)
}

#[cfg(not(test))]
pub fn main() {
    let duration = Duration::seconds(1); // Process event after one second.
    println!("{} elapsed before event triggered", handle_event(duration));
}

#[test]
pub fn test_events() {
    let duration = Duration::seconds(1) / 10; // Process event after one tenth of a second.
    // Make sure it really did take at least that long for the event to be processed.
    assert!(duration <= handle_event(duration));
}
<commit_msg>Removing unused attributes left behind by mistake.<commit_after>// Implements http://rosettacode.org/wiki/Events
//
// Rust uses condition variables (Condvars) for asynchronous event processing.  Each Mutex has a
// list of zero or more Condvars, which are essentially events that the task may wait on or signal
// when it holds the mutex.  When a task begins to wait on a condvar, it enters a waiting state
// until the condvar is signaled by another task, at which point it resumes processing.  Thus, each
// condvar represents an event on which a task may wait.  The one subtlety is that condvar signals
// are only received if there is actually a task waiting on the signal--see the below program for
// an example of how this may be achieved in practice.

extern crate sync;
extern crate time;

use std::io::timer::Timer;
use std::sync::Arc;
use std::time::duration::Duration;
use sync::raw::Mutex;

// Given a duration to wait before sending an event from one process to another, returns the
// elapsed time before the event was actually sent.
fn handle_event(duration: Duration) -> Duration {
    // Create a Mutex.  By default a Mutex is created with a single condition variable (condvar_id
    // 0) but it can be created with an arbitrary number using Mutex::new_with_condvars();
    let mutex = Arc::new(Mutex::new());
    let mutex_ = mutex.clone();
    let mut timer = Timer::new().unwrap();
    let start = time::precise_time_ns();
    // Lock the mutex
    let guard = mutex.lock();
    // Start our secondary task (which will signal our waiting main task)
    spawn(proc() {
        // Lock the mutex
        let guard = mutex_.lock();
        // Sleep for `duration`.
        timer.sleep(duration);
        // Signal the waiting mutex (equivalent to guard.cond.signal_on(0)).
        // One can also signal to all tasks on the waiting mutex with broadcast (broadcast_on(0)).
        //
        // Note that if no tasks are actually waiting yet (which is possible, if we got the lock
        // before the other task), then this might report that it failed to wake up any tasks.
        // That is why the mutex was locked before the task was spawned--we know we cannot possibly
        // get past the mutex at the top of the task until the wait() statement below is reached.
        guard.cond.signal();
        // Although we signaled the waiting mutex, it will not awaken until this guard is dropped.
    });
    // Wait for the event state to be set to signaled (equivalent to guard.cond.wait_on(0)).
    guard.cond.wait();
    // Should be done signaling (i.e. we've waited for `duration`).
    let end = time::precise_time_ns();
    // When the guard exits scope, the condvar is reset.
    drop(guard);
    // Return the elapsed time
    Duration::nanoseconds((end - start) as i64)
}

#[cfg(not(test))]
pub fn main() {
    let duration = Duration::seconds(1); // Process event after one second.
    println!("{} elapsed before event triggered", handle_event(duration));
}

#[test]
pub fn test_events() {
    let duration = Duration::seconds(1) / 10; // Process event after one tenth of a second.
    // Make sure it really did take at least that long for the event to be processed.
    assert!(duration <= handle_event(duration));
}
<|endoftext|>


<commit_before>//! Utility wrappers to simplify writing OpenGL code.
//!
//! This crate aspires to provide an abstraction over OpenGL's raw API in order to simplify the
//! task of writing higher-level rendering code for OpenGL. `gl-util` is much in the vein of
//! [glutin](https://github.com/tomaka/glium) and [gfx-rs](https://github.com/gfx-rs/gfx) before
//! it, the main difference being that it is much more poorly constructed and is being developed by
//! someone much less experienced with OpenGL.

extern crate bootstrap_gl as gl;

use gl::{BufferName, BufferTarget, BufferUsage, ClearBufferMask, DrawMode, GlType, VertexArrayName};
use std::mem;

pub use gl::{AttributeLocation};
pub use gl::platform::swap_buffers;

/// Initializes global OpenGL state and creates the OpenGL context needed to perform rendering.
pub fn init() {
    gl::create_context();
}

/// Represents a buffer of vertex data and the layout of that data.
///
/// Wraps a vertex buffer object and vertex array object into one struct.
#[derive(Debug)]
pub struct VertexBuffer {
    buffer_name: BufferName,
    vertex_array_name: VertexArrayName,
}

impl VertexBuffer {
    /// Creates a new `VertexBuffer` object.
    pub fn new() -> VertexBuffer {
        let mut buffer_name = BufferName::null();
        let mut vertex_array_name = VertexArrayName::null();
        unsafe {
            gl::gen_buffers(1, &mut buffer_name);
            gl::gen_vertex_arrays(1, &mut vertex_array_name);
        }

        VertexBuffer {
            buffer_name: buffer_name,
            vertex_array_name: vertex_array_name,
        }
    }

    /// Fills the buffer with the contents of the data slice.
    pub fn set_data(&mut self, data: &[f32]) {
        let data_ptr = data.as_ptr() as *const ();
        let byte_count = data.len() * mem::size_of::<f32>();

        unsafe {
            gl::bind_buffer(BufferTarget::Array, self.buffer_name);
            gl::buffer_data(
                BufferTarget::Array,
                byte_count as isize,
                data_ptr,
                BufferUsage::StaticDraw);
            gl::bind_buffer(BufferTarget::Array, BufferName::null());
        }
    }
}

impl Drop for VertexBuffer {
    fn drop(&mut self) {
        unsafe {
            gl::delete_buffers(1, &mut self.buffer_name);
            gl::delete_vertex_arrays(1, &mut self.vertex_array_name);
        }
    }
}
<commit_msg>Add Render Functionality For VertexBuffer<commit_after>//! Utility wrappers to simplify writing OpenGL code.
//!
//! This crate aspires to provide an abstraction over OpenGL's raw API in order to simplify the
//! task of writing higher-level rendering code for OpenGL. `gl-util` is much in the vein of
//! [glutin](https://github.com/tomaka/glium) and [gfx-rs](https://github.com/gfx-rs/gfx) before
//! it, the main difference being that it is much more poorly constructed and is being developed by
//! someone much less experienced with OpenGL.

extern crate bootstrap_gl as gl;

use gl::{BufferName, BufferTarget, BufferUsage, ClearBufferMask, DrawMode, GlType, VertexArrayName};
use std::mem;

pub use gl::{AttributeLocation};
pub use gl::platform::swap_buffers;

/// Initializes global OpenGL state and creates the OpenGL context needed to perform rendering.
pub fn init() {
    gl::create_context();
}

/// TODO: Take clear mask (and values) as parameters.
pub fn clear() {
    unsafe { gl::clear(ClearBufferMask::Color); }
}

/// Represents a buffer of vertex data and the layout of that data.
///
/// Wraps a vertex buffer object and vertex array object into one struct.
#[derive(Debug)]
pub struct VertexBuffer {
    buffer_name: BufferName,
    vertex_array_name: VertexArrayName,
}

impl VertexBuffer {
    /// Creates a new `VertexBuffer` object.
    pub fn new() -> VertexBuffer {
        let mut buffer_name = BufferName::null();
        let mut vertex_array_name = VertexArrayName::null();
        unsafe {
            gl::gen_buffers(1, &mut buffer_name);
            gl::gen_vertex_arrays(1, &mut vertex_array_name);
        }

        VertexBuffer {
            buffer_name: buffer_name,
            vertex_array_name: vertex_array_name,
        }
    }

    /// Fills the buffer with the contents of the data slice.
    pub fn set_data(&mut self, data: &[f32]) {
        let data_ptr = data.as_ptr() as *const ();
        let byte_count = data.len() * mem::size_of::<f32>();

        unsafe {
            gl::bind_buffer(BufferTarget::Array, self.buffer_name);
            gl::buffer_data(
                BufferTarget::Array,
                byte_count as isize,
                data_ptr,
                BufferUsage::StaticDraw);
            gl::bind_buffer(BufferTarget::Array, BufferName::null());
        }
    }

    /// Specifies how the data for a particular vertex attribute is laid out in the buffer.
    pub fn set_attrib(
        &mut self,
        attrib: AttributeLocation,
        elements: usize,
        stride: usize,
        offset: usize
    ) {
        unsafe {
            gl::bind_buffer(BufferTarget::Array, self.buffer_name);
            gl::bind_vertex_array(self.vertex_array_name);

            gl::enable_vertex_attrib_array(AttributeLocation::from_index(0));
            gl::vertex_attrib_pointer(
                attrib,
                elements as i32,
                GlType::Float,
                false,
                stride as i32,
                offset);

            gl::bind_vertex_array(VertexArrayName::null());
            gl::bind_buffer(BufferTarget::Array, BufferName::null());
        }
    }

    /// Draws the contents of the vertex buffer to the screen.
    pub fn draw(&self) {
        unsafe {
            gl::bind_buffer(BufferTarget::Array, self.buffer_name);
            gl::bind_vertex_array(self.vertex_array_name);

            gl::draw_arrays(DrawMode::Triangles, 0, 3);

            gl::bind_vertex_array(VertexArrayName::null());
            gl::bind_buffer(BufferTarget::Array, BufferName::null());
        }
    }
}

impl Drop for VertexBuffer {
    fn drop(&mut self) {
        unsafe {
            gl::delete_buffers(1, &mut self.buffer_name);
            gl::delete_vertex_arrays(1, &mut self.vertex_array_name);
        }
    }
}
<|endoftext|>


<commit_before>use numvec::Vec2f;
use std::str;
use std::vec::Vec;
use super::archive::Archive;
use super::types::{WadThing, WadLinedef, WadSidedef, WadVertex, WadSeg,
                   WadSubsector, WadNode, WadSector, VertexId, LevelName};
use super::util::from_wad_coords;

pub struct Level {
    pub things: Vec<WadThing>,
    pub linedefs: Vec<WadLinedef>,
    pub sidedefs: Vec<WadSidedef>,
    pub vertices: Vec<WadVertex>,
    pub segs: Vec<WadSeg>,
    pub subsectors: Vec<WadSubsector>,
    pub nodes: Vec<WadNode>,
    pub sectors: Vec<WadSector>,
}

impl Level {
    pub fn from_archive(wad: &mut Archive, name: &LevelName) -> Level {
        info!("Reading level data for '{}'...", str::from_utf8(name).unwrap());
        let start_index = wad.get_lump_index(name).expect("No such level.");
        let things = wad.read_lump::<WadThing>(start_index + 1);
        let linedefs = wad.read_lump::<WadLinedef>(start_index + 2);
        let sidedefs = wad.read_lump::<WadSidedef>(start_index + 3);
        let vertices = wad.read_lump::<WadVertex>(start_index + 4);
        let segs = wad.read_lump::<WadSeg>(start_index + 5);
        let subsectors = wad.read_lump::<WadSubsector>(start_index + 6);
        let nodes = wad.read_lump::<WadNode>(start_index + 7);
        let sectors = wad.read_lump::<WadSector>(start_index + 8);

        info!("Loaded level '{}':", str::from_utf8(name).unwrap());
        info!("    {:4} things", things.len())
        info!("    {:4} linedefs", linedefs.len())
        info!("    {:4} sidedefs", sidedefs.len())
        info!("    {:4} vertices", vertices.len())
        info!("    {:4} segs", segs.len())
        info!("    {:4} subsectors", subsectors.len())
        info!("    {:4} nodes", nodes.len())
        info!("    {:4} sectors", sectors.len())

        Level {
            things: things,
            linedefs: linedefs,
            sidedefs: sidedefs,
            vertices: vertices,
            segs: segs,
            subsectors: subsectors,
            nodes: nodes,
            sectors: sectors,
        }
    }

    pub fn vertex(&self, id: VertexId) -> Vec2f {
        from_wad_coords(self.vertices[id as uint].x,
                        self.vertices[id as uint].y)
    }

    pub fn seg_linedef<'a>(&'a self, seg: &WadSeg) -> &'a WadLinedef {
        &self.linedefs[seg.linedef as uint]
    }

    pub fn seg_vertices(&self, seg: &WadSeg) -> (Vec2f, Vec2f) {
        (self.vertex(seg.start_vertex), self.vertex(seg.end_vertex))
    }

    pub fn left_sidedef<'a>(&'a self, linedef: &WadLinedef)
            -> &'a WadSidedef {
        &self.sidedefs[linedef.left_side as uint]
    }

    pub fn right_sidedef<'a>(&'a self, linedef: &WadLinedef)
            -> &'a WadSidedef {
        &self.sidedefs[linedef.right_side as uint]
    }

    pub fn sidedef_sector<'a>(&'a self, sidedef: &WadSidedef) -> &'a WadSector {
        &self.sectors[sidedef.sector as uint]
    }

    pub fn ssector_segs<'a>(&'a self, ssector: &WadSubsector) -> &'a [WadSeg] {
        self.segs.slice(ssector.first_seg as uint,
                        (ssector.first_seg as uint + ssector.num_segs as uint))
    }
}
<commit_msg>Got rid of some magic numbers<commit_after>use numvec::Vec2f;
use std::str;
use std::vec::Vec;
use super::archive::Archive;
use super::types::{WadThing, WadLinedef, WadSidedef, WadVertex, WadSeg,
                   WadSubsector, WadNode, WadSector, VertexId, LevelName};
use super::util::from_wad_coords;

static THINGS_OFFSET: uint = 1;
static LINEDEFS_OFFSET: uint = 2;
static SIDEDEFS_OFFSET: uint = 3;
static VERTICES_OFFSET: uint = 4;
static SEGS_OFFSET: uint = 5;
static SSECTORS_OFFSET: uint = 6;
static NODES_OFFSET: uint = 7;
static SECTORS_OFFSET: uint = 8;

pub struct Level {
    pub things: Vec<WadThing>,
    pub linedefs: Vec<WadLinedef>,
    pub sidedefs: Vec<WadSidedef>,
    pub vertices: Vec<WadVertex>,
    pub segs: Vec<WadSeg>,
    pub subsectors: Vec<WadSubsector>,
    pub nodes: Vec<WadNode>,
    pub sectors: Vec<WadSector>,
}

impl Level {
    pub fn from_archive(wad: &mut Archive, name: &LevelName) -> Level {
        info!("Reading level data for '{}'...", str::from_utf8(name).unwrap());
        let start_index = wad.get_lump_index(name).expect("No such level.");
        let things = wad.read_lump(start_index + THINGS_OFFSET);
        let linedefs = wad.read_lump(start_index + LINEDEFS_OFFSET);
        let sidedefs = wad.read_lump(start_index + SIDEDEFS_OFFSET);
        let vertices = wad.read_lump(start_index + VERTICES_OFFSET);
        let segs = wad.read_lump(start_index + SEGS_OFFSET);
        let subsectors = wad.read_lump(start_index + SSECTORS_OFFSET);
        let nodes = wad.read_lump(start_index + NODES_OFFSET);
        let sectors = wad.read_lump(start_index + SECTORS_OFFSET);

        info!("Loaded level '{}':", str::from_utf8(name).unwrap());
        info!("    {:4} things", things.len())
        info!("    {:4} linedefs", linedefs.len())
        info!("    {:4} sidedefs", sidedefs.len())
        info!("    {:4} vertices", vertices.len())
        info!("    {:4} segs", segs.len())
        info!("    {:4} subsectors", subsectors.len())
        info!("    {:4} nodes", nodes.len())
        info!("    {:4} sectors", sectors.len())

        Level {
            things: things,
            linedefs: linedefs,
            sidedefs: sidedefs,
            vertices: vertices,
            segs: segs,
            subsectors: subsectors,
            nodes: nodes,
            sectors: sectors,
        }
    }

    pub fn vertex(&self, id: VertexId) -> Vec2f {
        from_wad_coords(self.vertices[id as uint].x,
                        self.vertices[id as uint].y)
    }

    pub fn seg_linedef<'a>(&'a self, seg: &WadSeg) -> &'a WadLinedef {
        &self.linedefs[seg.linedef as uint]
    }

    pub fn seg_vertices(&self, seg: &WadSeg) -> (Vec2f, Vec2f) {
        (self.vertex(seg.start_vertex), self.vertex(seg.end_vertex))
    }

    pub fn left_sidedef<'a>(&'a self, linedef: &WadLinedef)
            -> &'a WadSidedef {
        &self.sidedefs[linedef.left_side as uint]
    }

    pub fn right_sidedef<'a>(&'a self, linedef: &WadLinedef)
            -> &'a WadSidedef {
        &self.sidedefs[linedef.right_side as uint]
    }

    pub fn sidedef_sector<'a>(&'a self, sidedef: &WadSidedef) -> &'a WadSector {
        &self.sectors[sidedef.sector as uint]
    }

    pub fn ssector_segs<'a>(&'a self, ssector: &WadSubsector) -> &'a [WadSeg] {
        self.segs.slice(ssector.first_seg as uint,
                        (ssector.first_seg as uint + ssector.num_segs as uint))
    }
}
<|endoftext|>


<commit_before>
use std::fmt;
use std::io::Read;  // Trait providing read_to_string()
use std::env;

use hyper;
use serde;
use serde_json;

use BuildQuery;
use Groups;

pub const API_VERSION: u16 = 3;

#[derive(Debug)]
pub struct Pagination {
    pub page: u16,
    pub per_page: u16,
}

pub struct GitLab {
    scheme: String,
    domain: String,
    port: u16,
    private_token: String,
    client: hyper::Client,
    pagination: Option<Pagination>,
}

// Explicitly implement Debug trait for GitLab so we can hide the token.
impl fmt::Debug for GitLab {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "GitLab {{ scheme: {}, domain: {}, port: {}, private_token: XXXXXXXXXXXXXXXXXXXX, \
                client: {:?}, pagination: {:?} }}",
               self.scheme,
               self.domain,
               self.port,
               self.client,
               self.pagination)
    }
}

impl GitLab {
    pub fn new(scheme: &str, domain: &str, port: u16, private_token: &str) -> GitLab {
        GitLab {
            scheme: scheme.to_string(),
            domain: domain.to_string(),
            port: port,
            private_token: private_token.to_string(),
            client: match env::var("HTTP_PROXY") {
                Ok(proxy) => {
                    let proxy: Vec<&str> = proxy.trim_left_matches("http://").split(':').collect();
                    let hostname = proxy[0].to_string();
                    let port = proxy[1];

                    hyper::Client::with_http_proxy(hostname, port.parse().unwrap())
                }
                Err(_) => hyper::Client::new(),
            },
            pagination: None,
        }
    }

    pub fn new_http(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("http", domain, 80, private_token)
    }

    pub fn new_https(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("https", domain, 443, private_token)
    }

    /// Build a URL used to access GitLab instance, including some parameters.
    ///
    /// # Examples
    ///
    /// Example from GitLab: https://docs.gitlab.com/ce/api/#basic-usage
    ///
    /// ```
    /// use gitlab_api::GitLab;
    ///
    /// let expected_url = "https://gitlab.example.com:\
    ///                     443/api/v3/groups?\
    ///                     order_by=path&private_token=XXXXXXXXXXXXX&page=1&per_page=20";
    ///
    /// let gl = GitLab::new_https("gitlab.example.com", "XXXXXXXXXXXXX");
    ///
    /// assert_eq!(gl.build_url("groups?order_by=path"), expected_url);
    /// ```
    pub fn build_url(&self, query: &str) -> String {
        let params_splitter = if query.find('?').is_some() { "&" } else { "?" };
        let mut url = format!("{}://{}:{}/api/v{}/{}{}private_token={}",
                self.scheme,
                self.domain,
                self.port,
                API_VERSION,
                query,
                params_splitter,
                self.private_token);

        self.pagination.as_ref().map(|pagination| {
            url.push_str(&format!("&page={}&per_page={}",
                pagination.page,
                pagination.per_page));
        });

        url
    }

    pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
        let url = self.build_url("version");
        // Close connections after each GET.
        self.client.get(&url).header(hyper::header::Connection::close()).send()
    }

    pub fn set_pagination(&mut self, pagination: Pagination) {
        self.pagination = Some(pagination);
    }

    pub fn get<T>(&self, query: &str) -> Result<T, serde_json::Error>
        where T: serde::Deserialize
    {
        let url = self.build_url(query);
        info!("url: {:?}", url);
        let mut res: hyper::client::Response = self.client
            .get(&url)
            .header(hyper::header::Connection::close())
            .send()
            .unwrap();

        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();
        debug!("body: {:?}", body);

        // FIXME: Properly handle the error. Will require defining our own errors...
        assert_eq!(res.status, hyper::status::StatusCode::Ok);

        serde_json::from_str(body.as_str())
    }

    // pub fn version(&self) -> Result<Version, serde_json::Error> {
    //     self.get("version")
    // }
    //
    // pub fn groups(&self) -> Result<Groups, serde_json::Error> {
    //     self.get("groups")
    // }
    //
    // pub fn projects(&self) -> Result<Projects, serde_json::Error> {
    //     self.get("projects")
    // }

    pub fn groups(&mut self, listing: ::groups::Listing) -> Result<Groups, serde_json::Error> {
        let query = listing.build_query();
        self.get(&query)
    }

    pub fn owned_groups(&mut self) -> Result<Groups, serde_json::Error> {
        let query = ::groups::owned_groups::Listing::new().build_query();
        info!("query: {:?}", query);
        self.get(&query)
    }
}

// #[cfg(test)]
// mod tests {
//
// #[test]
// fn it_works() {
// }
// }
//
<commit_msg>Fix docstring: remove pagination fro `build_url()`<commit_after>
use std::fmt;
use std::io::Read;  // Trait providing read_to_string()
use std::env;

use hyper;
use serde;
use serde_json;

use BuildQuery;
use Groups;

pub const API_VERSION: u16 = 3;

#[derive(Debug)]
pub struct Pagination {
    pub page: u16,
    pub per_page: u16,
}

pub struct GitLab {
    scheme: String,
    domain: String,
    port: u16,
    private_token: String,
    client: hyper::Client,
    pagination: Option<Pagination>,
}

// Explicitly implement Debug trait for GitLab so we can hide the token.
impl fmt::Debug for GitLab {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "GitLab {{ scheme: {}, domain: {}, port: {}, private_token: XXXXXXXXXXXXXXXXXXXX, \
                client: {:?}, pagination: {:?} }}",
               self.scheme,
               self.domain,
               self.port,
               self.client,
               self.pagination)
    }
}

impl GitLab {
    pub fn new(scheme: &str, domain: &str, port: u16, private_token: &str) -> GitLab {
        GitLab {
            scheme: scheme.to_string(),
            domain: domain.to_string(),
            port: port,
            private_token: private_token.to_string(),
            client: match env::var("HTTP_PROXY") {
                Ok(proxy) => {
                    let proxy: Vec<&str> = proxy.trim_left_matches("http://").split(':').collect();
                    let hostname = proxy[0].to_string();
                    let port = proxy[1];

                    hyper::Client::with_http_proxy(hostname, port.parse().unwrap())
                }
                Err(_) => hyper::Client::new(),
            },
            pagination: None,
        }
    }

    pub fn new_http(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("http", domain, 80, private_token)
    }

    pub fn new_https(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("https", domain, 443, private_token)
    }

    /// Build a URL used to access GitLab instance, including some parameters.
    ///
    /// # Examples
    ///
    /// Example from GitLab: https://docs.gitlab.com/ce/api/#basic-usage
    ///
    /// ```
    /// use gitlab_api::GitLab;
    ///
    /// let expected_url = "https://gitlab.example.com:\
    ///                     443/api/v3/groups?order_by=path&private_token=XXXXXXXXXXXXX";
    ///
    /// let gl = GitLab::new_https("gitlab.example.com", "XXXXXXXXXXXXX");
    ///
    /// assert_eq!(gl.build_url("groups?order_by=path"), expected_url);
    /// ```
    pub fn build_url(&self, query: &str) -> String {
        let params_splitter = if query.find('?').is_some() { "&" } else { "?" };
        let mut url = format!("{}://{}:{}/api/v{}/{}{}private_token={}",
                self.scheme,
                self.domain,
                self.port,
                API_VERSION,
                query,
                params_splitter,
                self.private_token);

        self.pagination.as_ref().map(|pagination| {
            url.push_str(&format!("&page={}&per_page={}",
                pagination.page,
                pagination.per_page));
        });

        url
    }

    pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
        let url = self.build_url("version");
        // Close connections after each GET.
        self.client.get(&url).header(hyper::header::Connection::close()).send()
    }

    pub fn set_pagination(&mut self, pagination: Pagination) {
        self.pagination = Some(pagination);
    }

    pub fn get<T>(&self, query: &str) -> Result<T, serde_json::Error>
        where T: serde::Deserialize
    {
        let url = self.build_url(query);
        info!("url: {:?}", url);
        let mut res: hyper::client::Response = self.client
            .get(&url)
            .header(hyper::header::Connection::close())
            .send()
            .unwrap();

        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();
        debug!("body: {:?}", body);

        // FIXME: Properly handle the error. Will require defining our own errors...
        assert_eq!(res.status, hyper::status::StatusCode::Ok);

        serde_json::from_str(body.as_str())
    }

    // pub fn version(&self) -> Result<Version, serde_json::Error> {
    //     self.get("version")
    // }
    //
    // pub fn groups(&self) -> Result<Groups, serde_json::Error> {
    //     self.get("groups")
    // }
    //
    // pub fn projects(&self) -> Result<Projects, serde_json::Error> {
    //     self.get("projects")
    // }

    pub fn groups(&mut self, listing: ::groups::Listing) -> Result<Groups, serde_json::Error> {
        let query = listing.build_query();
        self.get(&query)
    }

    pub fn owned_groups(&mut self) -> Result<Groups, serde_json::Error> {
        let query = ::groups::owned_groups::Listing::new().build_query();
        info!("query: {:?}", query);
        self.get(&query)
    }
}

// #[cfg(test)]
// mod tests {
//
// #[test]
// fn it_works() {
// }
// }
//
<|endoftext|>