<commit_before>#![deny(unused)]
#![cfg_attr(test, deny(warnings))]
#![recursion_limit="128"]

#[macro_use] extern crate failure;
#[macro_use] extern crate log;
#[macro_use] extern crate scoped_tls;
#[macro_use] extern crate serde_derive;
#[macro_use] extern crate serde_json;
extern crate atty;
extern crate crates_io as registry;
extern crate crossbeam;
extern crate curl;
extern crate docopt;
extern crate filetime;
extern crate flate2;
extern crate fs2;
extern crate git2;
extern crate glob;
extern crate hex;
extern crate home;
extern crate ignore;
extern crate jobserver;
extern crate lazycell;
extern crate libc;
extern crate libgit2_sys;
extern crate num_cpus;
extern crate same_file;
extern crate semver;
extern crate serde;
extern crate serde_ignored;
extern crate shell_escape;
extern crate tar;
extern crate tempdir;
extern crate termcolor;
extern crate toml;
extern crate url;
#[cfg(target_os = "macos")]
extern crate core_foundation;

use std::fmt;

use serde::Deserialize;
use serde::ser;
use docopt::Docopt;
use failure::Error;

use core::Shell;
use core::shell::Verbosity::Verbose;

pub use util::{CargoError, CargoResult, CliError, CliResult, Config};
pub use util::errors::Internal;

pub const CARGO_ENV: &'static str = "CARGO";

pub mod core;
pub mod ops;
pub mod sources;
pub mod util;

pub struct CommitInfo {
    pub short_commit_hash: String,
    pub commit_hash: String,
    pub commit_date: String,
}

pub struct CfgInfo {
    // Information about the git repository we may have been built from.
    pub commit_info: Option<CommitInfo>,
    // The release channel we were built for.
    pub release_channel: String,
}

pub struct VersionInfo {
    pub major: String,
    pub minor: String,
    pub patch: String,
    pub pre_release: Option<String>,
    // Information that's only available when we were built with
    // configure/make, rather than cargo itself.
    pub cfg_info: Option<CfgInfo>,
}

impl fmt::Display for VersionInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "cargo {}.{}.{}",
               self.major, self.minor, self.patch)?;
        if let Some(channel) = self.cfg_info.as_ref().map(|ci| &ci.release_channel) {
            if channel != "stable" {
                write!(f, "-{}", channel)?;
                let empty = String::from("");
                write!(f, "{}", self.pre_release.as_ref().unwrap_or(&empty))?;
            }
        };

        if let Some(ref cfg) = self.cfg_info {
            if let Some(ref ci) = cfg.commit_info {
                write!(f, " ({} {})",
                       ci.short_commit_hash, ci.commit_date)?;
            }
        };
        Ok(())
    }
}

pub fn call_main_without_stdin<'de, Flags: Deserialize<'de>>(
            exec: fn(Flags, &mut Config) -> CliResult,
            config: &mut Config,
            usage: &str,
            args: &[String],
            options_first: bool) -> CliResult
{
    let docopt = Docopt::new(usage).unwrap()
        .options_first(options_first)
        .argv(args.iter().map(|s| &s[..]))
        .help(true);

    let flags = docopt.deserialize().map_err(|e| {
        let code = if e.fatal() {1} else {0};
        CliError::new(e.into(), code)
    })?;

    exec(flags, config)
}

pub fn print_json<T: ser::Serialize>(obj: &T) {
    let encoded = serde_json::to_string(&obj).unwrap();
    println!("{}", encoded);
}

pub fn exit_with_error(err: CliError, shell: &mut Shell) -> ! {
    debug!("exit_with_error; err={:?}", err);

    let CliError { error, exit_code, unknown } = err;
    // exit_code == 0 is non-fatal error, e.g. docopt version info
    let fatal = exit_code != 0;

    let hide = unknown && shell.verbosity() != Verbose;

    if let Some(error) = error {
        if hide {
            drop(shell.error("An unknown error occurred"))
        } else if fatal {
            drop(shell.error(&error))
        } else {
            println!("{}", error);
        }

        if !handle_cause(&error, shell) || hide {
            drop(writeln!(shell.err(), "\nTo learn more, run the command again \
                                        with --verbose."));
        }
    }

    std::process::exit(exit_code)
}

pub fn handle_error(err: CargoError, shell: &mut Shell) {
    debug!("handle_error; err={:?}", &err);

    let _ignored_result = shell.error(&err);
    handle_cause(&err, shell);
}

fn handle_cause(cargo_err: &Error, shell: &mut Shell) -> bool {
    fn print(error: String, shell: &mut Shell) {
        drop(writeln!(shell.err(), "\nCaused by:"));
        drop(writeln!(shell.err(), "  {}", error));
    }

    let verbose = shell.verbosity();

    if verbose == Verbose {
        // The first error has already been printed to the shell
        // Print all remaining errors
        for err in cargo_err.causes().skip(1) {
            print(err.to_string(), shell);
        }
    } else {
        // The first error has already been printed to the shell
        // Print remaining errors until one marked as Internal appears
        for err in cargo_err.causes().skip(1) {
            if err.downcast_ref::<Internal>().is_some() {
                return false;
            }

            print(err.to_string(), shell);
        }
    }

    true
}

pub fn version() -> VersionInfo {
    macro_rules! env_str {
        ($name:expr) => { env!($name).to_string() }
    }
    macro_rules! option_env_str {
        ($name:expr) => { option_env!($name).map(|s| s.to_string()) }
    }
    match option_env!("CFG_RELEASE_CHANNEL") {
        // We have environment variables set up from configure/make.
        Some(_) => {
            let commit_info =
                option_env!("CFG_COMMIT_HASH").map(|s| {
                    CommitInfo {
                        commit_hash: s.to_string(),
                        short_commit_hash: option_env_str!("CFG_SHORT_COMMIT_HASH").unwrap(),
                        commit_date: option_env_str!("CFG_COMMIT_DATE").unwrap(),
                    }
                });
            VersionInfo {
                major: env_str!("CARGO_PKG_VERSION_MAJOR"),
                minor: env_str!("CARGO_PKG_VERSION_MINOR"),
                patch: env_str!("CARGO_PKG_VERSION_PATCH"),
                pre_release: option_env_str!("CARGO_PKG_VERSION_PRE"),
                cfg_info: Some(CfgInfo {
                    release_channel: option_env_str!("CFG_RELEASE_CHANNEL").unwrap(),
                    commit_info: commit_info,
                }),
            }
        },
        // We are being compiled by Cargo itself.
        None => {
            VersionInfo {
                major: env_str!("CARGO_PKG_VERSION_MAJOR"),
                minor: env_str!("CARGO_PKG_VERSION_MINOR"),
                patch: env_str!("CARGO_PKG_VERSION_PATCH"),
                pre_release: option_env_str!("CARGO_PKG_VERSION_PRE"),
                cfg_info: None,
            }
        }
    }
}
<commit_msg>Auto merge of #5039 - lawliet89:docopt-bounds, r=alexcrichton<commit_after>#![deny(unused)]
#![cfg_attr(test, deny(warnings))]
#![recursion_limit="128"]

#[macro_use] extern crate failure;
#[macro_use] extern crate log;
#[macro_use] extern crate scoped_tls;
#[macro_use] extern crate serde_derive;
#[macro_use] extern crate serde_json;
extern crate atty;
extern crate crates_io as registry;
extern crate crossbeam;
extern crate curl;
extern crate docopt;
extern crate filetime;
extern crate flate2;
extern crate fs2;
extern crate git2;
extern crate glob;
extern crate hex;
extern crate home;
extern crate ignore;
extern crate jobserver;
extern crate lazycell;
extern crate libc;
extern crate libgit2_sys;
extern crate num_cpus;
extern crate same_file;
extern crate semver;
extern crate serde;
extern crate serde_ignored;
extern crate shell_escape;
extern crate tar;
extern crate tempdir;
extern crate termcolor;
extern crate toml;
extern crate url;
#[cfg(target_os = "macos")]
extern crate core_foundation;

use std::fmt;

use serde::de::DeserializeOwned;
use serde::ser;
use docopt::Docopt;
use failure::Error;

use core::Shell;
use core::shell::Verbosity::Verbose;

pub use util::{CargoError, CargoResult, CliError, CliResult, Config};
pub use util::errors::Internal;

pub const CARGO_ENV: &'static str = "CARGO";

pub mod core;
pub mod ops;
pub mod sources;
pub mod util;

pub struct CommitInfo {
    pub short_commit_hash: String,
    pub commit_hash: String,
    pub commit_date: String,
}

pub struct CfgInfo {
    // Information about the git repository we may have been built from.
    pub commit_info: Option<CommitInfo>,
    // The release channel we were built for.
    pub release_channel: String,
}

pub struct VersionInfo {
    pub major: String,
    pub minor: String,
    pub patch: String,
    pub pre_release: Option<String>,
    // Information that's only available when we were built with
    // configure/make, rather than cargo itself.
    pub cfg_info: Option<CfgInfo>,
}

impl fmt::Display for VersionInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "cargo {}.{}.{}",
               self.major, self.minor, self.patch)?;
        if let Some(channel) = self.cfg_info.as_ref().map(|ci| &ci.release_channel) {
            if channel != "stable" {
                write!(f, "-{}", channel)?;
                let empty = String::from("");
                write!(f, "{}", self.pre_release.as_ref().unwrap_or(&empty))?;
            }
        };

        if let Some(ref cfg) = self.cfg_info {
            if let Some(ref ci) = cfg.commit_info {
                write!(f, " ({} {})",
                       ci.short_commit_hash, ci.commit_date)?;
            }
        };
        Ok(())
    }
}

pub fn call_main_without_stdin<Flags: DeserializeOwned>(
            exec: fn(Flags, &mut Config) -> CliResult,
            config: &mut Config,
            usage: &str,
            args: &[String],
            options_first: bool) -> CliResult
{
    let docopt = Docopt::new(usage).unwrap()
        .options_first(options_first)
        .argv(args.iter().map(|s| &s[..]))
        .help(true);

    let flags = docopt.deserialize().map_err(|e| {
        let code = if e.fatal() {1} else {0};
        CliError::new(e.into(), code)
    })?;

    exec(flags, config)
}

pub fn print_json<T: ser::Serialize>(obj: &T) {
    let encoded = serde_json::to_string(&obj).unwrap();
    println!("{}", encoded);
}

pub fn exit_with_error(err: CliError, shell: &mut Shell) -> ! {
    debug!("exit_with_error; err={:?}", err);

    let CliError { error, exit_code, unknown } = err;
    // exit_code == 0 is non-fatal error, e.g. docopt version info
    let fatal = exit_code != 0;

    let hide = unknown && shell.verbosity() != Verbose;

    if let Some(error) = error {
        if hide {
            drop(shell.error("An unknown error occurred"))
        } else if fatal {
            drop(shell.error(&error))
        } else {
            println!("{}", error);
        }

        if !handle_cause(&error, shell) || hide {
            drop(writeln!(shell.err(), "\nTo learn more, run the command again \
                                        with --verbose."));
        }
    }

    std::process::exit(exit_code)
}

pub fn handle_error(err: CargoError, shell: &mut Shell) {
    debug!("handle_error; err={:?}", &err);

    let _ignored_result = shell.error(&err);
    handle_cause(&err, shell);
}

fn handle_cause(cargo_err: &Error, shell: &mut Shell) -> bool {
    fn print(error: String, shell: &mut Shell) {
        drop(writeln!(shell.err(), "\nCaused by:"));
        drop(writeln!(shell.err(), "  {}", error));
    }

    let verbose = shell.verbosity();

    if verbose == Verbose {
        // The first error has already been printed to the shell
        // Print all remaining errors
        for err in cargo_err.causes().skip(1) {
            print(err.to_string(), shell);
        }
    } else {
        // The first error has already been printed to the shell
        // Print remaining errors until one marked as Internal appears
        for err in cargo_err.causes().skip(1) {
            if err.downcast_ref::<Internal>().is_some() {
                return false;
            }

            print(err.to_string(), shell);
        }
    }

    true
}

pub fn version() -> VersionInfo {
    macro_rules! env_str {
        ($name:expr) => { env!($name).to_string() }
    }
    macro_rules! option_env_str {
        ($name:expr) => { option_env!($name).map(|s| s.to_string()) }
    }
    match option_env!("CFG_RELEASE_CHANNEL") {
        // We have environment variables set up from configure/make.
        Some(_) => {
            let commit_info =
                option_env!("CFG_COMMIT_HASH").map(|s| {
                    CommitInfo {
                        commit_hash: s.to_string(),
                        short_commit_hash: option_env_str!("CFG_SHORT_COMMIT_HASH").unwrap(),
                        commit_date: option_env_str!("CFG_COMMIT_DATE").unwrap(),
                    }
                });
            VersionInfo {
                major: env_str!("CARGO_PKG_VERSION_MAJOR"),
                minor: env_str!("CARGO_PKG_VERSION_MINOR"),
                patch: env_str!("CARGO_PKG_VERSION_PATCH"),
                pre_release: option_env_str!("CARGO_PKG_VERSION_PRE"),
                cfg_info: Some(CfgInfo {
                    release_channel: option_env_str!("CFG_RELEASE_CHANNEL").unwrap(),
                    commit_info: commit_info,
                }),
            }
        },
        // We are being compiled by Cargo itself.
        None => {
            VersionInfo {
                major: env_str!("CARGO_PKG_VERSION_MAJOR"),
                minor: env_str!("CARGO_PKG_VERSION_MINOR"),
                patch: env_str!("CARGO_PKG_VERSION_PATCH"),
                pre_release: option_env_str!("CARGO_PKG_VERSION_PRE"),
                cfg_info: None,
            }
        }
    }
}
<|endoftext|>


<commit_before>#![crate_id="CSVProvider#0.1-pre"]
#![crate_type="dylib"]

// why doesn't "lib" work?
/*
CSVSample.rs:38:4: 38:21 error: macro undefined: 'ProvideCSV_labels'
CSVSample.rs:38    ProvideCSV_labels!("MyCSV", "./sample1.txt", "Verse");
                   ^~~~~~~~~~~~~~~~~
error: aborting due to previous error
 */

// rustc 0.11.0-pre (732e057 2014-06-06 01:21:54 -0700)
// host: x86_64-apple-darwin

#![feature(globs, macro_registrar, macro_rules, quote, managed_boxes)]

extern crate syntax;

use syntax::ast::{Name,
                  TokenTree,
                  Expr,
                  ExprLit,
                  ExprVec,
                  LitStr,
                  DUMMY_NODE_ID,
                  MutImmutable};
use syntax::codemap::Span;
use syntax::ext::base::{SyntaxExtension,
                        ExtCtxt,
                        MacResult,
                        DummyResult,
                        MacExpr,
                        NormalTT,
                        BasicMacroExpander};
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{InternedString, COMMA, EOF};

use std::io;
use std::io::fs;
use std::os;
use std::str;
use std::io::Process;

#[macro_registrar]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
    register(token::intern("ProvideCSV_labels"),
             NormalTT(box BasicMacroExpander {
                 expander: provide_labels,
                 span: None,
             },
             None));
}

#[deriving(Clone)]
struct Entry {
    str: InternedString,
    expr: @Expr
}

// see https://github.com/sfackler/syntax-ext-talk/blob/gh-pages/simple-ext/lib.rs
fn parse_entries(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
    let mut parser = parse::new_parser_from_tts(
         cx.parse_sess(),
         cx.cfg(),
         tts.iter()
            .map(|x| (*x).clone())
            .collect()
    );

    let mut entries: Vec<Entry> = Vec::new();

    let mut error = false;
    while parser.token != EOF {
        let entry = parser.parse_expr();

        let entry_str = match entry.node {
            ExprLit(lit) => {
                match lit.node {
                    LitStr(ref s, _) => s.clone(),
                    _ => {
                        cx.span_err(entry.span, "expected string literal");
                        error = true;
                        InternedString::new("")
                    }
                }
            }
            _ => {
                cx.span_err(entry.span, "expected string literal");
                error = true;
                InternedString::new("")
            }
        };

        entries.push(Entry { str: entry_str, expr: entry });

        if !parser.eat(&COMMA) && parser.token != EOF {
            cx.span_err(parser.span, "expected `,`");
            return None;
        }
    }

    if error {
        return None;
    }

    Some(entries)
}

// FIXME: right now we only look at one column (CSV, haha)

fn provide_labels(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> {
   let mut entries = match parse_entries(cx, tts) {
      Some(entries) => entries,
      None => return DummyResult::expr(sp),
   };

/*
   println!("provide_labels: args: {}", entries);

   let name   = entries[0];
   let path   = entries[1];
   let labels = entries[2];

   println!("provide_labels: name:   {}", name);
   println!("provide_labels: path:   {}", path);
   println!("provide_labels: labels: {}", labels);
*/

   return DummyResult::expr(sp);
}

<commit_msg>logging arguments given<commit_after>#![crate_id="CSVProvider#0.1-pre"]
#![crate_type="dylib"]

// why doesn't "lib" work?
/*
CSVSample.rs:38:4: 38:21 error: macro undefined: 'ProvideCSV_labels'
CSVSample.rs:38    ProvideCSV_labels!("MyCSV", "./sample1.txt", "Verse");
                   ^~~~~~~~~~~~~~~~~
error: aborting due to previous error
 */

// rustc 0.11.0-pre (732e057 2014-06-06 01:21:54 -0700)
// host: x86_64-apple-darwin

#![feature(globs, macro_registrar, macro_rules, quote, managed_boxes)]

extern crate syntax;

use syntax::ast::{Name,
                  TokenTree,
                  Expr,
                  ExprLit,
                  ExprVec,
                  LitStr,
                  DUMMY_NODE_ID,
                  MutImmutable};
use syntax::codemap::Span;
use syntax::ext::base::{SyntaxExtension,
                        ExtCtxt,
                        MacResult,
                        DummyResult,
                        MacExpr,
                        NormalTT,
                        BasicMacroExpander};
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{InternedString, COMMA, EOF};

use std::io;
use std::io::fs;
use std::os;
use std::str;
use std::io::Process;

#[macro_registrar]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
    register(token::intern("ProvideCSV_labels"),
             NormalTT(box BasicMacroExpander {
                 expander: provide_csv_given_labels,
                 span: None,
             },
             None));
}

#[deriving(Clone)]
struct Entry {
    str: InternedString,
    expr: @Expr
}

// see https://github.com/sfackler/syntax-ext-talk/blob/gh-pages/simple-ext/lib.rs
fn parse_entries(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
    let mut parser = parse::new_parser_from_tts(
         cx.parse_sess(),
         cx.cfg(),
         tts.iter()
            .map(|x| (*x).clone())
            .collect()
    );

    let mut entries: Vec<Entry> = Vec::new();

    let mut error = false;
    while parser.token != EOF {
        let entry = parser.parse_expr();

        let entry_str = match entry.node {
            ExprLit(lit) => {
                match lit.node {
                    LitStr(ref s, _) => s.clone(),
                    _ => {
                        cx.span_err(entry.span, "expected string literal");
                        error = true;
                        InternedString::new("")
                    }
                }
            }
            _ => {
                cx.span_err(entry.span, "expected string literal");
                error = true;
                InternedString::new("")
            }
        };

        entries.push(Entry { str: entry_str, expr: entry });

        if !parser.eat(&COMMA) && parser.token != EOF {
            cx.span_err(parser.span, "expected `,`");
            return None;
        }
    }

    if error {
        return None;
    }

    Some(entries)
}

// FIXME: right now we only look at one column (CSV, haha)

fn provide_csv_given_labels(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> {
   let mut entries = match parse_entries(cx, tts) {
      Some(entries) => entries,
      None => return DummyResult::expr(sp),
   };

   //println!("provide_labels: args: {}", entries);

   let name   = entries.pop().expect("should be given a type name");
   let path   = entries.pop().expect("should be given a CSV file path");
   let labels = entries.pop().expect("should be given column labels");

   println!("provide_csv_given_labels: name:   {}", name.str);
   println!("provide_csv_given_labels: path:   {}", path.str);
   println!("provide_csv_given_labels: labels: {}", labels.str);

   return DummyResult::expr(sp);
}

<|endoftext|>


<commit_before>#![crate_id="CSVProvider#0.1-pre"]
#![crate_type="dylib"]

// why doesn't "lib" work?

// rustc 0.11.0-pre (b47f2226a25654c5b781d27a91f2fa5274b3a347 2014-06-28 14:31:37 +0000)

#![feature(globs, macro_rules, quote, managed_boxes, plugin_registrar)]

#![allow(unused_imports)]
//#![allow(unused_variable)]
#![allow(uppercase_variables)]
#![allow(unnecessary_parens)]

extern crate syntax;
extern crate rustc;

use syntax::ast::{
   Name,
   TokenTree,
   Expr,
   ExprLit,
   ExprVec,
   LitStr,
   Ident,
   DUMMY_NODE_ID,
   MutImmutable
};
use syntax::codemap::Span;
use syntax::ext::base::{
   SyntaxExtension,
   BasicMacroExpander,
   ExtCtxt,
   MacResult,
   MacPat,
   MacItem,
   DummyResult,
   MacExpr,
   NormalTT
};
use std::gc::Gc;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{InternedString, COMMA, EOF};

use std::io;
use std::io::fs;
use std::os;
use std::str;
use std::io::Process;
use syntax::ast;
use syntax::ast::Name;

use syntax::util::small_vector::SmallVector;

use std::collections::HashMap;
use std::gc::{Gc, GC};

use rustc::plugin::Registry;

#[plugin_registrar]
pub fn macro_registrar(reg: &mut Registry) {
   reg.register_macro("ProvideCSV_labels", provide_csv_given_labels);
}

#[deriving(Clone)]
struct Entry {
   str: InternedString,
   expr: Gc<Expr>
}

// see https://github.com/sfackler/syntax-ext-talk/blob/gh-pages/simple-ext/lib.rs
fn parse_entries(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
   let mut parser = parse::new_parser_from_tts(
       cx.parse_sess(),
       cx.cfg(),
       tts.iter()
          .map(|x| (*x).clone())
          .collect()
   );

   let mut entries: Vec<Entry> = Vec::new();

   let mut error = false;
   while parser.token != EOF {
      let entry = parser.parse_expr();

      let entry_str = match entry.node {
         ExprLit(lit) => {
            match lit.node {
               LitStr(ref s, _) => s.clone(),
               _ => {
                  cx.span_err(entry.span, "expected string literal (1)");
                  error = true;
                  InternedString::new("")
               }
            }
         }
         _ => {
                cx.span_err(entry.span, "expected string literal (2)");
                error = true;
                InternedString::new("")
         }
      };

      entries.push(Entry { str: entry_str, expr: entry });

      if !parser.eat(&COMMA) && parser.token != EOF {
         cx.span_err(parser.span, "expected `,`");
         return None;
      }
    }

   if error {
      return None;
   }

   Some(entries)
}

pub struct MacItems {
   items: Vec<Gc<ast::Item>>
}
impl MacItems {
   pub fn new(items: Vec<Gc<ast::Item>>) -> Box<MacResult> {
      box MacItems { items: items } as Box<MacResult>
   }
}
impl MacResult for MacItems {
   fn make_items(&self) -> Option<SmallVector<Gc<ast::Item>>> {
      Some(SmallVector::many(self.items.clone()))
   }
}

fn parse_csv_row<'a>(raw: &'a str, expected_columns: Option<uint>) -> str::StrSplits<'a> {
   let mut it = raw.split_str(",");

   // take a pulse
   let _whatever = it.map(|xx| {
      println!("iterating over row elements: {}", xx.to_string());
      return xx;
   });

   match expected_columns {
      Some(count) => {
         if (count == it.count()) {
            println!("parse_csv_row: matching length expected");
         } else {
            println!("parse_csv_row: wrong length!");
         }
      },
      None => (),
   }

   return it;
}

fn provide_csv_given_labels(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> {
   let mut entries = match parse_entries(cx, tts) {
      Some(entries) => entries,
      None => return DummyResult::expr(sp),
   };

   let raw_name   : Entry = entries.shift().expect("should be given a type name");
   let raw_path   : Entry = entries.shift().expect("should be given a CSV file path");
   let raw_labels : Entry = entries.shift().expect("should be given column labels");

   let name   : InternedString = raw_name.str;
   let path   : InternedString = raw_path.str;
   let labels : InternedString = raw_labels.str;

   println!("provide_csv_given_labels: name:   {}", name);
   println!("provide_csv_given_labels: path:   {}", path);
   println!("provide_csv_given_labels: labels: {}", labels);

   // PENDING
   // read the CSV and try to get the type of data in it
   // create a type with
   //  * this name,
   //  * these labeled columns,
   //  * discovered type of data
   //  * a constructor which reads the whole file

   fn interned_to_ident(xx: InternedString) -> Ident {
      let yy: String = format!("{}", xx);
      let zz: &str = yy.as_slice();
      return token::str_to_ident(zz);
   }

   fn interned_to_ident_with_suffix(xx: InternedString, suff: &str) -> Ident {
      let yy: String = format!("{}{}", xx, suff);
      let zz: &str = yy.as_slice();
      return token::str_to_ident(zz);
   }

   let MyCsv    = interned_to_ident(name.clone());
   let MyCsvRow = interned_to_ident_with_suffix(name, "Row");
   let col0 = interned_to_ident(labels); // FIXME: parse this field

   println!("Defining items in the CSV provider...");

   // FIXME: Why is this fn necessary?
   let define_my_csv_row = |cx0 : &mut ExtCtxt| {
      return quote_item!(cx0,
         pub struct $MyCsvRow {
            pub $col0: String,
         }
      );
   };

   let define_my_csv = |cx0 : &mut ExtCtxt| {
      let item1: Option<Gc<syntax::ast::Item>> = quote_item!(cx0,
         pub struct $MyCsv {
            pub data: Vec<$MyCsvRow>,
         }
      );
      return item1;
   };

   let item0: Option<Gc<syntax::ast::Item>> = define_my_csv_row(cx);
   let item1: Option<Gc<syntax::ast::Item>> = define_my_csv(cx);
   let item2: Option<Gc<syntax::ast::Item>> = quote_item!(cx,
      impl $MyCsv {
         pub fn new() -> $MyCsv {
            println!("HMMMMMM.");
            return $MyCsv {
               data: (vec![
                  $MyCsvRow { $col0: "zero".to_string() },
                  $MyCsvRow { $col0:  "one".to_string() },
                  $MyCsvRow { $col0:  "two".to_string() },
               ]),
            };
         }
      }
   );

   let items = vec![item0.expect("Should be able to make struct MyCsvRow"),
                    item1.expect("Should be able to make struct MyCsv."),
                    item2.expect("Should be able to implement MyCsv.")];

   println!("OK");

   return MacItems::new(items);
}

<commit_msg>another<commit_after>#![crate_id="CSVProvider#0.1-pre"]
#![crate_type="dylib"]

// why doesn't "lib" work?

// rustc 0.11.0-pre (b47f2226a25654c5b781d27a91f2fa5274b3a347 2014-06-28 14:31:37 +0000)

#![feature(globs, macro_rules, quote, managed_boxes, plugin_registrar)]

#![allow(unused_imports)]
//#![allow(unused_variable)]
#![allow(uppercase_variables)]
#![allow(unnecessary_parens)]

extern crate syntax;
extern crate rustc;

use syntax::ast::{
   Name,
   TokenTree,
   Expr,
   ExprLit,
   ExprVec,
   LitStr,
   Ident,
   DUMMY_NODE_ID,
   MutImmutable
};
use syntax::codemap::Span;
use syntax::ext::base::{
   SyntaxExtension,
   BasicMacroExpander,
   ExtCtxt,
   MacResult,
   MacPat,
   MacItem,
   DummyResult,
   MacExpr,
   NormalTT
};
use std::gc::Gc;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{InternedString, COMMA, EOF};

use std::io;
use std::io::fs;
use std::os;
use std::str;
use std::io::Process;
use syntax::ast;
use syntax::ast::Name;

use syntax::util::small_vector::SmallVector;

use std::collections::HashMap;
use std::gc::{Gc, GC};

use rustc::plugin::Registry;

#[plugin_registrar]
pub fn macro_registrar(reg: &mut Registry) {
   reg.register_macro("ProvideCSV_labels", provide_csv_given_labels);
}

#[deriving(Clone)]
struct Entry {
   str: InternedString,
   expr: Gc<Expr>
}

// see https://github.com/sfackler/syntax-ext-talk/blob/gh-pages/simple-ext/lib.rs
fn parse_entries(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
   let mut parser = parse::new_parser_from_tts(
       cx.parse_sess(),
       cx.cfg(),
       tts.iter()
          .map(|x| (*x).clone())
          .collect()
   );

   let mut entries: Vec<Entry> = Vec::new();

   let mut error = false;
   while parser.token != EOF {
      let entry = parser.parse_expr();

      let entry_str = match entry.node {
         ExprLit(lit) => {
            match lit.node {
               LitStr(ref s, _) => s.clone(),
               _ => {
                  cx.span_err(entry.span, "expected string literal (1)");
                  error = true;
                  InternedString::new("")
               }
            }
         }
         _ => {
                cx.span_err(entry.span, "expected string literal (2)");
                error = true;
                InternedString::new("")
         }
      };

      entries.push(Entry { str: entry_str, expr: entry });

      if !parser.eat(&COMMA) && parser.token != EOF {
         cx.span_err(parser.span, "expected `,`");
         return None;
      }
    }

   if error {
      return None;
   }

   Some(entries)
}

pub struct MacItems {
   items: Vec<Gc<ast::Item>>
}
impl MacItems {
   pub fn new(items: Vec<Gc<ast::Item>>) -> Box<MacResult> {
      box MacItems { items: items } as Box<MacResult>
   }
}
impl MacResult for MacItems {
   fn make_items(&self) -> Option<SmallVector<Gc<ast::Item>>> {
      Some(SmallVector::many(self.items.clone()))
   }
}

fn parse_csv_row<'a>(raw: &'a str, expected_columns: Option<uint>) -> str::StrSplits<'a> {
   let mut it = raw.split_str(",");

   // take a pulse
   let _whatever = it.map(|xx| {
      println!("iterating over row elements: {}", xx.to_string());
      return xx;
   });

   match expected_columns {
      Some(count) => {
         if (count == it.count()) {
            println!("parse_csv_row: matching length expected");
         } else {
            println!("parse_csv_row: wrong length!");
         }
      },
      None => (),
   }

   return it;
}

fn provide_csv_given_labels(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> {
   let mut entries = match parse_entries(cx, tts) {
      Some(entries) => entries,
      None => return DummyResult::expr(sp),
   };

   let raw_name   : Entry = entries.shift().expect("should be given a type name");
   let raw_path   : Entry = entries.shift().expect("should be given a CSV file path");
   let raw_labels : Entry = entries.shift().expect("should be given column labels");

   let name   : InternedString = raw_name.str;
   let path   : InternedString = raw_path.str;
   let labels : InternedString = raw_labels.str;

   println!("provide_csv_given_labels: name:   {}", name);
   println!("provide_csv_given_labels: path:   {}", path);
   println!("provide_csv_given_labels: labels: {}", labels);

   // PENDING
   // read the CSV and try to get the type of data in it
   // create a type with
   //  * this name,
   //  * these labeled columns,
   //  * discovered type of data
   //  * a constructor which reads the whole file

   fn interned_to_ident(xx: InternedString) -> Ident {
      let yy: String = format!("{}", xx);
      let zz: &str = yy.as_slice();
      return token::str_to_ident(zz);
   }

   fn interned_to_ident_with_suffix(xx: InternedString, suff: &str) -> Ident {
      let yy: String = format!("{}{}", xx, suff);
      let zz: &str = yy.as_slice();
      return token::str_to_ident(zz);
   }

   let MyCsv    = interned_to_ident(name.clone());
   let MyCsvRow = interned_to_ident_with_suffix(name, "Row");
   let col0     = interned_to_ident(labels.clone()); // FIXME: parse this field

   let temp = format!("{}", labels);
   let labels_parsed = parse_csv_row(temp.as_slice(), None);

   println!("Defining items in the CSV provider...");

   // FIXME: Why is this fn necessary?
   let define_my_csv_row = |cx0 : &mut ExtCtxt| {
      return quote_item!(cx0,
         pub struct $MyCsvRow {
            pub $col0: String,
         }
      );
   };

   let define_my_csv = |cx0 : &mut ExtCtxt| {
      let item1: Option<Gc<syntax::ast::Item>> = quote_item!(cx0,
         pub struct $MyCsv {
            pub data: Vec<$MyCsvRow>,
         }
      );
      return item1;
   };

   let item0: Option<Gc<syntax::ast::Item>> = define_my_csv_row(cx);
   let item1: Option<Gc<syntax::ast::Item>> = define_my_csv(cx);
   let item2: Option<Gc<syntax::ast::Item>> = quote_item!(cx,
      impl $MyCsv {
         pub fn new() -> $MyCsv {
            println!("HMMMMMM.");
            return $MyCsv {
               data: (vec![
                  $MyCsvRow { $col0: "zero".to_string() },
                  $MyCsvRow { $col0:  "one".to_string() },
                  $MyCsvRow { $col0:  "two".to_string() },
               ]),
            };
         }
      }
   );

   let items = vec![item0.expect("Should be able to make struct MyCsvRow"),
                    item1.expect("Should be able to make struct MyCsv."),
                    item2.expect("Should be able to implement MyCsv.")];

   println!("OK");

   return MacItems::new(items);
}

<|endoftext|>


<commit_before>// v2 tests which must be compatible with v3 tests
mod v2;

// v3 tests are generated from v2 tests by copy&replace
#[cfg(proto3)]
mod v3;
<commit_msg>Restore proto3 tests which were accidentally disabled<commit_after>// v2 tests which must be compatible with v3 tests
mod v2;

// v3 tests are generated from v2 tests by copy&replace
mod v3;
<|endoftext|>


<commit_before>///! Encapsulates the implementation of the syscall collectors.

use libc;
use num_cpus;
use chrono::{NaiveDateTime, DateTime, UTC, TimeZone};
use nix::Error as NixError;
use nix::errno;
use nix::fcntl::{open, O_RDWR, O_SYNC};
use nix::unistd::close;
use nix::sys::stat::S_IRUSR;
use nix::sys::mman::{mmap, munmap};
use nix::sys::mman::{MAP_SHARED, PROT_READ, PROT_WRITE};
use std::ptr;
use std::mem::size_of;

use syscall::{Syscall, SyscallInfo};
use syscall::syscall_table::SyscallTable;
use error::{Error, Result};

const RING_BUF_SIZE: usize = 8 * 1024 * 1024;
const BUFFER_EMPTY_WAIT_TIME_MS: u32 = 30;
const MAX_N_CONSECUTIVE_WAITS: usize = 4;

const PPM_IOCTL_MAGIC: u8 = 's' as u8;
const PPM_IOCTL_DISABLE_CAPTURE: u8 = 1;
const PPM_IOCTL_ENABLE_CAPTURE: u8 = 1;

ioctl!(none ioctl_start with PPM_IOCTL_MAGIC, PPM_IOCTL_ENABLE_CAPTURE);
ioctl!(none ioctl_stop with PPM_IOCTL_MAGIC, PPM_IOCTL_DISABLE_CAPTURE);

#[repr(C)]
struct RingBufferInfo {
    head: u32,
    tail: u32,
    num_syscalls: u64,
    num_drops_buffer: u64,
    num_drops_pf: u64,
    num_preemptions: u64,
    num_context_switches: u64
}

struct RingBufferDev {
    fd: i32,
    buffer: *mut libc::c_char,
    buffer_info: *mut RingBufferInfo,
    last_readsize: u32,
    next_syscall: *mut libc::c_char,
    len: u32
}

pub trait Collector {
    fn start(&mut self) -> Result<usize>;

    fn stop(&mut self) -> Result<()>;

    fn next(&mut self) -> Option<SyscallInfo>;
}

pub struct RingBufferCollector {
    devs: Vec<RingBufferDev>,
    consecutive_waits: usize,
    syscall_table: SyscallTable
}

/// The implementation of the syscall collector based on the
/// Sysdig's rock solid kernel driver. The driver uses circular / ring
/// buffer data structure to publish the intercepted system calls from the
/// kernel space. This aims to provide extremely high throughput and efficiency.
impl Collector for RingBufferCollector {
    /// Opens the available ring buffer devices and maps the corresponding
    /// buffer content and the buffer information to a memory region
    /// of the calling thread.
    ///
    /// If the previous operations are done successfully, it sends the
    /// IO code to the underlying device to start the capture from the
    /// kernel driver.
    ///
    /// Returns `Result::Ok(i)` where `i` is the number of devices (CPUs) detected on the
    /// system or `Result::Err(e)` where `e` is the payload with a string containing the detailed
    /// error message.
    ///
    /// # Example
    ///
    /// ```
    /// let collector = RingBufferCollector::new();
    /// match collector.start() {
    ///     Ok(num_devs) => {
    ///
    ///     },
    ///     Err(e) => {
    ///     }
    /// }
    /// ```
    ///
    fn start(&mut self) -> Result<usize> {
        let num_devs = num_cpus::get();
        let len = RING_BUF_SIZE * 2;

        // open and map each of the char devices
        // which represent the ring buffers
        for i in 0..num_devs {
            match open(format!("/dev/sysdig{}", i).as_str(), O_RDWR | O_SYNC, S_IRUSR) {
                Ok(fd) => {
                    let buffer = mmap(ptr::null_mut(),
                                      len,
                                      PROT_READ,
                                      MAP_SHARED, fd, 0);
                    let buffer_info = mmap(ptr::null_mut(), size_of::<RingBufferInfo>(),
                                           PROT_READ | PROT_WRITE,
                                           MAP_SHARED, fd, 0);
                    if buffer.is_err() || buffer_info.is_err() {
                        munmap(buffer.unwrap() as *mut libc::c_void, len);
                        close(fd);
                        return Err(Error::RingBufferMapping);
                    }
                    let dev = RingBufferDev {
                        fd: fd,
                        buffer: buffer.unwrap() as *mut libc::c_char,
                        buffer_info: buffer_info.unwrap() as *mut RingBufferInfo,
                        last_readsize: 0,
                        next_syscall: ptr::null_mut(),
                        len: 0
                    };

                    self.devs.push(dev);
                    // ~ send the ioctl code to start the capture
                    unsafe { ioctl_start(fd); }

                },
                Err(e) => {
                    match e {
                        NixError::Sys(err) => {
                            if err == errno::ENODEV {
                                continue;
                            } else if err == errno::EBUSY {
                                return Err(Error::TooManyCollectors);
                            } else {
                                return Err(Error::DeviceError);
                            }
                        },
                        NixError::InvalidPath => {}
                    }
                }
            }
        }
        Ok(num_devs)
    }

    fn stop(&mut self) -> Result<()> {
        let len = RING_BUF_SIZE * 2;
        for dev in &self.devs {
            unsafe { ioctl_stop(dev.fd); }
            munmap(dev.buffer as *mut libc::c_void, len);
            munmap(dev.buffer_info as *mut libc::c_void, size_of::<RingBufferInfo>());
            close(dev.fd);
        }
        Ok(())
    }

    /// Consumes the next available syscall event from the ring buffer. If the buffers
    /// are empty, the head and tail are updated accordingly to pick up the
    /// new generated syscall events. When `consecutive_waits` is above the defined threshold, the
    /// main thread is suspended for `BUFFER_EMPTY_WAIT_TIME_MS` milliseconds. This can give a
    /// chance for buffers to refill.
    ///
    /// Returns `Some(syscall)` where `syscall` stores a plethora of information about the
    /// intercepted syscall event. On error it returns `None`.
    ///
    /// # Example
    ///
    /// ```
    /// let collector = RingBufferCollector::new();
    /// loop {
    ///     match collector.next() {
    ///         Some(syscall_info) => {
    ///             println!("syscall name {}", syscall_info.name);
    ///         },
    ///         None => {}
    ///     }
    /// }
    /// ```
    ///
    fn next(&mut self) -> Option<SyscallInfo> {
        let mut cpu = None;
        let mut syscall = ptr::null_mut();

        for (j, dev) in self.devs.iter().enumerate() {
            if dev.len == 0 {
                continue;
            } else {
                syscall = dev.next_syscall as *mut Syscall;
                cpu = Some(j);
            }
        }
        if cpu != None {
            let cpuid = cpu.unwrap();
            unsafe {
                assert!(self.devs[cpuid].len >= (*syscall).len);
                let len = (*syscall).len as isize;
                self.devs[cpuid].len -= (*syscall).len;
                self.devs[cpuid].next_syscall = self.devs[cpuid].next_syscall.offset(len);
            };
        } else {
            // ~ in case buffers are empty
            if self.check_next_wait() {
                unsafe { libc::usleep(BUFFER_EMPTY_WAIT_TIME_MS * 1000); }
                self.consecutive_waits += 1;
            }

            for mut dev in &mut self.devs {
                let buffer_info = dev.buffer_info;
                let ttail: usize = unsafe { ((*buffer_info).tail + dev.last_readsize) as usize };
                if ttail < RING_BUF_SIZE {
                    unsafe { (*dev.buffer_info).tail = ttail as u32 }
                } else {
                    unsafe { (*dev.buffer_info).tail = (ttail - RING_BUF_SIZE) as u32 }
                }

                let read_size = Self::get_buffer_readsize(buffer_info);
                dev.last_readsize = read_size;
                dev.len = read_size;

                unsafe { dev.next_syscall = dev.buffer.offset(ttail as isize); }
            }
        }

        if syscall.is_null() {
            None
        } else {
            let id = unsafe { (*syscall).id };
            let ts = unsafe { (*syscall).ts };

            match self.syscall_table.get_syscall_meta(id as usize) {
                Some(meta) => {
                    let timestamp = NaiveDateTime::from_timestamp((ts / 1000000000) as i64, 0);
                    let syscall_info = SyscallInfo {
                        ts: DateTime::<UTC>::from_utc(timestamp, UTC),
                        name: meta.name.to_string(),
                        params: meta.build_params(syscall),
                    };
                    return Some(syscall_info);

                },
                None => { return None; }
            }
        }
    }
}

impl RingBufferCollector {

    pub fn new() -> RingBufferCollector {
        RingBufferCollector {
            devs: Vec::<RingBufferDev>::new(),
            consecutive_waits: 0,
            syscall_table: SyscallTable::default()
        }
    }

    /// Determines if we should wait while the ring buffers
    /// are refilled with new system call events. Returns `true` if the
    /// condition is met, or `false` otherwise.
    fn check_next_wait(&mut self) -> bool {
        let mut res = true;
        for dev in &self.devs {
            let read_size = Self::get_buffer_readsize(dev.buffer_info);

            if read_size > 20000 {
                self.consecutive_waits = 0;
                res = false;
            }
        }
        if res == false {
            return false
        }

        if self.consecutive_waits >= MAX_N_CONSECUTIVE_WAITS {
            self.consecutive_waits = 0;
            return false;
        } else {
            return true;
        }
    }

    /// Calculates the new size of the buffer to be read.
    fn get_buffer_readsize(buffer_info: *mut RingBufferInfo) -> u32 {
        let head: usize = unsafe { (*buffer_info).head as usize };
        let tail: usize = unsafe { (*buffer_info).tail as usize };

        (if tail > head { RING_BUF_SIZE - tail + head } else { head - tail }) as u32

    }
}
<commit_msg>minor refactoring<commit_after>///! Encapsulates the implementation of the syscall collectors.

use libc;
use num_cpus;
use chrono::{NaiveDateTime, DateTime, UTC, TimeZone};
use nix::Error as NixError;
use nix::errno;
use nix::fcntl::{open, O_RDWR, O_SYNC};
use nix::unistd::close;
use nix::sys::stat::S_IRUSR;
use nix::sys::mman::{mmap, munmap};
use nix::sys::mman::{MAP_SHARED, PROT_READ, PROT_WRITE};
use std::ptr;
use std::mem::size_of;

use syscall::{Syscall, SyscallInfo};
use syscall::syscall_table::SyscallTable;
use error::{Error, Result};

const RING_BUF_SIZE: usize = 8 * 1024 * 1024;
const BUFFER_EMPTY_WAIT_TIME_MS: u32 = 30;
const MAX_N_CONSECUTIVE_WAITS: usize = 4;

const PPM_IOCTL_MAGIC: u8 = 's' as u8;
const PPM_IOCTL_DISABLE_CAPTURE: u8 = 1;
const PPM_IOCTL_ENABLE_CAPTURE: u8 = 1;

ioctl!(none ioctl_start with PPM_IOCTL_MAGIC, PPM_IOCTL_ENABLE_CAPTURE);
ioctl!(none ioctl_stop with PPM_IOCTL_MAGIC, PPM_IOCTL_DISABLE_CAPTURE);

#[repr(C)]
struct RingBufferInfo {
    head: u32,
    tail: u32,
    num_syscalls: u64,
    num_drops_buffer: u64,
    num_drops_pf: u64,
    num_preemptions: u64,
    num_context_switches: u64
}

struct RingBufferDev {
    fd: i32,
    buffer: *mut libc::c_char,
    buffer_info: *mut RingBufferInfo,
    last_readsize: u32,
    next_syscall: *mut libc::c_char,
    len: u32
}

pub trait Collector {
    fn start(&mut self) -> Result<usize>;

    fn stop(&mut self) -> Result<()>;

    fn next(&mut self) -> Option<SyscallInfo>;
}

pub struct RingBufferCollector {
    devs: Vec<RingBufferDev>,
    consecutive_waits: usize,
    syscall_table: SyscallTable
}

/// The implementation of the syscall collector based on the
/// Sysdig's rock solid kernel driver. The driver uses circular / ring
/// buffer data structure to publish the intercepted system calls from the
/// kernel space. This aims to provide extremely high throughput and efficiency.
impl Collector for RingBufferCollector {
    /// Opens the available ring buffer devices and maps the corresponding
    /// buffer content and the buffer information to a memory region
    /// of the calling thread.
    ///
    /// If the previous operations are done successfully, it sends the
    /// IO code to the underlying device to start the capture from the
    /// kernel driver.
    ///
    /// Returns `Result::Ok(i)` where `i` is the number of devices (CPUs) detected on the
    /// system or `Result::Err(e)` where `e` is the payload with a string containing the detailed
    /// error message.
    ///
    /// # Example
    ///
    /// ```
    /// let collector = RingBufferCollector::new();
    /// match collector.start() {
    ///     Ok(num_devs) => {
    ///
    ///     },
    ///     Err(e) => {
    ///     }
    /// }
    /// ```
    ///
    fn start(&mut self) -> Result<usize> {
        let num_devs = num_cpus::get();
        let len = RING_BUF_SIZE * 2;

        // ~ open and map each of the char devices
        // which represent the ring buffers
        for i in 0..num_devs {
            match open(format!("/dev/sysdig{}", i).as_str(), O_RDWR | O_SYNC, S_IRUSR) {
                Ok(fd) => {
                    let buffer = mmap(ptr::null_mut(),
                                      len,
                                      PROT_READ,
                                      MAP_SHARED, fd, 0);
                    let buffer_info = mmap(ptr::null_mut(), size_of::<RingBufferInfo>(),
                                           PROT_READ | PROT_WRITE,
                                           MAP_SHARED, fd, 0);
                    if buffer.is_err() || buffer_info.is_err() {
                        munmap(buffer.unwrap() as *mut libc::c_void, len);
                        close(fd);
                        return Err(Error::RingBufferMapping);
                    }
                    let dev = RingBufferDev {
                        fd: fd,
                        buffer: buffer.unwrap() as *mut libc::c_char,
                        buffer_info: buffer_info.unwrap() as *mut RingBufferInfo,
                        last_readsize: 0,
                        next_syscall: ptr::null_mut(),
                        len: 0
                    };

                    self.devs.push(dev);
                    // ~ send the ioctl code to start the capture
                    unsafe { ioctl_start(fd); }

                },
                Err(e) => {
                    match e {
                        NixError::Sys(err) => {
                            if err == errno::ENODEV {
                                continue;
                            } else if err == errno::EBUSY {
                                return Err(Error::TooManyCollectors);
                            } else {
                                return Err(Error::DeviceError);
                            }
                        },
                        NixError::InvalidPath => {}
                    }
                }
            }
        }
        Ok(num_devs)
    }

    fn stop(&mut self) -> Result<()> {
        let len = RING_BUF_SIZE * 2;
        for dev in &self.devs {
            unsafe { ioctl_stop(dev.fd); }
            munmap(dev.buffer as *mut libc::c_void, len);
            munmap(dev.buffer_info as *mut libc::c_void, size_of::<RingBufferInfo>());
            close(dev.fd);
        }
        Ok(())
    }

    /// Consumes the next available syscall event from the ring buffer. If the buffers
    /// are empty, the head and tail are updated accordingly to pick up the
    /// new generated syscall events. When `consecutive_waits` is above the defined threshold, the
    /// main thread is suspended for `BUFFER_EMPTY_WAIT_TIME_MS` milliseconds. This can give a
    /// chance for buffers to refill.
    ///
    /// Returns `Some(syscall)` where `syscall` stores a plethora of information about the
    /// intercepted syscall event. On error it returns `None`.
    ///
    /// # Example
    ///
    /// ```
    /// let collector = RingBufferCollector::new();
    /// loop {
    ///     match collector.next() {
    ///         Some(syscall_info) => {
    ///             println!("syscall name {}", syscall_info.name);
    ///         },
    ///         None => {}
    ///     }
    /// }
    /// ```
    ///
    fn next(&mut self) -> Option<SyscallInfo> {
        let mut cpu = None;
        let mut syscall = ptr::null_mut();

        for (j, dev) in self.devs.iter().enumerate() {
            if dev.len == 0 {
                continue;
            } else {
                syscall = dev.next_syscall as *mut Syscall;
                cpu = Some(j);
            }
        }
        if cpu != None {
            let cpuid = cpu.unwrap();
            unsafe {
                assert!(self.devs[cpuid].len >= (*syscall).len);
                let len = (*syscall).len as isize;
                self.devs[cpuid].len -= (*syscall).len;
                self.devs[cpuid].next_syscall = self.devs[cpuid].next_syscall.offset(len);
            };
        } else {
            // ~ in case buffers are empty
            if self.check_next_wait() {
                unsafe { libc::usleep(BUFFER_EMPTY_WAIT_TIME_MS * 1000); }
                self.consecutive_waits += 1;
            }

            for mut dev in &mut self.devs {
                let buffer_info = dev.buffer_info;
                let ttail: usize = unsafe { ((*buffer_info).tail + dev.last_readsize) as usize };
                if ttail < RING_BUF_SIZE {
                    unsafe { (*dev.buffer_info).tail = ttail as u32 }
                } else {
                    unsafe { (*dev.buffer_info).tail = (ttail - RING_BUF_SIZE) as u32 }
                }

                let read_size = Self::get_buffer_readsize(buffer_info);
                dev.last_readsize = read_size;
                dev.len = read_size;

                unsafe { dev.next_syscall = dev.buffer.offset(ttail as isize); }
            }
        }

        if syscall.is_null() {
            None
        } else {
            let id = unsafe { (*syscall).id };
            let ts = unsafe { (*syscall).ts };

            match self.syscall_table.get_syscall_meta(id as usize) {
                Some(meta) => {
                    let timestamp = NaiveDateTime::from_timestamp((ts / 1000000000) as i64, 0);
                    let syscall_info = SyscallInfo {
                        ts: DateTime::<UTC>::from_utc(timestamp, UTC),
                        name: meta.name.to_string(),
                        params: meta.build_params(syscall),
                    };
                    return Some(syscall_info);

                },
                None => { return None; }
            }
        }
    }
}

impl RingBufferCollector {

    pub fn new() -> RingBufferCollector {
        RingBufferCollector {
            devs: Vec::<RingBufferDev>::new(),
            consecutive_waits: 0,
            syscall_table: SyscallTable::default()
        }
    }

    /// Determines if we should wait while the ring buffers
    /// are refilled with new system call events. Returns `true` if the
    /// condition is met, or `false` otherwise.
    fn check_next_wait(&mut self) -> bool {
        let mut res = true;
        for dev in &self.devs {
            let read_size = Self::get_buffer_readsize(dev.buffer_info);

            if read_size > 20000 {
                self.consecutive_waits = 0;
                res = false;
            }
        }
        if res == false {
            return false
        }

        if self.consecutive_waits >= MAX_N_CONSECUTIVE_WAITS {
            self.consecutive_waits = 0;
            return false;
        } else {
            return true;
        }
    }

    /// Calculates the new size of the buffer to be read.
    fn get_buffer_readsize(buffer_info: *mut RingBufferInfo) -> u32 {
        let head: usize = unsafe { (*buffer_info).head as usize };
        let tail: usize = unsafe { (*buffer_info).tail as usize };
        (if tail > head { RING_BUF_SIZE - tail + head } else { head - tail }) as u32
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use super::command;
use super::chain;

pub fn install_nginx(host: &str) {
    let nginx_install_command = format!("ssh root@{} 'apt-get update && apt-get install -y nginx'", host);
    println!("Running:\n\t\t{}", nginx_install_command);
    let result = command::run_host_cmd(&nginx_install_command);
    if !result.success {
        println!("{}", result.stderr);
    }
}

fn create_host_file(host: &str, port: &str) -> String {
    let filepath = format!("/tmp/{}.conf", host);
    let path = Path::new(&filepath);
    let mut file = File::create(&path).expect("Failed to open file!");

    let template = r#"
server {
    listen 80;

    server_name {1};

    set_real_ip_from 127.0.0.1;
    set_real_ip_from 192.168.2.1;
    real_ip_header X-Forwarded-For;

    location / {
        proxy_pass http://localhost:{2};
        proxy_http_version 1.1;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}"#;

    // These are hacks LOL, cause format! didn't seem to work with multiline string
    let mut contents = template.replace("{1}", host);
    contents = contents.replace("{2}", port);
    file.write_all(contents.as_bytes());
    return filepath.clone();
}

pub fn add_nginx_host(host: &str, port: &str) {
    let filename = create_host_file(host, port);
    // TODO: cleanup host file after scp
    let conf_create_command = format!("scp {} root@{}:/etc/nginx/conf.d/", filename, host);
    println!("Running:\n\t\t{}", conf_create_command);
    let result = command::run_host_cmd(&conf_create_command);
    if !result.success {
        println!("{}", result.stderr);
    }
}

pub fn install_letsencrypt_cert(host: &str) {
    let install_certbot_cmd = format!("ssh root@{} 'add-apt-repository ppa:certbot/certbot && apt-get update && apt-get install -y python-certbot-nginx'", host);
    println!("Running:\n\t\t{}", install_certbot_cmd);
    let result = command::run_host_cmd(&install_certbot_cmd);
    if !result.success {
        println!("{}", result.stderr);
    }
    let run_certbot_command = format!("ssh root@{} 'certbot --nginx -d {}'", host, host);
    println!("Please run:\n\t{}", run_certbot_command);
}

pub fn install_rust(host: &str) {
    let install_rust_cmd = format!("ssh root@{} 'curl https://sh.rustup.rs -sSf | sh -s -- -y'", host);
    let result = command::run_host_cmd(&install_rust_cmd);
    if !result.success {
        warn!("{}", result.stderr);
    } else {
        info!("{}", result.stdout);
    }
}

pub fn install_python(host: &str) {
    let install_python_cmd = format!("ssh root@{} 'apt-get update && apt-get install -y python'", host);
    let result = command::run_host_cmd(&install_python_cmd);
    if !result.success {
        warn!("{}", result.stderr);
    } else {
        info!("{}", result.stdout);
    }
}

pub fn install_jekyll(host: &str) {
    let _ = chain::CommandChain::new()
        .cmd(&format!("ssh root@{} 'apt-get update && apt-get install -y rubygems build-essential ruby-dev'", host))
        .cmd(&format!("ssh root@{} 'gem install jekyll bundler'", host))
        .execute();
}

pub fn renew_cert(host: &str) {
    let _ = chain::CommandChain::new()
        .cmd(&format!("ssh root@{} 'certbot --nginx renew'", host))
        .execute();
}

pub fn setup_iptables(host: &str) {
    let _ = chain::CommandChain::new()
        .cmd(&format!("ssh root@{} 'iptables -P INPUT ACCEPT'", host)) // First, switch input back to accept
        .cmd(&format!("ssh root@{} 'iptables -F'", host))
        .cmd(&format!("ssh root@{} 'iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP'", host))
        .cmd(&format!("ssh root@{} 'iptables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP'", host))
        .cmd(&format!("ssh root@{} 'iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP'", host))
        .cmd(&format!("ssh root@{} 'iptables -A INPUT -s 127.0.0.1 -j ACCEPT'", host))
        .cmd(&format!("ssh root@{} 'iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT'", host))
        .cmd(&format!("ssh root@{} 'iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT'", host))
        .cmd(&format!("ssh root@{} 'iptables -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT'", host))
        .cmd(&format!("ssh root@{} 'iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT'", host))
        .cmd(&format!("ssh root@{} 'iptables -P OUTPUT ACCEPT'", host))
        .cmd(&format!("ssh root@{} 'iptables -P INPUT DROP'", host))
        .execute();
}

pub fn install_sqlite3(host: &str) {
    let _ = chain::CommandChain::new()
        .cmd(&format!("ssh root@{} 'apt-get update'", host))
        .cmd(&format!("ssh root@{} 'apt-get install -y sqlite3 libsqlite3-dev'", host))
        .execute();
}
<commit_msg>rewrite configure to remove comment and use only chain<commit_after>use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use super::chain;

pub fn install_nginx(host: &str) {
    let nginx_install_command = format!("ssh root@{} 'apt-get update && apt-get install -y nginx'", host);
    let _ = chain::CommandChain::new()
        .cmd(&nginx_install_command)
        .execute();
}

fn create_host_file(host: &str, port: &str) -> String {
    let filepath = format!("/tmp/{}.conf", host);
    let path = Path::new(&filepath);
    let mut file = File::create(&path).expect("Failed to open file!");

    let template = r#"
server {
    listen 80;

    server_name {1};

    set_real_ip_from 127.0.0.1;
    set_real_ip_from 192.168.2.1;
    real_ip_header X-Forwarded-For;

    location / {
        proxy_pass http://localhost:{2};
        proxy_http_version 1.1;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}"#;

    // These are hacks LOL, cause format! didn't seem to work with multiline string
    let mut contents = template.replace("{1}", host);
    contents = contents.replace("{2}", port);
    assert!(file.write_all(contents.as_bytes()).is_ok());
    return filepath.clone();
}

pub fn add_nginx_host(host: &str, port: &str) {
    let filename = create_host_file(host, port);
    let conf_create_command = format!("scp {} root@{}:/etc/nginx/conf.d/", filename, host);
    let _ = chain::CommandChain::new()
        .cmd(&conf_create_command)
        .cmd(&format!("rm {}", filename))
        .execute();
}

pub fn install_letsencrypt_cert(host: &str) {
    let install_certbot_cmd = format!("ssh root@{} 'add-apt-repository ppa:certbot/certbot && apt-get update && apt-get install -y python-certbot-nginx'", host);
    let _ = chain::CommandChain::new()
        .cmd(&install_certbot_cmd)
        .execute();
    let run_certbot_command = format!("ssh root@{} 'certbot --nginx -d {}'", host, host);
    println!("Please run:\n\t{}", run_certbot_command);
}

pub fn install_rust(host: &str) {
    let install_rust_cmd = format!("ssh root@{} 'curl https://sh.rustup.rs -sSf | sh -s -- -y'", host);
    let _ = chain::CommandChain::new()
        .cmd(&install_rust_cmd)
        .execute();
}

pub fn install_python(host: &str) {
    let install_python_cmd = format!("ssh root@{} 'apt-get update && apt-get install -y python'", host);
    let _ = chain::CommandChain::new()
        .cmd(&install_python_cmd)
        .execute();
}

pub fn install_jekyll(host: &str) {
    let _ = chain::CommandChain::new()
        .cmd(&format!("ssh root@{} 'apt-get update && apt-get install -y rubygems build-essential ruby-dev'", host))
        .cmd(&format!("ssh root@{} 'gem install jekyll bundler'", host))
        .execute();
}

pub fn renew_cert(host: &str) {
    let _ = chain::CommandChain::new()
        .cmd(&format!("ssh root@{} 'certbot --nginx renew'", host))
        .execute();
}

pub fn setup_iptables(host: &str) {
    let _ = chain::CommandChain::new()
        .cmd(&format!("ssh root@{} 'iptables -P INPUT ACCEPT'", host)) // First, switch input back to accept
        .cmd(&format!("ssh root@{} 'iptables -F'", host))
        .cmd(&format!("ssh root@{} 'iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP'", host))
        .cmd(&format!("ssh root@{} 'iptables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP'", host))
        .cmd(&format!("ssh root@{} 'iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP'", host))
        .cmd(&format!("ssh root@{} 'iptables -A INPUT -s 127.0.0.1 -j ACCEPT'", host))
        .cmd(&format!("ssh root@{} 'iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT'", host))
        .cmd(&format!("ssh root@{} 'iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT'", host))
        .cmd(&format!("ssh root@{} 'iptables -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT'", host))
        .cmd(&format!("ssh root@{} 'iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT'", host))
        .cmd(&format!("ssh root@{} 'iptables -P OUTPUT ACCEPT'", host))
        .cmd(&format!("ssh root@{} 'iptables -P INPUT DROP'", host))
        .execute();
}

pub fn install_sqlite3(host: &str) {
    let _ = chain::CommandChain::new()
        .cmd(&format!("ssh root@{} 'apt-get update'", host))
        .cmd(&format!("ssh root@{} 'apt-get install -y sqlite3 libsqlite3-dev'", host))
        .execute();
}
<|endoftext|>


<commit_before>//! Constants related to STUN.

/// The magic cookie value.
///
/// > The magic cookie field **MUST** contain the fixed value `0x2112A442` in
/// > network byte order.
/// > In [RFC 3489](https://tools.ietf.org/html/rfc3489), this field was part of
/// > the transaction ID; placing the magic cookie in this location allows
/// > a server to detect if the client will understand certain attributes
/// > that were added in this revised specification.  In addition, it aids
/// > in distinguishing STUN packets from packets of other protocols when
/// > STUN is multiplexed with those other protocols on the same port.
/// >
/// > ([RFC 5389 -- 6. STUN Message Structure](https://tools.ietf.org/html/rfc5389#section-6))
pub const MAGIC_COOKIE: u32 = 0x2112A442;

pub const DEFAULT_PORT: u16 = 3478;
pub const DEFAULT_MAX_MESSAGE_SIZE: usize = 548;
pub const DEFAULT_RTO_MS: u64 = 500;
pub const DEFAULT_RTO_CACHE_DURATION_MS: u64 = 10 * 60 * 1000;
pub const DEFAULT_RC: u32 = 7;
pub const DEFAULT_RM: u32 = 16;
pub const DEFAULT_TI_MS: u64 = 39_500;
pub const DEFAULT_MAX_CLIENT_CONCURRENCY: usize = 10;
pub const DEFAULT_MIN_TRANSACTION_INTERVAL_MS: u64 = DEFAULT_RTO_MS;
<commit_msg>Add documentation for constants<commit_after>//! Constant values related to STUN.

/// The magic cookie value.
///
/// > The magic cookie field **MUST** contain the fixed value `0x2112A442` in
/// > network byte order.
/// > In [RFC 3489](https://tools.ietf.org/html/rfc3489), this field was part of
/// > the transaction ID; placing the magic cookie in this location allows
/// > a server to detect if the client will understand certain attributes
/// > that were added in this revised specification.  In addition, it aids
/// > in distinguishing STUN packets from packets of other protocols when
/// > STUN is multiplexed with those other protocols on the same port.
/// >
/// > ([RFC 5389 -- 6. STUN Message Structure](https://tools.ietf.org/html/rfc5389#section-6))
pub const MAGIC_COOKIE: u32 = 0x2112A442;

/// The default TCP and UDP port for STUN.
pub const DEFAULT_PORT: u16 = 3478;

/// The default TLS port for STUN.
pub const DEFAULT_TLS_PORT: u16 = 5349;

/// The default maximum size of a message.
///
/// > All STUN messages sent over UDP SHOULD be less than the path MTU, if
/// > known.  If the path MTU is unknown, messages SHOULD be the smaller of
/// > 576 bytes and the first-hop MTU for IPv4 [RFC1122] and 1280 bytes for
/// > IPv6 [RFC2460].  This value corresponds to the overall size of the IP
/// > packet.  Consequently, for IPv4, the actual STUN message would need
/// > to be less than **548 bytes** (576 minus 20-byte IP header, minus 8-byte
/// > UDP header, assuming no IP options are used).
/// >
/// > ([RFC 5389 -- 7.1. Forming a Request or an Indication]
/// > (https://tools.ietf.org/html/rfc5389#section-7.1))
pub const DEFAULT_MAX_MESSAGE_SIZE: usize = 548;

/// The default value of RTO (Retransmission TimeOut).
///
/// > A client SHOULD retransmit a STUN request message starting with an
/// > interval of RTO ("Retransmission TimeOut"), doubling after each
/// > retransmission.  The RTO is an estimate of the round-trip time (RTT),
/// > and is computed as described in RFC 2988 [RFC2988], with two
/// > exceptions.  First, the initial value for RTO SHOULD be configurable
/// > (rather than the 3 s recommended in RFC 2988) and SHOULD be greater
/// > than **500 ms**.
/// >
/// > ([RFC 5389 -- 7.2.1. Sending over UDP]
/// > (https://tools.ietf.org/html/rfc5389#section-7.2.1))
pub const DEFAULT_RTO_MS: u64 = 500;

/// The default duration preserving a cached RTO (Retransmission TimeOut).
///
/// > The value for RTO SHOULD be cached by a client after the completion
/// > of the transaction, and used as the starting value for RTO for the
/// > next transaction to the same server (based on equality of IP
/// > address).  The value SHOULD be considered stale and discarded after
/// > **10 minutes**.
/// >
/// > ([RFC 5389 -- 7.2.1. Sending over UDP]
/// > (https://tools.ietf.org/html/rfc5389#section-7.2.1))
pub const DEFAULT_RTO_CACHE_DURATION_MS: u64 = 10 * 60 * 1000;

/// The default value of Rc.
///
/// > Retransmissions continue until a response is received, or until a
/// > total of **Rc** requests have been sent.  Rc SHOULD be configurable and
/// > SHOULD have a default of **7**.
/// >
/// > ([RFC 5389 -- 7.2.1. Sending over UDP]
/// > (https://tools.ietf.org/html/rfc5389#section-7.2.1))
pub const DEFAULT_RC: u32 = 7;

/// The default value of Rm.
///
/// > Retransmissions continue until a response is received, or until a
/// > total of Rc requests have been sent.  Rc SHOULD be configurable and
/// > SHOULD have a default of 7. If, after the last request, a duration
/// > equal to **Rm** times the RTO has passed without a response (providing
/// > ample time to get a response if only this final request actually
/// > succeeds), the client SHOULD consider the transaction to have failed.
/// > **Rm** SHOULD be configurable and SHOULD have a default of **16**.
/// >
/// > ([RFC 5389 -- 7.2.1. Sending over UDP]
/// > (https://tools.ietf.org/html/rfc5389#section-7.2.1))
pub const DEFAULT_RM: u32 = 16;

/// The default value of Ti (Timeout duration for a request over TCP).
///
/// > Reliability of STUN over TCP and TLS-over-TCP is handled by TCP
/// > itself, and there are no retransmissions at the STUN protocol level.
/// > However, for a request/response transaction, if the client has not
/// > received a response by **Ti** seconds after it sent the SYN to establish
/// > the connection, it considers the transaction to have timed out.  **Ti**
/// > SHOULD be configurable and SHOULD have a default of **39.5s**.
/// >
/// > ([RFC 5389 -- 7.2.2. Sending over TCP or TLS-over-TCP]
/// > (https://tools.ietf.org/html/rfc5389#section-7.2.2))
pub const DEFAULT_TI_MS: u64 = 39_500;

/// The default max concurrent transactions by a client to a server.
///
/// > At any time, a client MAY have multiple outstanding STUN requests
/// > with the same STUN server (that is, multiple transactions in
/// > progress, with different transaction IDs).  Absent other limits to
/// > the rate of new transactions (such as those specified by ICE for
/// > connectivity checks or when STUN is run over TCP), a client SHOULD
/// > space new transactions to a server by RTO and SHOULD limit itself to
/// > **ten outstanding transactions** to the same server.
/// >
/// > ([RFC 5389 -- 7.2. Sending the Request or Indication]
/// > (https://tools.ietf.org/html/rfc5389#section-7.2))
pub const DEFAULT_MAX_CLIENT_CONCURRENCY: usize = 10;

/// The default interval between transactions issued by a client to a serve.
///
/// > At any time, a client MAY have multiple outstanding STUN requests
/// > with the same STUN server (that is, multiple transactions in
/// > progress, with different transaction IDs).  Absent other limits to
/// > the rate of new transactions (such as those specified by ICE for
/// > connectivity checks or when STUN is run over TCP), **a client SHOULD
/// > space new transactions to a server by RTO** and SHOULD limit itself to
/// > ten outstanding transactions to the same server.
/// >
/// > ([RFC 5389 -- 7.2. Sending the Request or Indication]
/// > (https://tools.ietf.org/html/rfc5389#section-7.2))
pub const DEFAULT_MIN_TRANSACTION_INTERVAL_MS: u64 = DEFAULT_RTO_MS;
<|endoftext|>


<commit_before>//! Types used in the registry.

use std::sync::Arc;
use std::fmt::{Debug, Formatter};
use std::fmt::Result as FmtResult;

use rustc_serialize::json::Json;

bitflags! {
    /// Access permissions for items in the registry
    pub flags AccessFlags: u8 {
        #[allow(dead_code)]
        /// Default flags
        const LUA_PRIVATE = 0,
        /// Lua thread can read the data
        const LUA_READ    = 1 << 0,
        /// Lua thread can write to the data
        const LUA_WRITE   = 1 << 1,
    }
}

/// Command type for Rust function
pub type CommandFn = Arc<Fn() + Send + Sync>;

/// Function which will yield an object
pub type GetFn = Arc<Fn() -> Json + Send + Sync>;

/// Function which will set an object
pub type SetFn = Arc<Fn(Json) + Send + Sync>;

/// Enum of types of registry fields
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum FieldType { Object, Property, Command }

/// Data which can be stored in the registry
#[derive(Clone)]
pub enum RegistryField {
    /// An object with permission flags
    Object {
        /// Permission flags for Lua get/setting the value
        flags: AccessFlags,
        /// Data associated with this value
        data: Arc<Json>
    },
    /// A registry value whose get and set maps to other Rust code
    Property {
        /// Method called to set a property
        get: Option<GetFn>,
        /// Method called to set a property
        set: Option<SetFn>
    },
    /// A command
    Command(CommandFn)
}

/// Result of what can be accessed from a registry value.
#[derive(Clone)]
pub enum RegistryGetData {
    /// An object in the registry
    Object(AccessFlags, Arc<Json>),
    /// Get field of a property in the registry
    Property(GetFn)
}

impl Debug for RegistryField {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match self {
            &RegistryField::Object { ref flags, ref data } =>
                f.debug_struct("RegistryField::Object")
                .field("flags", flags as &Debug)
                .field("data", data as &Debug).finish(),
            &RegistryField::Property { ref get, ref set } => {
                let new_get = match get { &Some(_) => Some(true), &None => None };
                let new_set = match set { &Some(_) => Some(true), &None => None };
                f.debug_struct("RegistryField::Property")
                    .field("get", &new_get)
                    .field("set", &new_set)
                    .finish()
            }
            &RegistryField::Command(_) =>
                write!(f, "RegistryField::Command(...)")
        }
    }
}

impl Debug for RegistryGetData {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match self {
            &RegistryGetData::Object(ref flags, ref data) =>
                f.debug_struct("RegistryGetData::Object")
                .field("flags", flags as &Debug)
                .field("data", data as &Debug).finish(),
            &RegistryGetData::Property(_) =>
                write!(f, "RegistryGetData::Property")
        }
    }
}

impl FieldType {
    pub fn can_set_from(self, other: FieldType) -> bool {
        match self {
            FieldType::Command => other == FieldType::Command,
            FieldType::Property =>
                other == FieldType::Object ||
                other == FieldType::Property,
            FieldType::Object => other == FieldType::Object
        }
    }
}

impl RegistryField {
    /// What access the module has to it
    #[allow(dead_code)]
    pub fn get_flags(&self) -> Option<AccessFlags> {
        match self {
            &RegistryField::Object { ref flags, .. } => Some(flags.clone()),
            &RegistryField::Property { ref get, ref set } => {
                let mut flags = AccessFlags::empty();
                if get.is_some() { flags.insert(LUA_READ); }
                if set.is_some() { flags.insert(LUA_WRITE); }
                Some(flags)
            },
            _ => None
        }
    }

    /// Attempts to access the RegistryField as a command
    pub fn get_command(&self) -> Option<CommandFn> {
        match *self {
            RegistryField::Command(ref com) => Some(com.clone()),
            _ => None
        }
    }

    /// Attempts to access the RegistryField as a file
    #[allow(dead_code)]
    pub fn get_data(&self) -> Option<RegistryGetData> {
        match *self {
            RegistryField::Object { ref flags, ref data } =>
                Some(RegistryGetData::Object(flags.clone(), data.clone())),
            RegistryField::Property { ref get, .. } =>
                get.clone().and_then(|g| Some(RegistryGetData::Property(g))),
            _ => None
        }
    }

    /// Converts this RegistryField to maybe a command
    #[allow(dead_code)]
    pub fn as_command(self) -> Option<CommandFn> {
        match self {
            RegistryField::Command(com) => Some(com),
            _ => None
        }
    }

    pub fn as_object(self) -> Option<(AccessFlags, Arc<Json>)> {
        match self {
            RegistryField::Object { flags, data } => Some((flags, data)),
            _ => None
        }
    }

    pub fn as_property(self) -> Option<(Option<GetFn>, Option<SetFn>)> {
        match self {
            RegistryField::Property { get, set } => Some((get, set)),
            _ => None
        }
    }

    #[allow(dead_code)]
    pub fn as_property_get(self) -> Option<GetFn> {
        self.as_property().and_then(|(maybe_get, _)| maybe_get)
    }

    pub fn as_property_set(self) -> Option<SetFn> {
        self.as_property().and_then(|(_, maybe_set)| maybe_set)
    }

    /// Gets the type of this registry field
    pub fn get_type(&self) -> FieldType {
        match self {
            &RegistryField::Object { .. }   => FieldType::Object,
            &RegistryField::Property { .. } => FieldType::Property,
            &RegistryField::Command(_)      => FieldType::Command
        }
    }
}

impl RegistryGetData {
    pub fn resolve(self) -> (AccessFlags, Arc<Json>) {
        match self {
            RegistryGetData::Object(flags, data) => (flags, data),
            RegistryGetData::Property(get) =>
                (AccessFlags::all(), Arc::new(get()))
        }
    }

    #[allow(dead_code)]
    pub fn get_type(&self) -> FieldType {
        match self {
            &RegistryGetData::Property(_) => FieldType::Property,
            &RegistryGetData::Object(_, _) => FieldType::Object
        }
    }
}
<commit_msg>Add comments<commit_after>//! Types used in the registry.

use std::sync::Arc;
use std::fmt::{Debug, Formatter};
use std::fmt::Result as FmtResult;

use rustc_serialize::json::Json;

bitflags! {
    /// Access permissions for items in the registry
    pub flags AccessFlags: u8 {
        #[allow(dead_code)]
        /// Default flags
        const LUA_PRIVATE = 0,
        /// Lua thread can read the data
        const LUA_READ    = 1 << 0,
        /// Lua thread can write to the data
        const LUA_WRITE   = 1 << 1,
    }
}

/// Command type for Rust function
pub type CommandFn = Arc<Fn() + Send + Sync>;

/// Function which will yield an object
pub type GetFn = Arc<Fn() -> Json + Send + Sync>;

/// Function which will set an object
pub type SetFn = Arc<Fn(Json) + Send + Sync>;

/// Enum of types of registry fields
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum FieldType { Object, Property, Command }

/// Data which can be stored in the registry
#[derive(Clone)]
pub enum RegistryField {
    /// An object with permission flags
    Object {
        /// Permission flags for Lua get/setting the value
        flags: AccessFlags,
        /// Data associated with this value
        data: Arc<Json>
    },
    /// A registry value whose get and set maps to other Rust code
    Property {
        /// Method called to set a property
        get: Option<GetFn>,
        /// Method called to set a property
        set: Option<SetFn>
    },
    /// A command
    Command(CommandFn)
}

/// Result of what can be accessed from a registry value.
#[derive(Clone)]
pub enum RegistryGetData {
    /// An object in the registry
    Object(AccessFlags, Arc<Json>),
    /// Get field of a property in the registry
    Property(GetFn)
}

impl Debug for RegistryField {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match self {
            &RegistryField::Object { ref flags, ref data } =>
                f.debug_struct("RegistryField::Object")
                .field("flags", flags as &Debug)
                .field("data", data as &Debug).finish(),
            &RegistryField::Property { ref get, ref set } => {
                let new_get = match get { &Some(_) => Some(true), &None => None };
                let new_set = match set { &Some(_) => Some(true), &None => None };
                f.debug_struct("RegistryField::Property")
                    .field("get", &new_get)
                    .field("set", &new_set)
                    .finish()
            }
            &RegistryField::Command(_) =>
                write!(f, "RegistryField::Command(...)")
        }
    }
}

impl Debug for RegistryGetData {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match self {
            &RegistryGetData::Object(ref flags, ref data) =>
                f.debug_struct("RegistryGetData::Object")
                .field("flags", flags as &Debug)
                .field("data", data as &Debug).finish(),
            &RegistryGetData::Property(_) =>
                write!(f, "RegistryGetData::Property")
        }
    }
}

impl FieldType {
    /// Whether a field of this type can be changed by a field of type other.
    pub fn can_set_from(self, other: FieldType) -> bool {
        match self {
            FieldType::Command => other == FieldType::Command,
            FieldType::Property =>
                other == FieldType::Object ||
                other == FieldType::Property,
            FieldType::Object => other == FieldType::Object
        }
    }
}

impl RegistryField {
    /// What access the module has to it
    #[allow(dead_code)]
    pub fn get_flags(&self) -> Option<AccessFlags> {
        match self {
            &RegistryField::Object { ref flags, .. } => Some(flags.clone()),
            &RegistryField::Property { ref get, ref set } => {
                let mut flags = AccessFlags::empty();
                if get.is_some() { flags.insert(LUA_READ); }
                if set.is_some() { flags.insert(LUA_WRITE); }
                Some(flags)
            },
            _ => None
        }
    }

    /// Attempts to access the RegistryField as a command
    pub fn get_command(&self) -> Option<CommandFn> {
        match *self {
            RegistryField::Command(ref com) => Some(com.clone()),
            _ => None
        }
    }

    /// Attempts to access the RegistryField as a file
    #[allow(dead_code)]
    pub fn get_data(&self) -> Option<RegistryGetData> {
        match *self {
            RegistryField::Object { ref flags, ref data } =>
                Some(RegistryGetData::Object(flags.clone(), data.clone())),
            RegistryField::Property { ref get, .. } =>
                get.clone().and_then(|g| Some(RegistryGetData::Property(g))),
            _ => None
        }
    }

    /// Converts this RegistryField to maybe a command
    #[allow(dead_code)]
    pub fn as_command(self) -> Option<CommandFn> {
        match self {
            RegistryField::Command(com) => Some(com),
            _ => None
        }
    }

    /// Converts this RegistryField to maybe an object. Does not call property methods.
    pub fn as_object(self) -> Option<(AccessFlags, Arc<Json>)> {
        match self {
            RegistryField::Object { flags, data } => Some((flags, data)),
            _ => None
        }
    }

    /// Gets this field as maybe a property with maybe a getter and setter.
    pub fn as_property(self) -> Option<(Option<GetFn>, Option<SetFn>)> {
        match self {
            RegistryField::Property { get, set } => Some((get, set)),
            _ => None
        }
    }

    /// Returns the getter, if this field is a property with a getter.
    #[allow(dead_code)]
    pub fn as_property_get(self) -> Option<GetFn> {
        self.as_property().and_then(|(maybe_get, _)| maybe_get)
    }

    /// Returns a setter if this field is a property with a setter.
    pub fn as_property_set(self) -> Option<SetFn> {
        self.as_property().and_then(|(_, maybe_set)| maybe_set)
    }

    /// Gets the type of this registry field
    pub fn get_type(&self) -> FieldType {
        match self {
            &RegistryField::Object { .. }   => FieldType::Object,
            &RegistryField::Property { .. } => FieldType::Property,
            &RegistryField::Command(_)      => FieldType::Command
        }
    }
}

impl RegistryGetData {
    /// Collapses the waveform.
    ///
    /// If this is a Json, returns the Json data. If this is a property, runs the
    /// method and returns the output.
    pub fn resolve(self) -> (AccessFlags, Arc<Json>) {
        match self {
            RegistryGetData::Object(flags, data) => (flags, data),
            RegistryGetData::Property(get) =>
                (AccessFlags::all(), Arc::new(get()))
        }
    }

    /// Gets the FieldType of this GetData (property or object)
    #[allow(dead_code)]
    pub fn get_type(&self) -> FieldType {
        match self {
            &RegistryGetData::Property(_) => FieldType::Property,
            &RegistryGetData::Object(_, _) => FieldType::Object
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Empty main function<commit_after><|endoftext|>


<commit_before>use std::fmt;
use std::str::FromStr;

use super::lang_mapping;
use crate::error::Error;
use crate::Lang;

#[cfg(feature = "enum-map")]
use enum_map::Enum;

/// Represents a writing system (Latin, Cyrillic, Arabic, etc).
#[cfg_attr(feature = "enum-map", derive(Enum))]
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum Script {
    // Keep this in alphabetic order (for C bindings)
    Arabic,
    Bengali,
    Cyrillic,
    Devanagari,
    Ethiopic,
    Georgian,
    Greek,
    Gujarati,
    Gurmukhi,
    Hangul,
    Hebrew,
    Hiragana,
    Kannada,
    Katakana,
    Khmer,
    Latin,
    Malayalam,
    Mandarin,
    Myanmar,
    Oriya,
    Sinhala,
    Tamil,
    Telugu,
    Thai,
}

// Array of all existing Script values.
const VALUES: [Script; 24] = [
    Script::Arabic,
    Script::Bengali,
    Script::Cyrillic,
    Script::Devanagari,
    Script::Ethiopic,
    Script::Georgian,
    Script::Greek,
    Script::Gujarati,
    Script::Gurmukhi,
    Script::Hangul,
    Script::Hebrew,
    Script::Hiragana,
    Script::Kannada,
    Script::Katakana,
    Script::Khmer,
    Script::Latin,
    Script::Malayalam,
    Script::Mandarin,
    Script::Myanmar,
    Script::Oriya,
    Script::Sinhala,
    Script::Tamil,
    Script::Telugu,
    Script::Thai,
];

impl Script {
    /// Get all existing scripts.
    ///
    /// # Example
    /// ```
    /// use whatlang::Script;
    /// for script in Script::all() {
    ///     println!("{}", script);
    /// }
    /// ```
    pub fn all() -> &'static [Script] {
        &VALUES
    }

    pub fn name(&self) -> &str {
        match *self {
            Script::Latin => "Latin",
            Script::Cyrillic => "Cyrillic",
            Script::Arabic => "Arabic",
            Script::Devanagari => "Devanagari",
            Script::Hiragana => "Hiragana",
            Script::Katakana => "Katakana",
            Script::Ethiopic => "Ethiopic",
            Script::Hebrew => "Hebrew",
            Script::Bengali => "Bengali",
            Script::Georgian => "Georgian",
            Script::Mandarin => "Mandarin",
            Script::Hangul => "Hangul",
            Script::Greek => "Greek",
            Script::Kannada => "Kannada",
            Script::Tamil => "Tamil",
            Script::Thai => "Thai",
            Script::Gujarati => "Gujarati",
            Script::Gurmukhi => "Gurmukhi",
            Script::Telugu => "Telugu",
            Script::Malayalam => "Malayalam",
            Script::Oriya => "Oriya",
            Script::Myanmar => "Myanmar",
            Script::Sinhala => "Sinhala",
            Script::Khmer => "Khmer",
        }
    }

    pub fn langs(&self) -> &[Lang] {
        lang_mapping::script_langs(*self)
    }
}

impl fmt::Display for Script {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.name())
    }
}

impl FromStr for Script {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().trim() {
            "latin" => Ok(Script::Latin),
            "cyrillic" => Ok(Script::Cyrillic),
            "arabic" => Ok(Script::Arabic),
            "devanagari" => Ok(Script::Devanagari),
            "hiragana" => Ok(Script::Hiragana),
            "katakana" => Ok(Script::Katakana),
            "ethiopic" => Ok(Script::Ethiopic),
            "hebrew" => Ok(Script::Hebrew),
            "bengali" => Ok(Script::Bengali),
            "georgian" => Ok(Script::Georgian),
            "mandarin" => Ok(Script::Mandarin),
            "hangul" => Ok(Script::Hangul),
            "greek" => Ok(Script::Greek),
            "kannada" => Ok(Script::Kannada),
            "tamil" => Ok(Script::Tamil),
            "thai" => Ok(Script::Thai),
            "gujarati" => Ok(Script::Gujarati),
            "gurmukhi" => Ok(Script::Gurmukhi),
            "telugu" => Ok(Script::Telugu),
            "malayalam" => Ok(Script::Malayalam),
            "oriya" => Ok(Script::Oriya),
            "myanmar" => Ok(Script::Myanmar),
            "sinhala" => Ok(Script::Sinhala),
            "khmer" => Ok(Script::Khmer),
            _ => Err(Error::ParseScript(s.to_string())),
        }
    }
}

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

    #[test]
    fn test_all() {
        assert_eq!(Script::all().len(), 24);
        let all = Script::all();
        assert!(all.contains(&Script::Cyrillic));
        assert!(all.contains(&Script::Arabic));
        assert!(all.contains(&Script::Latin));
    }

    #[test]
    fn test_from_str() {
        for &script in Script::all() {
            let s = script.name();
            assert_eq!(s.parse::<Script>().unwrap(), script);
            assert_eq!(s.to_lowercase().parse::<Script>().unwrap(), script);
            assert_eq!(s.to_uppercase().parse::<Script>().unwrap(), script);
        }

        let result = "foobar".parse::<Script>();
        assert!(matches!(result, Err(Error::ParseScript(_))));
    }

    #[test]
    fn test_langs() {
        // Vec of all langs obtained with script.langs()
        let script_langs: Vec<Lang> = Script::all()
            .iter()
            .map(|script| script.langs())
            .flatten().copied()
            .collect();

        // Ensure all langs belong at least to one script
        for lang in Lang::all() {
            assert!(script_langs.contains(&lang));
        }
    }
}
<commit_msg>Fix formatting<commit_after>use std::fmt;
use std::str::FromStr;

use super::lang_mapping;
use crate::error::Error;
use crate::Lang;

#[cfg(feature = "enum-map")]
use enum_map::Enum;

/// Represents a writing system (Latin, Cyrillic, Arabic, etc).
#[cfg_attr(feature = "enum-map", derive(Enum))]
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum Script {
    // Keep this in alphabetic order (for C bindings)
    Arabic,
    Bengali,
    Cyrillic,
    Devanagari,
    Ethiopic,
    Georgian,
    Greek,
    Gujarati,
    Gurmukhi,
    Hangul,
    Hebrew,
    Hiragana,
    Kannada,
    Katakana,
    Khmer,
    Latin,
    Malayalam,
    Mandarin,
    Myanmar,
    Oriya,
    Sinhala,
    Tamil,
    Telugu,
    Thai,
}

// Array of all existing Script values.
const VALUES: [Script; 24] = [
    Script::Arabic,
    Script::Bengali,
    Script::Cyrillic,
    Script::Devanagari,
    Script::Ethiopic,
    Script::Georgian,
    Script::Greek,
    Script::Gujarati,
    Script::Gurmukhi,
    Script::Hangul,
    Script::Hebrew,
    Script::Hiragana,
    Script::Kannada,
    Script::Katakana,
    Script::Khmer,
    Script::Latin,
    Script::Malayalam,
    Script::Mandarin,
    Script::Myanmar,
    Script::Oriya,
    Script::Sinhala,
    Script::Tamil,
    Script::Telugu,
    Script::Thai,
];

impl Script {
    /// Get all existing scripts.
    ///
    /// # Example
    /// ```
    /// use whatlang::Script;
    /// for script in Script::all() {
    ///     println!("{}", script);
    /// }
    /// ```
    pub fn all() -> &'static [Script] {
        &VALUES
    }

    pub fn name(&self) -> &str {
        match *self {
            Script::Latin => "Latin",
            Script::Cyrillic => "Cyrillic",
            Script::Arabic => "Arabic",
            Script::Devanagari => "Devanagari",
            Script::Hiragana => "Hiragana",
            Script::Katakana => "Katakana",
            Script::Ethiopic => "Ethiopic",
            Script::Hebrew => "Hebrew",
            Script::Bengali => "Bengali",
            Script::Georgian => "Georgian",
            Script::Mandarin => "Mandarin",
            Script::Hangul => "Hangul",
            Script::Greek => "Greek",
            Script::Kannada => "Kannada",
            Script::Tamil => "Tamil",
            Script::Thai => "Thai",
            Script::Gujarati => "Gujarati",
            Script::Gurmukhi => "Gurmukhi",
            Script::Telugu => "Telugu",
            Script::Malayalam => "Malayalam",
            Script::Oriya => "Oriya",
            Script::Myanmar => "Myanmar",
            Script::Sinhala => "Sinhala",
            Script::Khmer => "Khmer",
        }
    }

    pub fn langs(&self) -> &[Lang] {
        lang_mapping::script_langs(*self)
    }
}

impl fmt::Display for Script {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.name())
    }
}

impl FromStr for Script {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().trim() {
            "latin" => Ok(Script::Latin),
            "cyrillic" => Ok(Script::Cyrillic),
            "arabic" => Ok(Script::Arabic),
            "devanagari" => Ok(Script::Devanagari),
            "hiragana" => Ok(Script::Hiragana),
            "katakana" => Ok(Script::Katakana),
            "ethiopic" => Ok(Script::Ethiopic),
            "hebrew" => Ok(Script::Hebrew),
            "bengali" => Ok(Script::Bengali),
            "georgian" => Ok(Script::Georgian),
            "mandarin" => Ok(Script::Mandarin),
            "hangul" => Ok(Script::Hangul),
            "greek" => Ok(Script::Greek),
            "kannada" => Ok(Script::Kannada),
            "tamil" => Ok(Script::Tamil),
            "thai" => Ok(Script::Thai),
            "gujarati" => Ok(Script::Gujarati),
            "gurmukhi" => Ok(Script::Gurmukhi),
            "telugu" => Ok(Script::Telugu),
            "malayalam" => Ok(Script::Malayalam),
            "oriya" => Ok(Script::Oriya),
            "myanmar" => Ok(Script::Myanmar),
            "sinhala" => Ok(Script::Sinhala),
            "khmer" => Ok(Script::Khmer),
            _ => Err(Error::ParseScript(s.to_string())),
        }
    }
}

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

    #[test]
    fn test_all() {
        assert_eq!(Script::all().len(), 24);
        let all = Script::all();
        assert!(all.contains(&Script::Cyrillic));
        assert!(all.contains(&Script::Arabic));
        assert!(all.contains(&Script::Latin));
    }

    #[test]
    fn test_from_str() {
        for &script in Script::all() {
            let s = script.name();
            assert_eq!(s.parse::<Script>().unwrap(), script);
            assert_eq!(s.to_lowercase().parse::<Script>().unwrap(), script);
            assert_eq!(s.to_uppercase().parse::<Script>().unwrap(), script);
        }

        let result = "foobar".parse::<Script>();
        assert!(matches!(result, Err(Error::ParseScript(_))));
    }

    #[test]
    fn test_langs() {
        // Vec of all langs obtained with script.langs()
        let script_langs: Vec<Lang> = Script::all()
            .iter()
            .map(|script| script.langs())
            .flatten()
            .copied()
            .collect();

        // Ensure all langs belong at least to one script
        for lang in Lang::all() {
            assert!(script_langs.contains(&lang));
        }
    }
}
<|endoftext|>


<commit_before>use super::get_json;
use json::JsonValue;

use Degree;

const base_url: &'static str = "https://www2.htw-dresden.de/~app/API/GetExams.php";

pub fn student_exams(year: u16, course: u16, degree: Degree) -> Option<Vec<Exam>> {
    let url = format!("{base}?StgJhr={year}&Stg={course}&AbSc={degree}",
                      base = base_url,
                      year = year,
                      course = course,
                      degree = degree.short());

    let json = get_json(&url);
    let arr = match json {
        JsonValue::Array(arr) => arr,
        _ => panic!("No data in json array ¯\\_(ツ)_/¯"),
    };

    let mut exams: Vec<Exam> = Vec::new();
    for value in arr {
        let e = Exam::from_json(value);
        exams.push(e);
    }

    Some(exams)
}

pub fn prof_exams(prof: &str) {
    println!("Getting data for prof {}.", prof);
}

#[derive(Debug)]
pub struct Exam {
    pub title: String,
    pub exam_type: String,
    pub study_branch: String,
    pub day: String,
    pub start_time: String,
    pub end_time: String,
    pub examiner: String,
    pub next_chance: String,
    pub rooms: Vec<String>,
}

impl Exam {
    fn from_json(json: JsonValue) -> Exam {
        Exam {
            // wat o.O
            title: String::from(json["Title"].as_str().unwrap()),
            exam_type: String::from(json["ExamType"].as_str().unwrap()),
            study_branch: String::from(json["StudyBranch"].as_str().unwrap()),
            day: String::from(json["Day"].as_str().unwrap()),
            start_time: String::from(json["StartTime"].as_str().unwrap()),
            end_time: String::from(json["EndTime"].as_str().unwrap()),
            examiner: String::from(json["Examiner"].as_str().unwrap()),
            next_chance: String::from(json["NextChance"].as_str().unwrap()),
            rooms: vec![String::new()],
        }
    }
}
<commit_msg>clean up `use`s and use Year and Course type<commit_after>use json::JsonValue;

use Year;
use Degree;
use Course;
use get_json;

const base_url: &'static str = "https://www2.htw-dresden.de/~app/API/GetExams.php";

pub fn student_exams(year: Year, course: Course, degree: Degree) -> Option<Vec<Exam>> {
    let url = format!("{base}?StgJhr={year}&Stg={course}&AbSc={degree}",
                      base = base_url,
                      year = year,
                      course = course,
                      degree = degree.short());

    let json = get_json(&url);
    let arr = match json {
        JsonValue::Array(arr) => arr,
        _ => panic!("No data in json array ¯\\_(ツ)_/¯"),
    };

    let mut exams: Vec<Exam> = Vec::new();
    for value in arr {
        let e = Exam::from_json(value);
        exams.push(e);
    }

    Some(exams)
}

pub fn prof_exams(prof: &str) {}

#[derive(Debug)]
pub struct Exam {
    pub title: String,
    pub exam_type: String,
    pub study_branch: String,
    pub day: String,
    pub start_time: String,
    pub end_time: String,
    pub examiner: String,
    pub next_chance: String,
    pub rooms: Vec<String>,
}

impl Exam {
    fn from_json(json: JsonValue) -> Exam {
        Exam {
            // wat o.O
            title: String::from(json["Title"].as_str().unwrap()),
            exam_type: String::from(json["ExamType"].as_str().unwrap()),
            study_branch: String::from(json["StudyBranch"].as_str().unwrap()),
            day: String::from(json["Day"].as_str().unwrap()),
            start_time: String::from(json["StartTime"].as_str().unwrap()),
            end_time: String::from(json["EndTime"].as_str().unwrap()),
            examiner: String::from(json["Examiner"].as_str().unwrap()),
            next_chance: String::from(json["NextChance"].as_str().unwrap()),
            rooms: vec![String::new()],
        }
    }
}
<|endoftext|>