<commit_before>use std::io::{stderr, Write, ErrorKind};
use std::ascii::AsciiExt;
use std::str;
use std::fmt;
use std::process;

pub struct Options {
   pub print_offset: OffsetRadix,
   pub match_length: usize,
   pub path: String,
   pub zork_mode: bool
}

#[derive(Clone, Copy)]
pub enum OffsetRadix {
    None,
    Hex,
    Octal,
    Decimal
}

pub fn string_to_offset_radix(input: Option<&str>) -> Result<OffsetRadix, ()> {
    match input{
        Some(string) => match string {
            "x" => Ok(OffsetRadix::Hex),
            "o" => Ok(OffsetRadix::Octal),
            "d" => Ok(OffsetRadix::Decimal),
            _ => Err(())
        },
        None => Ok(OffsetRadix::None)
    }

}

pub fn bytes_to_strings<W: Write>(bytes: &[u8], w:  &mut W, opts: &Options) {
    let min_consecutive_chars = opts.match_length;
    let mut current_bytes : Vec<u8> = Vec::new();

    let mut offset = 0usize;

    for b in bytes {
        if b.is_ascii() {
            current_bytes.push(*b);
        }
        else  {
            if current_bytes.len() >= min_consecutive_chars {
                let result = str::from_utf8(&current_bytes);
                if let Ok(string) = result {
                    let offset_string = offset_string(offset, opts.print_offset);
                    let write_result = writeln!(w, "{}{}", offset_string, string);
                    if let Err(e) = write_result {
                        match e.kind() {
                            ErrorKind::BrokenPipe => break,
                            _ => {
                                writeln!(stderr(), "{}", e).unwrap();
                                process::exit(1);
                            }
                        }
                    }
                }
            }
            current_bytes.truncate(0);
            offset += 1;
        }
    }
    if current_bytes.len() >= min_consecutive_chars {
        let result = str::from_utf8(&current_bytes);
        if let Ok(string) = result {
            let offset_string = offset_string(offset, opts.print_offset);
            let write_result = writeln!(w, "{}{}", offset_string, string);
            match write_result {
                Ok(_) => (),
                Err(e) => match e.kind() {
                    ErrorKind::BrokenPipe => (),
                    _ => {
                        writeln!(stderr(), "{}", e).unwrap();
                        process::exit(1);
                    }
                }
            }
        }
    }
}

fn offset_string(offset: usize, radix: OffsetRadix) -> String {
    let mut output = "".to_string();
    match radix {
        OffsetRadix::Hex => fmt::write(&mut output, format_args!("0x{:X}: ", offset)).unwrap(),
        OffsetRadix::Octal => fmt::write(&mut output, format_args!("0o{:o}: ", offset)).unwrap(),
        OffsetRadix::Decimal => fmt::write(&mut output, format_args!("{}: ", offset)).unwrap(),
        OffsetRadix::None => ()
    }

    output
}

#[test]
fn it_writes_to_the_buffer() {

    use std::io::Cursor;

    let opts = Options {
        print_offset: OffsetRadix::None,
        match_length: 3,
        path: "".to_string(),
        zork_mode: false
    };

    let mut cursor = Cursor::new(Vec::new());
    bytes_to_strings("hello".as_bytes(), &mut cursor, &opts);
    let expected = "hello\n";
    let vec = cursor.into_inner();
    let actual = String::from_utf8(vec).unwrap();
    assert_eq!(expected, actual);
}

#[test]
fn it_omits_intermediate_nonsense() {
    use std::io::Cursor;
    let mut cursor = Cursor::new(Vec::new());

    let opts = Options {
        print_offset: OffsetRadix::None,
        match_length: 3,
        path: "".to_string(),
        zork_mode: false
    };

    let mut bytes : Vec<u8> = Vec::new();
    for b in "Hello".as_bytes() {
        bytes.push(*b);
    }
    bytes.push(254u8);
    bytes.push(254u8);
    for b in "World".as_bytes() {
        bytes.push(*b);
    }
    bytes_to_strings(&bytes, &mut cursor, &opts);
    let expected = "Hello\nWorld\n";
    let vec = cursor.into_inner();
    let actual = String::from_utf8(vec).unwrap();
    assert_eq!(expected, actual);
}
<commit_msg>define traits, but they are not used yet<commit_after>use std::io::{stderr, Write, ErrorKind};
use std::ascii::AsciiExt;
use std::str;
use std::fmt;
use std::process;

pub struct Options {
   pub print_offset: OffsetRadix,
   pub match_length: usize,
   pub path: String,
   pub zork_mode: bool
}

#[derive(Clone, Copy)]
pub enum OffsetRadix {
    None,
    Hex,
    Octal,
    Decimal
}

trait StringExtractor {
    fn bytes_to_string<W: Write>(&self, bytes: &[u8], writer: &mut W, options: &Options);
    fn offset_string(&self, offset: usize, radix: OffsetRadix) -> String;
}

// trait MinLengthContribution {
//     fn contribution(&self) -> usize;
// }

pub struct AsciiExtractor {

}

impl StringExtractor for AsciiExtractor {

}

pub fn string_to_offset_radix(input: Option<&str>) -> Result<OffsetRadix, ()> {
    match input{
        Some(string) => match string {
            "x" => Ok(OffsetRadix::Hex),
            "o" => Ok(OffsetRadix::Octal),
            "d" => Ok(OffsetRadix::Decimal),
            _ => Err(())
        },
        None => Ok(OffsetRadix::None)
    }

}

pub fn bytes_to_strings<W: Write>(bytes: &[u8], w:  &mut W, opts: &Options) {
    let min_consecutive_chars = opts.match_length;
    let mut current_bytes : Vec<u8> = Vec::new();

    let mut offset = 0usize;

    for b in bytes {
        if b.is_ascii() {
            current_bytes.push(*b);
        }
        else  {
            if current_bytes.len() >= min_consecutive_chars {
                let result = str::from_utf8(&current_bytes);
                if let Ok(string) = result {
                    let offset_string = offset_string(offset, opts.print_offset);
                    let write_result = writeln!(w, "{}{}", offset_string, string);
                    if let Err(e) = write_result {
                        match e.kind() {
                            ErrorKind::BrokenPipe => break,
                            _ => {
                                writeln!(stderr(), "{}", e).unwrap();
                                process::exit(1);
                            }
                        }
                    }
                }
            }
            current_bytes.truncate(0);
            offset += 1;
        }
    }
    if current_bytes.len() >= min_consecutive_chars {
        let result = str::from_utf8(&current_bytes);
        if let Ok(string) = result {
            let offset_string = offset_string(offset, opts.print_offset);
            let write_result = writeln!(w, "{}{}", offset_string, string);
            match write_result {
                Ok(_) => (),
                Err(e) => match e.kind() {
                    ErrorKind::BrokenPipe => (),
                    _ => {
                        writeln!(stderr(), "{}", e).unwrap();
                        process::exit(1);
                    }
                }
            }
        }
    }
}

fn offset_string(offset: usize, radix: OffsetRadix) -> String {
    let mut output = "".to_string();
    match radix {
        OffsetRadix::Hex => fmt::write(&mut output, format_args!("0x{:X}: ", offset)).unwrap(),
        OffsetRadix::Octal => fmt::write(&mut output, format_args!("0o{:o}: ", offset)).unwrap(),
        OffsetRadix::Decimal => fmt::write(&mut output, format_args!("{}: ", offset)).unwrap(),
        OffsetRadix::None => ()
    }

    output
}

#[test]
fn it_writes_to_the_buffer() {

    use std::io::Cursor;

    let opts = Options {
        print_offset: OffsetRadix::None,
        match_length: 3,
        path: "".to_string(),
        zork_mode: false
    };

    let mut cursor = Cursor::new(Vec::new());
    bytes_to_strings("hello".as_bytes(), &mut cursor, &opts);
    let expected = "hello\n";
    let vec = cursor.into_inner();
    let actual = String::from_utf8(vec).unwrap();
    assert_eq!(expected, actual);
}

#[test]
fn it_omits_intermediate_nonsense() {
    use std::io::Cursor;
    let mut cursor = Cursor::new(Vec::new());

    let opts = Options {
        print_offset: OffsetRadix::None,
        match_length: 3,
        path: "".to_string(),
        zork_mode: false
    };

    let mut bytes : Vec<u8> = Vec::new();
    for b in "Hello".as_bytes() {
        bytes.push(*b);
    }
    bytes.push(254u8);
    bytes.push(254u8);
    for b in "World".as_bytes() {
        bytes.push(*b);
    }
    bytes_to_strings(&bytes, &mut cursor, &opts);
    let expected = "Hello\nWorld\n";
    let vec = cursor.into_inner();
    let actual = String::from_utf8(vec).unwrap();
    assert_eq!(expected, actual);
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::io::{BufReader, BufWriter, ErrorKind};
use std::path::{Path, PathBuf};
use std::process::Command;

use mktemp::Temp;

pub struct Sandbox {
    scratch_dir: Temp,
}

impl Sandbox {
    pub fn new() -> Self {
        Sandbox {
            scratch_dir: Temp::new_dir().expect("Unable to create temp dir"),
        }
    }

    pub fn compile(&self, req: &CompileRequest) -> CompileResponse {
        self.write_source_code(&req.code);

        let mut output_path = self.scratch_dir.as_ref().to_path_buf();
        output_path.push("compiler-output");
        let mut command = self.compile_command(req.target, req.channel, req.mode, req.tests);

        let output = command.output().expect("Failed to run");

        CompileResponse {
            success: output.status.success(),
            code: read(&output_path).unwrap_or_else(String::new),
            stdout: String::from_utf8(output.stdout).expect("Stdout was not UTF-8"),
            stderr: String::from_utf8(output.stderr).expect("Stderr was not UTF-8"),
        }
    }

    pub fn execute(&self, req: &ExecuteRequest) -> ExecuteResponse {
        self.write_source_code(&req.code);
        let mut command = self.execute_command(req.channel, req.mode, req.tests);

        let output = command.output().expect("Failed to run");

        ExecuteResponse {
            success: output.status.success(),
            stdout: String::from_utf8(output.stdout).expect("Stdout was not UTF-8"),
            stderr: String::from_utf8(output.stderr).expect("Stderr was not UTF-8"),
        }
    }

    pub fn format(&self, req: &FormatRequest) -> FormatResponse {
        let path = self.write_source_code(&req.code);
        let mut command = self.format_command();

        let output = command.output().expect("Failed to run");

        FormatResponse {
            success: output.status.success(),
            code: read(path.as_path()).expect("No formatting output"),
            stdout: String::from_utf8(output.stdout).expect("Stdout was not UTF-8"),
            stderr: String::from_utf8(output.stderr).expect("Stderr was not UTF-8"),
        }
    }

    fn write_source_code(&self, code: &str) -> PathBuf {
        let data = code.as_bytes();

        let path = {
            let mut p = self.scratch_dir.to_path_buf();
            p.push("main.rs");
            p
        };

        let file = File::create(&path).expect("Unable to create source code");
        let mut file = BufWriter::new(file);

        file.write_all(data).expect("Unable to write source code");

        debug!("Wrote {} bytes of source to {}", data.len(), path.display());
        path
    }

    fn compile_command(&self, target: CompileTarget, channel: Channel, mode: Mode, tests: bool) -> Command {
        use self::CompileTarget::*;
        use self::Mode::*;

        let mut cmd = self.docker_command();

        let execution_cmd = match (target, mode, tests) {
            (LlvmIr, Debug, false) => r#"rustc main.rs -o compiler-output --emit llvm-ir"#,
            (LlvmIr, Debug, true) => r#"rustc --test main.rs -o compiler-output --emit llvm-ir"#,
            (LlvmIr, Release, false) => r#"rustc -C opt-level=3 main.rs -o compiler-output --emit llvm-ir"#,
            (LlvmIr, Release, true) => r#"rustc -C opt-level=3 --test main.rs -o compiler-output --emit llvm-ir"#,
            (Assembly, Debug, false) => r#"rustc main.rs -o compiler-output --emit asm"#,
            (Assembly, Debug, true) => r#"rustc --test main.rs -o compiler-output --emit asm"#,
            (Assembly, Release, false) => r#"rustc -C opt-level=3 main.rs -o compiler-output --emit asm"#,
            (Assembly, Release, true) => r#"rustc -C opt-level=3 --test main.rs -o compiler-output --emit asm"#,
        };

        cmd.arg(&channel.container_name()).args(&["bash", "-c", execution_cmd]);

        debug!("Compilation command is {:?}", cmd);

        cmd
    }

    fn execute_command(&self, channel: Channel, mode: Mode, tests: bool) -> Command {
        use self::Mode::*;

        let mut cmd = self.docker_command();

        let execution_cmd = match (mode, tests) {
            (Debug, false) => r#"rustc main.rs && ./main"#,
            (Debug, true) => r#"rustc --test main.rs && ./main"#,
            (Release, false) => r#"rustc -C opt-level=3 main.rs && ./main"#,
            (Release, true) => r#"rustc -C opt-level=3 --test main.rs && ./main"#,
        };

        cmd.arg(&channel.container_name()).args(&["bash", "-c", execution_cmd]);

        debug!("Execution command is {:?}", cmd);

        cmd
    }

    fn format_command(&self) -> Command {
        let mut cmd = self.docker_command();

        cmd.arg("rustfmt").args(&["main.rs"]);

        debug!("Formatting command is {:?}", cmd);

        cmd
    }

    fn docker_command(&self) -> Command {
        const DIR_INSIDE_CONTAINER: &'static str = "/source";

        let utf8_dir = self.scratch_dir.as_ref().to_str().expect("Unable to convert directory to UTF-8");
        let mount_source_volume = format!("{}:{}", utf8_dir, DIR_INSIDE_CONTAINER);

        let mut cmd = Command::new("docker");

        cmd
            .arg("run")
            .args(&["--volume", &mount_source_volume])
            .args(&["--workdir", DIR_INSIDE_CONTAINER]);

        cmd
    }
}

fn read(path: &Path) -> Option<String> {
    let f = match File::open(path) {
        Ok(f) => f,
        Err(ref e) if e.kind() == ErrorKind::NotFound => return None,
        Err(e) => panic!("Couldn't open file {}: {}", path.display(), e),
    };
    let mut f = BufReader::new(f);

    let mut s = String::new();
    f.read_to_string(&mut s).expect("Couldn't read");
    Some(s)
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum CompileTarget {
    Assembly,
    LlvmIr,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Channel {
    Stable,
    Beta,
    Nightly,
}

impl Channel {
    fn container_name(&self) -> &'static str {
        use self::Channel::*;

        match *self {
            Stable => "rust-stable",
            Beta => "rust-beta",
            Nightly => "rust-nightly",
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Mode {
    Debug,
    Release,
}

#[derive(Debug, Clone)]
pub struct CompileRequest {
    pub target: CompileTarget,
    pub channel: Channel,
    pub mode: Mode,
    pub tests: bool,
    pub code: String,
}

#[derive(Debug, Clone)]
pub struct CompileResponse {
    pub success: bool,
    pub code: String,
    pub stdout: String,
    pub stderr: String,
}

#[derive(Debug, Clone)]
pub struct ExecuteRequest {
    pub channel: Channel,
    pub mode: Mode,
    pub tests: bool,
    pub code: String,
}

#[derive(Debug, Clone)]
pub struct ExecuteResponse {
    pub success: bool,
    pub stdout: String,
    pub stderr: String,
}

#[derive(Debug, Clone)]
pub struct FormatRequest {
    pub code: String,
}

#[derive(Debug, Clone)]
pub struct FormatResponse {
    pub success: bool,
    pub code: String,
    pub stdout: String,
    pub stderr: String,
}
<commit_msg>DRY up compiler command building<commit_after>use std::fs::File;
use std::io::prelude::*;
use std::io::{BufReader, BufWriter, ErrorKind};
use std::path::{Path, PathBuf};
use std::process::Command;

use mktemp::Temp;

pub struct Sandbox {
    scratch_dir: Temp,
}

impl Sandbox {
    pub fn new() -> Self {
        Sandbox {
            scratch_dir: Temp::new_dir().expect("Unable to create temp dir"),
        }
    }

    pub fn compile(&self, req: &CompileRequest) -> CompileResponse {
        self.write_source_code(&req.code);

        let mut output_path = self.scratch_dir.as_ref().to_path_buf();
        output_path.push("compiler-output");
        let mut command = self.compile_command(req.target, req.channel, req.mode, req.tests);

        let output = command.output().expect("Failed to run");

        CompileResponse {
            success: output.status.success(),
            code: read(&output_path).unwrap_or_else(String::new),
            stdout: String::from_utf8(output.stdout).expect("Stdout was not UTF-8"),
            stderr: String::from_utf8(output.stderr).expect("Stderr was not UTF-8"),
        }
    }

    pub fn execute(&self, req: &ExecuteRequest) -> ExecuteResponse {
        self.write_source_code(&req.code);
        let mut command = self.execute_command(req.channel, req.mode, req.tests);

        let output = command.output().expect("Failed to run");

        ExecuteResponse {
            success: output.status.success(),
            stdout: String::from_utf8(output.stdout).expect("Stdout was not UTF-8"),
            stderr: String::from_utf8(output.stderr).expect("Stderr was not UTF-8"),
        }
    }

    pub fn format(&self, req: &FormatRequest) -> FormatResponse {
        let path = self.write_source_code(&req.code);
        let mut command = self.format_command();

        let output = command.output().expect("Failed to run");

        FormatResponse {
            success: output.status.success(),
            code: read(path.as_path()).expect("No formatting output"),
            stdout: String::from_utf8(output.stdout).expect("Stdout was not UTF-8"),
            stderr: String::from_utf8(output.stderr).expect("Stderr was not UTF-8"),
        }
    }

    fn write_source_code(&self, code: &str) -> PathBuf {
        let data = code.as_bytes();

        let path = {
            let mut p = self.scratch_dir.to_path_buf();
            p.push("main.rs");
            p
        };

        let file = File::create(&path).expect("Unable to create source code");
        let mut file = BufWriter::new(file);

        file.write_all(data).expect("Unable to write source code");

        debug!("Wrote {} bytes of source to {}", data.len(), path.display());
        path
    }

    fn compile_command(&self, target: CompileTarget, channel: Channel, mode: Mode, tests: bool) -> Command {
        let mut cmd = self.docker_command();

        let execution_cmd = build_execution_command(Some((target, "compiler-output")), mode, tests, "main.rs");

        cmd.arg(&channel.container_name()).args(&["bash", "-c", &execution_cmd]);

        debug!("Compilation command is {:?}", cmd);

        cmd
    }

    fn execute_command(&self, channel: Channel, mode: Mode, tests: bool) -> Command {
        let mut cmd = self.docker_command();

        let mut execution_cmd = build_execution_command(None, mode, tests, "main.rs");
        execution_cmd.push_str(" && ./main");

        cmd.arg(&channel.container_name()).args(&["bash", "-c", &execution_cmd]);

        debug!("Execution command is {:?}", cmd);

        cmd
    }

    fn format_command(&self) -> Command {
        let mut cmd = self.docker_command();

        cmd.arg("rustfmt").args(&["main.rs"]);

        debug!("Formatting command is {:?}", cmd);

        cmd
    }

    fn docker_command(&self) -> Command {
        const DIR_INSIDE_CONTAINER: &'static str = "/source";

        let utf8_dir = self.scratch_dir.as_ref().to_str().expect("Unable to convert directory to UTF-8");
        let mount_source_volume = format!("{}:{}", utf8_dir, DIR_INSIDE_CONTAINER);

        let mut cmd = Command::new("docker");

        cmd
            .arg("run")
            .args(&["--volume", &mount_source_volume])
            .args(&["--workdir", DIR_INSIDE_CONTAINER]);

        cmd
    }
}

fn build_execution_command(target: Option<(CompileTarget, &str)>, mode: Mode, tests: bool, source_file: &str) -> String {
    use self::CompileTarget::*;
    use self::Mode::*;

    let mut s = String::from("rustc");

    match mode {
        Debug => s.push_str(" -g"),
        Release => s.push_str(" -C opt-level=3"),
    }

    if tests {
        s.push_str(" --test");
    }

    if let Some((target, filename)) = target {
        match target {
            Assembly => s.push_str(" --emit asm"),
            LlvmIr => s.push_str(" --emit llvm-ir"),
        }
        s.push_str(" -o ");
        s.push_str(filename);
    }

    s.push_str(" ");
    s.push_str(source_file);

    s
}

fn read(path: &Path) -> Option<String> {
    let f = match File::open(path) {
        Ok(f) => f,
        Err(ref e) if e.kind() == ErrorKind::NotFound => return None,
        Err(e) => panic!("Couldn't open file {}: {}", path.display(), e),
    };
    let mut f = BufReader::new(f);

    let mut s = String::new();
    f.read_to_string(&mut s).expect("Couldn't read");
    Some(s)
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum CompileTarget {
    Assembly,
    LlvmIr,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Channel {
    Stable,
    Beta,
    Nightly,
}

impl Channel {
    fn container_name(&self) -> &'static str {
        use self::Channel::*;

        match *self {
            Stable => "rust-stable",
            Beta => "rust-beta",
            Nightly => "rust-nightly",
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Mode {
    Debug,
    Release,
}

#[derive(Debug, Clone)]
pub struct CompileRequest {
    pub target: CompileTarget,
    pub channel: Channel,
    pub mode: Mode,
    pub tests: bool,
    pub code: String,
}

#[derive(Debug, Clone)]
pub struct CompileResponse {
    pub success: bool,
    pub code: String,
    pub stdout: String,
    pub stderr: String,
}

#[derive(Debug, Clone)]
pub struct ExecuteRequest {
    pub channel: Channel,
    pub mode: Mode,
    pub tests: bool,
    pub code: String,
}

#[derive(Debug, Clone)]
pub struct ExecuteResponse {
    pub success: bool,
    pub stdout: String,
    pub stderr: String,
}

#[derive(Debug, Clone)]
pub struct FormatRequest {
    pub code: String,
}

#[derive(Debug, Clone)]
pub struct FormatResponse {
    pub success: bool,
    pub code: String,
    pub stdout: String,
    pub stderr: String,
}
<|endoftext|>


<commit_before>use std::str::FromStr;
use std::collections::HashSet;

use syntax::ast::*;
use syntax::ext::base::{ExtCtxt, Annotatable};
use syntax::codemap::{Span, Spanned, dummy_spanned};
use syntax::parse::token::str_to_ident;

use utils::{span, MetaItemExt, SpanExt};
use super::{Function, ParamIter};
use super::keyvalue::KVSpanned;
use rocket::{Method, ContentType};

/// This structure represents the parsed `route` attribute.
///
/// It contains all of the information supplied by the user and the span where
/// the user supplied the information. This structure can only be obtained by
/// calling the `RouteParams::from` function and passing in the entire decorator
/// environment.
#[derive(Debug)]
pub struct RouteParams {
    pub annotated_fn: Function,
    pub method: Spanned<Method>,
    pub path: Spanned<String>,
    pub form_param: Option<KVSpanned<Ident>>,
    pub query_param: Option<Spanned<Ident>>,
    pub format: Option<KVSpanned<ContentType>>,
    pub rank: Option<KVSpanned<isize>>,
}

impl RouteParams {
    /// Parses the route attribute from the given decorator context. If the
    /// parse is not successful, this function exits early with the appropriate
    /// error message to the user.
    pub fn from(ecx: &mut ExtCtxt,
                sp: Span,
                known_method: Option<Spanned<Method>>,
                meta_item: &MetaItem,
                annotated: &Annotatable)
                -> RouteParams {
        let function = Function::from(annotated).unwrap_or_else(|item_sp| {
            ecx.span_err(sp, "this attribute can only be used on functions...");
            ecx.span_fatal(item_sp, "...but was applied to the item above.");
        });

        let meta_items = meta_item.meta_item_list().unwrap_or_else(|| {
            ecx.struct_span_err(sp, "incorrect use of attribute")
                .help("attributes in Rocket must have the form: #[name(...)]")
                .emit();
            ecx.span_fatal(sp, "malformed attribute");
        });

        if meta_items.len() < 1 {
            ecx.span_fatal(sp, "attribute requires at least 1 parameter");
        }

        // Figure out the method. If it is known (i.e, because we're parsing a
        // helper attribute), use that method directly. Otherwise, try to parse
        // it from the list of meta items.
        let (method, attr_params) = match known_method {
            Some(method) => (method, meta_items),
            None => (parse_method(ecx, &meta_items[0]), &meta_items[1..])
        };

        if attr_params.len() < 1 {
            ecx.struct_span_err(sp, "attribute requires at least a path")
                .help(r#"example: #[get("/my/path")] or #[get(path = "/hi")]"#)
                .emit();
            ecx.span_fatal(sp, "malformed attribute");
        }

        // Parse the required path and optional query parameters.
        let (path, query) = parse_path(ecx, &attr_params[0]);

        // Parse all of the optional parameters.
        let mut seen_keys = HashSet::new();
        let (mut rank, mut form, mut format) = Default::default();
        for param in &attr_params[1..] {
            let kv_opt = kv_from_nested(&param);
            if kv_opt.is_none() {
                ecx.span_err(param.span(), "expected key = value");
                continue;
            }

            let kv = kv_opt.unwrap();
            match kv.key().as_str() {
                "rank" => rank = parse_opt(ecx, &kv, parse_rank),
                "form" => form = parse_opt(ecx, &kv, parse_form),
                "format" => format = parse_opt(ecx, &kv, parse_format),
                _ => {
                    let msg = format!("'{}' is not a known parameter", kv.key());
                    ecx.span_err(kv.span, &msg);
                    continue;
                }
            }

            if seen_keys.contains(kv.key()) {
                let msg = format!("{} was already defined", kv.key());
                ecx.struct_span_warn(param.span, &msg)
                   .note("the last declared value will be used")
                   .emit();
            } else {
                seen_keys.insert(kv.key().clone());
            }
        }

        RouteParams {
            method: method,
            path: path,
            form_param: form,
            query_param: query,
            format: format,
            rank: rank,
            annotated_fn: function,
        }
    }

    pub fn path_params<'s, 'a, 'c: 'a>(&'s self,
                                   ecx: &'a ExtCtxt<'c>)
                                    -> ParamIter<'s, 'a, 'c> {
        ParamIter::new(ecx, self.path.node.as_str(), self.path.span.trim(1))
    }
}

fn is_valid_method(method: Method) -> bool {
    use rocket::Method::*;
    match method {
        Get | Put | Post | Delete | Patch => true,
        _ => false
    }
}

pub fn kv_from_nested(item: &NestedMetaItem) -> Option<KVSpanned<LitKind>> {
    item.name_value().map(|(name, value)| {
        let k_span = item.span().shorten_to(name.len() as u32);
        KVSpanned {
            key: span(name.to_string(), k_span),
            value: value.clone(),
            span: item.span(),
        }
    })
}

fn param_string_to_ident(ecx: &ExtCtxt, s: Spanned<&str>) -> Option<Ident> {
    let string = s.node;
    if string.starts_with('<') && string.ends_with('>') {
        let param = &string[1..(string.len() - 1)];
        if param.chars().all(char::is_alphanumeric) {
            return Some(str_to_ident(param));
        }

        ecx.span_err(s.span, "parameter name must be alphanumeric");
    } else {
        ecx.span_err(s.span, "parameters must start with '<' and end with '>'");
    }

    None
}

fn parse_method(ecx: &ExtCtxt, meta_item: &NestedMetaItem) -> Spanned<Method> {
    if let Some(word) = meta_item.word() {
        if let Ok(method) = Method::from_str(&*word.name()) {
            if is_valid_method(method) {
                return span(method, word.span());
            }
        } else {
            let msg = format!("{} is not a valid HTTP method.", word.name());
            ecx.span_err(word.span(), &msg);
        }
    }

    // Fallthrough. Return default method.
    ecx.struct_span_err(meta_item.span, "expected a valid HTTP method")
        .help("valid methods are: GET, PUT, POST, DELETE, PATCH")
        .emit();

    return dummy_spanned(Method::Get);
}

fn parse_path(ecx: &ExtCtxt, meta_item: &NestedMetaItem) -> (Spanned<String>, Option<Spanned<Ident>>) {
    let from_string = |string: &str, sp: Span| {
        if let Some(q) = string.find('?') {
            let path = span(string[..q].to_string(), sp);
            let q_str = span(&string[(q + 1)..], sp);
            let query = param_string_to_ident(ecx, q_str).map(|i| span(i, sp));
            return (path, query);
        } else {
            return (span(string.to_string(), sp), None)
        }
    };

    let sp = meta_item.span();
    if let Some((name, lit)) = meta_item.name_value() {
        if name != "path" {
            ecx.span_err(sp, "the first key, if any, must be 'path'");
        } else if let LitKind::Str(ref s, _) = lit.node {
            return from_string(s, lit.span);
        } else {
            ecx.span_err(lit.span, "`path` value must be a string")
        }
    } else if let Some(s) = meta_item.str_lit() {
        return from_string(s, sp);
    } else {
        ecx.struct_span_err(sp, r#"expected `path = string` or a path string"#)
            .help(r#"you can specify the path directly as a string, \
                  e.g: "/hello/world", or as a key-value pair, \
                  e.g: path = "/hello/world" "#)
            .emit();
    }

    (dummy_spanned("".to_string()), None)
}

fn parse_opt<O, T, F>(ecx: &ExtCtxt, kv: &KVSpanned<T>, f: F) -> Option<KVSpanned<O>>
    where F: Fn(&ExtCtxt, &KVSpanned<T>) -> O
{
    Some(kv.map_ref(|_| f(ecx, kv)))
}

fn parse_form(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> Ident {
    if let LitKind::Str(ref s, _) = *kv.value() {
        if let Some(ident) = param_string_to_ident(ecx, span(s, kv.value.span)) {
            return ident;
        }
    }

    let err_string = r#"`form` value must be a parameter, e.g: "<name>"`"#;
    ecx.struct_span_fatal(kv.span, err_string)
        .help(r#"form, if specified, must be a key-value pair where
              the key is `form` and the value is a string with a single
              parameter inside '<' '>'. e.g: form = "<login>""#)
        .emit();

    str_to_ident("")
}

fn parse_rank(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> isize {
    if let LitKind::Int(n, _) = *kv.value() {
        let max = isize::max_value();
        if n <= max as u64 {
            return n as isize;
        } else {
            let msg = format!("rank must be less than or equal to {}", max);
            ecx.span_err(kv.value.span, msg.as_str());
        }
    } else {
        ecx.struct_span_err(kv.span, r#"`rank` value must be an int"#)
            .help(r#"the rank, if specified, must be a key-value pair where
                  the key is `rank` and the value is an integer.
                  e.g: rank = 1, or e.g: rank = 10"#)
            .emit();
    }

    -1
}

fn parse_format(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> ContentType {
    if let LitKind::Str(ref s, _) = *kv.value() {
        if let Ok(ct) = ContentType::from_str(s) {
            if ct.is_ext() {
                let msg = format!("'{}' is not a known content-type", s);
                ecx.span_warn(kv.value.span, &msg);
            } else {
                return ct;
            }
        } else {
            ecx.span_err(kv.value.span, "malformed content type");
        }
    }

    ecx.struct_span_err(kv.span, r#"`format` must be a "content/type"`"#)
        .help(r#"format, if specified, must be a key-value pair where
              the key is `format` and the value is a string representing the
              content-type accepted. e.g: format = "application/json""#)
        .emit();

    ContentType::any()
}
<commit_msg>Clean-up method parsing error.<commit_after>use std::str::FromStr;
use std::collections::HashSet;

use syntax::ast::*;
use syntax::ext::base::{ExtCtxt, Annotatable};
use syntax::codemap::{Span, Spanned, dummy_spanned};
use syntax::parse::token::str_to_ident;

use utils::{span, MetaItemExt, SpanExt};
use super::{Function, ParamIter};
use super::keyvalue::KVSpanned;
use rocket::{Method, ContentType};

/// This structure represents the parsed `route` attribute.
///
/// It contains all of the information supplied by the user and the span where
/// the user supplied the information. This structure can only be obtained by
/// calling the `RouteParams::from` function and passing in the entire decorator
/// environment.
#[derive(Debug)]
pub struct RouteParams {
    pub annotated_fn: Function,
    pub method: Spanned<Method>,
    pub path: Spanned<String>,
    pub form_param: Option<KVSpanned<Ident>>,
    pub query_param: Option<Spanned<Ident>>,
    pub format: Option<KVSpanned<ContentType>>,
    pub rank: Option<KVSpanned<isize>>,
}

impl RouteParams {
    /// Parses the route attribute from the given decorator context. If the
    /// parse is not successful, this function exits early with the appropriate
    /// error message to the user.
    pub fn from(ecx: &mut ExtCtxt,
                sp: Span,
                known_method: Option<Spanned<Method>>,
                meta_item: &MetaItem,
                annotated: &Annotatable)
                -> RouteParams {
        let function = Function::from(annotated).unwrap_or_else(|item_sp| {
            ecx.span_err(sp, "this attribute can only be used on functions...");
            ecx.span_fatal(item_sp, "...but was applied to the item above.");
        });

        let meta_items = meta_item.meta_item_list().unwrap_or_else(|| {
            ecx.struct_span_err(sp, "incorrect use of attribute")
                .help("attributes in Rocket must have the form: #[name(...)]")
                .emit();
            ecx.span_fatal(sp, "malformed attribute");
        });

        if meta_items.len() < 1 {
            ecx.span_fatal(sp, "attribute requires at least 1 parameter");
        }

        // Figure out the method. If it is known (i.e, because we're parsing a
        // helper attribute), use that method directly. Otherwise, try to parse
        // it from the list of meta items.
        let (method, attr_params) = match known_method {
            Some(method) => (method, meta_items),
            None => (parse_method(ecx, &meta_items[0]), &meta_items[1..])
        };

        if attr_params.len() < 1 {
            ecx.struct_span_err(sp, "attribute requires at least a path")
                .help(r#"example: #[get("/my/path")] or #[get(path = "/hi")]"#)
                .emit();
            ecx.span_fatal(sp, "malformed attribute");
        }

        // Parse the required path and optional query parameters.
        let (path, query) = parse_path(ecx, &attr_params[0]);

        // Parse all of the optional parameters.
        let mut seen_keys = HashSet::new();
        let (mut rank, mut form, mut format) = Default::default();
        for param in &attr_params[1..] {
            let kv_opt = kv_from_nested(&param);
            if kv_opt.is_none() {
                ecx.span_err(param.span(), "expected key = value");
                continue;
            }

            let kv = kv_opt.unwrap();
            match kv.key().as_str() {
                "rank" => rank = parse_opt(ecx, &kv, parse_rank),
                "form" => form = parse_opt(ecx, &kv, parse_form),
                "format" => format = parse_opt(ecx, &kv, parse_format),
                _ => {
                    let msg = format!("'{}' is not a known parameter", kv.key());
                    ecx.span_err(kv.span, &msg);
                    continue;
                }
            }

            if seen_keys.contains(kv.key()) {
                let msg = format!("{} was already defined", kv.key());
                ecx.struct_span_warn(param.span, &msg)
                   .note("the last declared value will be used")
                   .emit();
            } else {
                seen_keys.insert(kv.key().clone());
            }
        }

        RouteParams {
            method: method,
            path: path,
            form_param: form,
            query_param: query,
            format: format,
            rank: rank,
            annotated_fn: function,
        }
    }

    pub fn path_params<'s, 'a, 'c: 'a>(&'s self,
                                   ecx: &'a ExtCtxt<'c>)
                                    -> ParamIter<'s, 'a, 'c> {
        ParamIter::new(ecx, self.path.node.as_str(), self.path.span.trim(1))
    }
}

fn is_valid_method(method: Method) -> bool {
    use rocket::Method::*;
    match method {
        Get | Put | Post | Delete | Patch => true,
        _ => false
    }
}

pub fn kv_from_nested(item: &NestedMetaItem) -> Option<KVSpanned<LitKind>> {
    item.name_value().map(|(name, value)| {
        let k_span = item.span().shorten_to(name.len() as u32);
        KVSpanned {
            key: span(name.to_string(), k_span),
            value: value.clone(),
            span: item.span(),
        }
    })
}

fn param_string_to_ident(ecx: &ExtCtxt, s: Spanned<&str>) -> Option<Ident> {
    let string = s.node;
    if string.starts_with('<') && string.ends_with('>') {
        let param = &string[1..(string.len() - 1)];
        if param.chars().all(char::is_alphanumeric) {
            return Some(str_to_ident(param));
        }

        ecx.span_err(s.span, "parameter name must be alphanumeric");
    } else {
        ecx.span_err(s.span, "parameters must start with '<' and end with '>'");
    }

    None
}

fn parse_method(ecx: &ExtCtxt, meta_item: &NestedMetaItem) -> Spanned<Method> {
    if let Some(word) = meta_item.word() {
        if let Ok(method) = Method::from_str(&*word.name()) {
            if is_valid_method(method) {
                return span(method, word.span());
            }
        } else {
            let msg = format!("'{}' is not a valid HTTP method.", word.name());
            ecx.span_err(word.span(), &msg);
        }
    }

    // Fallthrough. Return default method.
    ecx.struct_span_err(meta_item.span, "expected a valid HTTP method")
        .help("valid methods are: GET, PUT, POST, DELETE, PATCH")
        .emit();

    return dummy_spanned(Method::Get);
}

fn parse_path(ecx: &ExtCtxt, meta_item: &NestedMetaItem) -> (Spanned<String>, Option<Spanned<Ident>>) {
    let from_string = |string: &str, sp: Span| {
        if let Some(q) = string.find('?') {
            let path = span(string[..q].to_string(), sp);
            let q_str = span(&string[(q + 1)..], sp);
            let query = param_string_to_ident(ecx, q_str).map(|i| span(i, sp));
            return (path, query);
        } else {
            return (span(string.to_string(), sp), None)
        }
    };

    let sp = meta_item.span();
    if let Some((name, lit)) = meta_item.name_value() {
        if name != "path" {
            ecx.span_err(sp, "the first key, if any, must be 'path'");
        } else if let LitKind::Str(ref s, _) = lit.node {
            return from_string(s, lit.span);
        } else {
            ecx.span_err(lit.span, "`path` value must be a string")
        }
    } else if let Some(s) = meta_item.str_lit() {
        return from_string(s, sp);
    } else {
        ecx.struct_span_err(sp, r#"expected `path = string` or a path string"#)
            .help(r#"you can specify the path directly as a string, \
                  e.g: "/hello/world", or as a key-value pair, \
                  e.g: path = "/hello/world" "#)
            .emit();
    }

    (dummy_spanned("".to_string()), None)
}

fn parse_opt<O, T, F>(ecx: &ExtCtxt, kv: &KVSpanned<T>, f: F) -> Option<KVSpanned<O>>
    where F: Fn(&ExtCtxt, &KVSpanned<T>) -> O
{
    Some(kv.map_ref(|_| f(ecx, kv)))
}

fn parse_form(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> Ident {
    if let LitKind::Str(ref s, _) = *kv.value() {
        if let Some(ident) = param_string_to_ident(ecx, span(s, kv.value.span)) {
            return ident;
        }
    }

    let err_string = r#"`form` value must be a parameter, e.g: "<name>"`"#;
    ecx.struct_span_fatal(kv.span, err_string)
        .help(r#"form, if specified, must be a key-value pair where
              the key is `form` and the value is a string with a single
              parameter inside '<' '>'. e.g: form = "<login>""#)
        .emit();

    str_to_ident("")
}

fn parse_rank(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> isize {
    if let LitKind::Int(n, _) = *kv.value() {
        let max = isize::max_value();
        if n <= max as u64 {
            return n as isize;
        } else {
            let msg = format!("rank must be less than or equal to {}", max);
            ecx.span_err(kv.value.span, msg.as_str());
        }
    } else {
        ecx.struct_span_err(kv.span, r#"`rank` value must be an int"#)
            .help(r#"the rank, if specified, must be a key-value pair where
                  the key is `rank` and the value is an integer.
                  e.g: rank = 1, or e.g: rank = 10"#)
            .emit();
    }

    -1
}

fn parse_format(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> ContentType {
    if let LitKind::Str(ref s, _) = *kv.value() {
        if let Ok(ct) = ContentType::from_str(s) {
            if ct.is_ext() {
                let msg = format!("'{}' is not a known content-type", s);
                ecx.span_warn(kv.value.span, &msg);
            } else {
                return ct;
            }
        } else {
            ecx.span_err(kv.value.span, "malformed content type");
        }
    }

    ecx.struct_span_err(kv.span, r#"`format` must be a "content/type"`"#)
        .help(r#"format, if specified, must be a key-value pair where
              the key is `format` and the value is a string representing the
              content-type accepted. e.g: format = "application/json""#)
        .emit();

    ContentType::any()
}
<|endoftext|>


<commit_before>use json;
use std::ops;

use {Document};

pub use json::buffer::Target;

/// A buffer points to binary data representing geometry, animations, or skins.
#[derive(Clone, Debug)]
pub struct Buffer<'a> {
    /// The parent `Document` struct.
    document: &'a Document,

    /// The corresponding JSON index.
    index: usize,

    /// The corresponding JSON struct.
    json: &'a json::buffer::Buffer,
}

/// A view into a buffer generally representing a subset of the buffer.
#[derive(Clone, Debug)]
pub struct View<'a> {
    /// The parent `Document` struct.
    document: &'a Document,

    /// The corresponding JSON index.
    index: usize,

    /// The corresponding JSON struct.
    json: &'a json::buffer::View,

    /// The parent `Buffer`.
    parent: Buffer<'a>,
}

/// Describes a buffer data source.
#[derive(Clone, Debug)]
pub enum Source<'a> {
    /// Buffer data is contained in the `BIN` section of binary glTF.
    Bin,

    /// Buffer data is contained in an external data source.
    Uri(&'a str),
}

/// Buffer data belonging to an imported glTF asset.
#[cfg(feature = "import")]
#[derive(Clone, Debug)]
pub struct Data(pub Vec<u8>);

#[cfg(feature = "import")]
impl ops::Deref for Data {
    type Target = [u8];
    fn deref(&self) -> &Self::Target {
        self.0.as_slice()
    }
}

impl<'a> Buffer<'a> {
    /// Constructs a `Buffer`.
    pub(crate) fn new(
        document: &'a Document,
        index: usize,
        json: &'a json::buffer::Buffer,
    ) -> Self {
        Self {
            document: document,
            index: index,
            json: json,
        }
    }

    /// Returns the internal JSON index.
    pub fn index(&self) -> usize {
        self.index
    }

    /// Returns the buffer data source.
    pub fn source(&self) -> Source<'a> {
        if let Some(uri) = self.json.uri.as_ref().map(String::as_str) {
            Source::Uri(uri)
        } else {
            Source::Bin
        }
    }

    /// The length of the buffer in bytes.
    pub fn length(&self) -> usize {
        self.json.byte_length as usize
    }

    /// Optional user-defined name for this object.
    #[cfg(feature = "names")]
    pub fn name(&self) -> Option<&str> {
        self.json.name.as_ref().map(String::as_str)
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras {
        &self.json.extras
    }
}

impl<'a> View<'a> {
    /// Constructs a `View`.
    pub(crate) fn new(
        document: &'a Document,
        index: usize,
        json: &'a json::buffer::View,
    ) -> Self {
        let parent = document.buffers().nth(json.buffer.value()).unwrap();
        Self {
            document,
            index,
            json,
            parent,
        }
    }

    /// Returns the internal JSON index.
    pub fn index(&self) -> usize {
        self.index
    }

    /// Returns the parent `Buffer`.
    pub fn buffer(&self) -> Buffer<'a> {
        self.document.buffers().nth(self.json.buffer.value()).unwrap()
    }

    /// Returns the length of the buffer view in bytes.
    pub fn length(&self) -> usize {
        self.json.byte_length as usize
    }

    /// Returns the offset into the parent buffer in bytes.
    pub fn offset(&self) -> usize {
        self.json.byte_offset as usize
    }

    /// Returns the stride in bytes between vertex attributes or other interleavable
    /// data. When `None`, data is assumed to be tightly packed.
    pub fn stride(&self) -> Option<usize> {
        self.json.byte_stride.map(|x| x.0 as usize)
    }

    /// Optional user-defined name for this object.
    #[cfg(feature = "names")]
    pub fn name(&self) -> Option<&str> {
        self.json.name.as_ref().map(String::as_str)
    }

    /// Optional target the buffer should be bound to.
    pub fn target(&self) -> Option<Target> {
        self.json.target.map(|target| target.unwrap())
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras {
        &self.json.extras
    }
}
<commit_msg>Fix build warning without import feature<commit_after>use json;

#[cfg(feature = "import")]
use std::ops;

use {Document};

pub use json::buffer::Target;

/// A buffer points to binary data representing geometry, animations, or skins.
#[derive(Clone, Debug)]
pub struct Buffer<'a> {
    /// The parent `Document` struct.
    document: &'a Document,

    /// The corresponding JSON index.
    index: usize,

    /// The corresponding JSON struct.
    json: &'a json::buffer::Buffer,
}

/// A view into a buffer generally representing a subset of the buffer.
#[derive(Clone, Debug)]
pub struct View<'a> {
    /// The parent `Document` struct.
    document: &'a Document,

    /// The corresponding JSON index.
    index: usize,

    /// The corresponding JSON struct.
    json: &'a json::buffer::View,

    /// The parent `Buffer`.
    parent: Buffer<'a>,
}

/// Describes a buffer data source.
#[derive(Clone, Debug)]
pub enum Source<'a> {
    /// Buffer data is contained in the `BIN` section of binary glTF.
    Bin,

    /// Buffer data is contained in an external data source.
    Uri(&'a str),
}

/// Buffer data belonging to an imported glTF asset.
#[cfg(feature = "import")]
#[derive(Clone, Debug)]
pub struct Data(pub Vec<u8>);

#[cfg(feature = "import")]
impl ops::Deref for Data {
    type Target = [u8];
    fn deref(&self) -> &Self::Target {
        self.0.as_slice()
    }
}

impl<'a> Buffer<'a> {
    /// Constructs a `Buffer`.
    pub(crate) fn new(
        document: &'a Document,
        index: usize,
        json: &'a json::buffer::Buffer,
    ) -> Self {
        Self {
            document: document,
            index: index,
            json: json,
        }
    }

    /// Returns the internal JSON index.
    pub fn index(&self) -> usize {
        self.index
    }

    /// Returns the buffer data source.
    pub fn source(&self) -> Source<'a> {
        if let Some(uri) = self.json.uri.as_ref().map(String::as_str) {
            Source::Uri(uri)
        } else {
            Source::Bin
        }
    }

    /// The length of the buffer in bytes.
    pub fn length(&self) -> usize {
        self.json.byte_length as usize
    }

    /// Optional user-defined name for this object.
    #[cfg(feature = "names")]
    pub fn name(&self) -> Option<&str> {
        self.json.name.as_ref().map(String::as_str)
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras {
        &self.json.extras
    }
}

impl<'a> View<'a> {
    /// Constructs a `View`.
    pub(crate) fn new(
        document: &'a Document,
        index: usize,
        json: &'a json::buffer::View,
    ) -> Self {
        let parent = document.buffers().nth(json.buffer.value()).unwrap();
        Self {
            document,
            index,
            json,
            parent,
        }
    }

    /// Returns the internal JSON index.
    pub fn index(&self) -> usize {
        self.index
    }

    /// Returns the parent `Buffer`.
    pub fn buffer(&self) -> Buffer<'a> {
        self.document.buffers().nth(self.json.buffer.value()).unwrap()
    }

    /// Returns the length of the buffer view in bytes.
    pub fn length(&self) -> usize {
        self.json.byte_length as usize
    }

    /// Returns the offset into the parent buffer in bytes.
    pub fn offset(&self) -> usize {
        self.json.byte_offset as usize
    }

    /// Returns the stride in bytes between vertex attributes or other interleavable
    /// data. When `None`, data is assumed to be tightly packed.
    pub fn stride(&self) -> Option<usize> {
        self.json.byte_stride.map(|x| x.0 as usize)
    }

    /// Optional user-defined name for this object.
    #[cfg(feature = "names")]
    pub fn name(&self) -> Option<&str> {
        self.json.name.as_ref().map(String::as_str)
    }

    /// Optional target the buffer should be bound to.
    pub fn target(&self) -> Option<Target> {
        self.json.target.map(|target| target.unwrap())
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras {
        &self.json.extras
    }
}
<|endoftext|>


<commit_before>use hb;
use std;

use font::Font;
use common::{HarfbuzzObject, Tag};

pub type GlyphPosition = hb::hb_glyph_position_t;
pub type GlyphInfo = hb::hb_glyph_info_t;
pub type Feature = hb::hb_feature_t;

fn language_to_string(tag: hb::hb_language_t) -> &'static str {
    let lang_string_ptr = unsafe { hb::hb_language_to_string(tag) };
    let cstring = unsafe { std::ffi::CStr::from_ptr(lang_string_ptr) };
    cstring.to_str().expect("harfbuzz error: language string is not valid utf-8!")
}

struct BufferRaw {
    hb_buffer: *mut hb::hb_buffer_t,
}
impl BufferRaw {
    fn new() -> BufferRaw {
        let buffer = unsafe { hb::hb_buffer_create() };

        BufferRaw { hb_buffer: buffer }
    }

    fn as_raw(&self) -> *mut hb::hb_buffer_t {
        self.hb_buffer
    }

    fn len(&self) -> usize {
        unsafe { hb::hb_buffer_get_length(self.hb_buffer) as usize }
    }

    fn is_empty(&self) -> bool {
        self.len() == 0
    }

    fn add_str(&mut self, string: &str) {
        let utf8_ptr = string.as_ptr() as *const i8;
        unsafe {
            hb::hb_buffer_add_utf8(self.hb_buffer,
                                   utf8_ptr,
                                   string.len() as i32,
                                   0,
                                   string.len() as i32);
        }
    }

    fn set_direction(&mut self, direction: hb::hb_direction_t) {
        unsafe { hb::hb_buffer_set_direction(self.hb_buffer, direction) };
    }

    /// Returns the `Buffer`'s text direction.
    fn get_direction(&self) -> hb::hb_direction_t {
        unsafe { hb::hb_buffer_get_direction(self.hb_buffer) }
    }

    fn set_language(&mut self, lang: hb::hb_language_t) {
        unsafe { hb::hb_buffer_set_language(self.hb_buffer, lang) }
    }

    fn get_language(&self) -> &'static str {
        let lang = unsafe { hb::hb_buffer_get_language(self.hb_buffer) };
        language_to_string(lang)
    }

    fn set_script(&mut self, script: hb::hb_script_t) {
        unsafe { hb::hb_buffer_set_script(self.hb_buffer, script) }
    }

    fn get_script(&self) -> hb::hb_script_t {
        unsafe { hb::hb_buffer_get_script(self.hb_buffer) }
    }

    fn guess_segment_properties(&mut self) {
        unsafe { hb::hb_buffer_guess_segment_properties(self.hb_buffer) };
    }

    fn get_segment_properties(&self) -> hb::hb_segment_properties_t {
        unsafe {
            let mut segment_props: hb::hb_segment_properties_t = std::mem::uninitialized();
            hb::hb_buffer_get_segment_properties(self.hb_buffer, &mut segment_props as *mut _);
            segment_props
        }
    }

    fn shape(&mut self, font: &Font, features: &[Feature]) {
        unsafe {
            hb::hb_shape(font.as_raw(),
                         self.hb_buffer,
                         features.as_ptr(),
                         features.len() as u32)
        };
    }

    fn clear_contents(&mut self) {
        unsafe { hb::hb_buffer_clear_contents(self.hb_buffer) };
    }

    fn get_glyph_positions(&self) -> &mut [GlyphPosition] {
        unsafe {
            let mut length: u32 = 0;
            let glyph_pos = hb::hb_buffer_get_glyph_positions(self.hb_buffer,
                                                              &mut length as *mut u32);
            std::slice::from_raw_parts_mut(glyph_pos, length as usize)
        }
    }

    fn get_glyph_infos(&self) -> &mut [GlyphInfo] {
        unsafe {
            let mut length: u32 = 0;
            let glyph_infos = hb::hb_buffer_get_glyph_infos(self.hb_buffer,
                                                            &mut length as *mut u32);
            std::slice::from_raw_parts_mut(glyph_infos, length as usize)
        }
    }

    /// Reverse the `Buffer`'s contents.
    fn reverse(&mut self) {
        unsafe { hb::hb_buffer_reverse(self.hb_buffer) };
    }

    /// Reverse the `Buffer`'s contents in the range from `start` to `end`.
    fn reverse_range(&mut self, start: usize, end: usize) {
        assert!(start <= self.len(), end <= self.len());
        unsafe {
            hb::hb_buffer_reverse_range(self.hb_buffer, start as u32, end as u32)
        }
    }
}

impl Clone for BufferRaw {
    fn clone(&self) -> Self {
        unsafe {
            hb::hb_buffer_reference(self.hb_buffer);
        }
        BufferRaw { hb_buffer: self.hb_buffer }
    }
}

impl Drop for BufferRaw {
    fn drop(&mut self) {
        unsafe {
            hb::hb_buffer_destroy(self.hb_buffer);
        }
    }
}

/// A `UnicodeBuffer` can be filled with unicode text and corresponding
#[derive(Clone)]
pub struct UnicodeBuffer(BufferRaw);
#[allow(dead_code)]
impl UnicodeBuffer {
    /// Creates a new empty `Buffer`.
    pub fn new() -> UnicodeBuffer {
        UnicodeBuffer(BufferRaw::new())
    }

    /// Returns a pointer to the underlying raw harfbuzz buffer.
    pub fn as_raw(&self) -> *mut hb::hb_buffer_t {
        self.0.as_raw()
    }

    /// Returns the length of the data of the buffer.
    ///
    /// When called before shaping this is the number of unicode codepoints contained in the
    /// buffer. When called after shaping it returns the number of glyphs stored.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Returns `true` if the buffer contains no elements.
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    /// Adds the string slice to the `Buffer`'s array of codepoints.
    pub fn add_str(mut self, string: &str) -> UnicodeBuffer {
        self.0.add_str(string);
        self
    }

    fn get_string(&self) -> String {
        let mut result = String::with_capacity(self.len());
        for info in self.0.get_glyph_infos() {
            let chr = std::char::from_u32(info.codepoint).unwrap();
            result.push(chr)
        }
        result
    }

    /// Sets the text direction of the `Buffer`'s contents.
    pub fn set_direction(mut self, direction: hb::hb_direction_t) -> UnicodeBuffer {
        self.0.set_direction(direction);
        self
    }

    /// Returns the `Buffer`'s text direction.
    pub fn get_direction(&self) -> hb::hb_direction_t {
        self.0.get_direction()
    }

    pub fn set_script(mut self, script: Tag) -> UnicodeBuffer {
        self.0.set_script(script.0);
        self
    }

    pub fn get_script(&self) -> Tag {
        Tag(self.0.get_script())
    }

    pub fn get_language(&self) -> &'static str {
        self.0.get_language()
    }

    pub fn guess_segment_properties(mut self) -> UnicodeBuffer {
        self.0.guess_segment_properties();
        self
    }

    pub fn get_segment_properties(&self) -> hb::hb_segment_properties_t {
        self.0.get_segment_properties()
    }

    pub fn shape(mut self, font: &Font, features: &[Feature]) -> GlyphBuffer {
        self = self.guess_segment_properties();
        self.0.shape(font, features);
        GlyphBuffer(self.0)
    }

    pub fn clear_contents(mut self) -> UnicodeBuffer {
        self.0.clear_contents();
        self
    }
}

impl std::fmt::Debug for UnicodeBuffer {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        fmt.debug_struct("Buffer")
            .field("content", &self.get_string())
            .field("direction", &self.get_direction())
            .field("language", &self.get_language().to_owned())
            .finish()
    }
}

impl std::default::Default for UnicodeBuffer {
    fn default() -> UnicodeBuffer {
        UnicodeBuffer::new()
    }
}

/// A `GlyphBuffer` is obtained through the `shape` function of a `UnicodeBuffer`. It contains
/// the resulting output information of the shaping process.
#[derive(Clone)]
pub struct GlyphBuffer(BufferRaw);

impl GlyphBuffer {
    /// Returns the length of the data of the buffer.
    ///
    /// When called before shaping this is the number of unicode codepoints contained in the
    /// buffer. When called after shaping it returns the number of glyphs stored.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Returns `true` if the buffer contains no elements.
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    pub fn get_glyph_positions(&self) -> &mut [GlyphPosition] {
        self.0.get_glyph_positions()
    }

    pub fn get_glyph_infos(&self) -> &mut [GlyphInfo] {
        self.0.get_glyph_infos()
    }

    /// Reverse the `Buffer`'s contents.
    pub fn reverse(&mut self) {
        self.0.reverse()
    }

    /// Reverse the `Buffer`'s contents in the range from `start` to `end`.
    pub fn reverse_range(&mut self, start: usize, end: usize) {
        self.0.reverse_range(start, end)
    }

    /// Clears the contents of the glyph buffer and returns an empty `UnicodeBuffer` reusing the
    /// existing allocation.
    pub fn clear(mut self) -> UnicodeBuffer {
        self.0.clear_contents();
        UnicodeBuffer(self.0)
    }
}

impl std::fmt::Debug for GlyphBuffer {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        fmt.debug_struct("Buffer")
            .field("glyph positions", &self.get_glyph_positions())
            .field("glyph infos", &self.get_glyph_infos())
            .finish()
    }
}

#[derive(Debug, Clone)]
pub enum Buffer {
    UnicodeBuffer(UnicodeBuffer),
    GlyphBuffer(GlyphBuffer),
}
<commit_msg>Add as_raw implementation to 'GlyphBuffer'.<commit_after>use hb;
use std;

use font::Font;
use common::{HarfbuzzObject, Tag};

pub type GlyphPosition = hb::hb_glyph_position_t;
pub type GlyphInfo = hb::hb_glyph_info_t;
pub type Feature = hb::hb_feature_t;

fn language_to_string(tag: hb::hb_language_t) -> &'static str {
    let lang_string_ptr = unsafe { hb::hb_language_to_string(tag) };
    let cstring = unsafe { std::ffi::CStr::from_ptr(lang_string_ptr) };
    cstring.to_str().expect("harfbuzz error: language string is not valid utf-8!")
}

struct BufferRaw {
    hb_buffer: *mut hb::hb_buffer_t,
}
impl BufferRaw {
    fn new() -> BufferRaw {
        let buffer = unsafe { hb::hb_buffer_create() };

        BufferRaw { hb_buffer: buffer }
    }

    fn as_raw(&self) -> *mut hb::hb_buffer_t {
        self.hb_buffer
    }

    fn len(&self) -> usize {
        unsafe { hb::hb_buffer_get_length(self.hb_buffer) as usize }
    }

    fn is_empty(&self) -> bool {
        self.len() == 0
    }

    fn add_str(&mut self, string: &str) {
        let utf8_ptr = string.as_ptr() as *const i8;
        unsafe {
            hb::hb_buffer_add_utf8(self.hb_buffer,
                                   utf8_ptr,
                                   string.len() as i32,
                                   0,
                                   string.len() as i32);
        }
    }

    fn set_direction(&mut self, direction: hb::hb_direction_t) {
        unsafe { hb::hb_buffer_set_direction(self.hb_buffer, direction) };
    }

    /// Returns the `Buffer`'s text direction.
    fn get_direction(&self) -> hb::hb_direction_t {
        unsafe { hb::hb_buffer_get_direction(self.hb_buffer) }
    }

    fn set_language(&mut self, lang: hb::hb_language_t) {
        unsafe { hb::hb_buffer_set_language(self.hb_buffer, lang) }
    }

    fn get_language(&self) -> &'static str {
        let lang = unsafe { hb::hb_buffer_get_language(self.hb_buffer) };
        language_to_string(lang)
    }

    fn set_script(&mut self, script: hb::hb_script_t) {
        unsafe { hb::hb_buffer_set_script(self.hb_buffer, script) }
    }

    fn get_script(&self) -> hb::hb_script_t {
        unsafe { hb::hb_buffer_get_script(self.hb_buffer) }
    }

    fn guess_segment_properties(&mut self) {
        unsafe { hb::hb_buffer_guess_segment_properties(self.hb_buffer) };
    }

    fn get_segment_properties(&self) -> hb::hb_segment_properties_t {
        unsafe {
            let mut segment_props: hb::hb_segment_properties_t = std::mem::uninitialized();
            hb::hb_buffer_get_segment_properties(self.hb_buffer, &mut segment_props as *mut _);
            segment_props
        }
    }

    fn shape(&mut self, font: &Font, features: &[Feature]) {
        unsafe {
            hb::hb_shape(font.as_raw(),
                         self.hb_buffer,
                         features.as_ptr(),
                         features.len() as u32)
        };
    }

    fn clear_contents(&mut self) {
        unsafe { hb::hb_buffer_clear_contents(self.hb_buffer) };
    }

    fn get_glyph_positions(&self) -> &mut [GlyphPosition] {
        unsafe {
            let mut length: u32 = 0;
            let glyph_pos = hb::hb_buffer_get_glyph_positions(self.hb_buffer,
                                                              &mut length as *mut u32);
            std::slice::from_raw_parts_mut(glyph_pos, length as usize)
        }
    }

    fn get_glyph_infos(&self) -> &mut [GlyphInfo] {
        unsafe {
            let mut length: u32 = 0;
            let glyph_infos = hb::hb_buffer_get_glyph_infos(self.hb_buffer,
                                                            &mut length as *mut u32);
            std::slice::from_raw_parts_mut(glyph_infos, length as usize)
        }
    }

    /// Reverse the `Buffer`'s contents.
    fn reverse(&mut self) {
        unsafe { hb::hb_buffer_reverse(self.hb_buffer) };
    }

    /// Reverse the `Buffer`'s contents in the range from `start` to `end`.
    fn reverse_range(&mut self, start: usize, end: usize) {
        assert!(start <= self.len(), end <= self.len());
        unsafe {
            hb::hb_buffer_reverse_range(self.hb_buffer, start as u32, end as u32)
        }
    }
}

impl Clone for BufferRaw {
    fn clone(&self) -> Self {
        unsafe {
            hb::hb_buffer_reference(self.hb_buffer);
        }
        BufferRaw { hb_buffer: self.hb_buffer }
    }
}

impl Drop for BufferRaw {
    fn drop(&mut self) {
        unsafe {
            hb::hb_buffer_destroy(self.hb_buffer);
        }
    }
}

/// A `UnicodeBuffer` can be filled with unicode text and corresponding
#[derive(Clone)]
pub struct UnicodeBuffer(BufferRaw);
#[allow(dead_code)]
impl UnicodeBuffer {
    /// Creates a new empty `Buffer`.
    pub fn new() -> UnicodeBuffer {
        UnicodeBuffer(BufferRaw::new())
    }

    /// Returns a pointer to the underlying raw harfbuzz buffer.
    pub fn as_raw(&self) -> *mut hb::hb_buffer_t {
        self.0.as_raw()
    }

    /// Returns the length of the data of the buffer.
    ///
    /// When called before shaping this is the number of unicode codepoints contained in the
    /// buffer. When called after shaping it returns the number of glyphs stored.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Returns `true` if the buffer contains no elements.
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    /// Adds the string slice to the `Buffer`'s array of codepoints.
    pub fn add_str(mut self, string: &str) -> UnicodeBuffer {
        self.0.add_str(string);
        self
    }

    fn get_string(&self) -> String {
        let mut result = String::with_capacity(self.len());
        for info in self.0.get_glyph_infos() {
            let chr = std::char::from_u32(info.codepoint).unwrap();
            result.push(chr)
        }
        result
    }

    /// Sets the text direction of the `Buffer`'s contents.
    pub fn set_direction(mut self, direction: hb::hb_direction_t) -> UnicodeBuffer {
        self.0.set_direction(direction);
        self
    }

    /// Returns the `Buffer`'s text direction.
    pub fn get_direction(&self) -> hb::hb_direction_t {
        self.0.get_direction()
    }

    pub fn set_script(mut self, script: Tag) -> UnicodeBuffer {
        self.0.set_script(script.0);
        self
    }

    pub fn get_script(&self) -> Tag {
        Tag(self.0.get_script())
    }

    pub fn get_language(&self) -> &'static str {
        self.0.get_language()
    }

    pub fn guess_segment_properties(mut self) -> UnicodeBuffer {
        self.0.guess_segment_properties();
        self
    }

    pub fn get_segment_properties(&self) -> hb::hb_segment_properties_t {
        self.0.get_segment_properties()
    }

    pub fn shape(mut self, font: &Font, features: &[Feature]) -> GlyphBuffer {
        self = self.guess_segment_properties();
        self.0.shape(font, features);
        GlyphBuffer(self.0)
    }

    pub fn clear_contents(mut self) -> UnicodeBuffer {
        self.0.clear_contents();
        self
    }
}

impl std::fmt::Debug for UnicodeBuffer {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        fmt.debug_struct("Buffer")
            .field("content", &self.get_string())
            .field("direction", &self.get_direction())
            .field("language", &self.get_language().to_owned())
            .finish()
    }
}

impl std::default::Default for UnicodeBuffer {
    fn default() -> UnicodeBuffer {
        UnicodeBuffer::new()
    }
}

/// A `GlyphBuffer` is obtained through the `shape` function of a `UnicodeBuffer`. It contains
/// the resulting output information of the shaping process.
#[derive(Clone)]
pub struct GlyphBuffer(BufferRaw);

impl GlyphBuffer {
    /// Returns the length of the data of the buffer.
    ///
    /// When called before shaping this is the number of unicode codepoints contained in the
    /// buffer. When called after shaping it returns the number of glyphs stored.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Returns a pointer to the underlying raw harfbuzz buffer.
    pub fn as_raw(&self) -> *mut hb::hb_buffer_t {
        self.0.as_raw()
    }

    /// Returns `true` if the buffer contains no elements.
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    pub fn get_glyph_positions(&self) -> &mut [GlyphPosition] {
        self.0.get_glyph_positions()
    }

    pub fn get_glyph_infos(&self) -> &mut [GlyphInfo] {
        self.0.get_glyph_infos()
    }

    /// Reverse the `Buffer`'s contents.
    pub fn reverse(&mut self) {
        self.0.reverse()
    }

    /// Reverse the `Buffer`'s contents in the range from `start` to `end`.
    pub fn reverse_range(&mut self, start: usize, end: usize) {
        self.0.reverse_range(start, end)
    }

    /// Clears the contents of the glyph buffer and returns an empty `UnicodeBuffer` reusing the
    /// existing allocation.
    pub fn clear(mut self) -> UnicodeBuffer {
        self.0.clear_contents();
        UnicodeBuffer(self.0)
    }
}

impl std::fmt::Debug for GlyphBuffer {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        fmt.debug_struct("Buffer")
            .field("glyph positions", &self.get_glyph_positions())
            .field("glyph infos", &self.get_glyph_infos())
            .finish()
    }
}

#[derive(Debug, Clone)]
pub enum Buffer {
    UnicodeBuffer(UnicodeBuffer),
    GlyphBuffer(GlyphBuffer),
}
<|endoftext|>


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

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use fleetspeak_proto::common::{Message, Address};
use fleetspeak_proto::channel::{StartupData};
use lazy_static::lazy_static;
use prost;
use prost_types;
use std::io::{Read, Write, Result};
use std::marker::{Send, Sync};
use std::sync::Mutex;

pub struct Connection<R, W> {
    pub input: R,
    pub output: W,
}

impl<R: Read, W: Write> Connection<R, W> {

    pub fn new(input: R, output: W) -> Result<Self> {
        let mut conn = Connection {
            input: input,
            output: output,
        };
        conn.handshake()?;

        Ok(conn)
    }

    pub fn heartbeat(&mut self) -> Result<()> {
        let msg = Message {
            message_type: "Heartbeat".to_string(),
            destination: Some(Address {
                service_name: "system".to_string(),
                ..Default::default()
            }),
            ..Default::default()
        };

        self.emit(msg)
    }

    pub fn startup(&mut self, version: &str) -> Result<()> {
        let data = StartupData {
            pid: std::process::id() as i64,
            version: version.to_string(),
        };

        self.send("system", "StartupData", data)
    }

    pub fn send<M>(&mut self, service: &str, kind: &str, data: M) -> Result<()>
    where
        M: prost::Message,
    {
        let mut buf = Vec::new();
        prost::Message::encode(&data, &mut buf).map_err(invalid_data_error)?;

        let msg = Message {
            message_type: kind.to_string(),
            destination: Some(Address {
                service_name: service.to_string(),
                ..Default::default()
            }),
            data: Some(prost_types::Any {
                value: buf.to_vec(),
                ..Default::default()
            }),
            ..Default::default()
        };

        self.emit(msg)
    }

    pub fn receive<M>(&mut self) -> Result<M>
    where
        M: prost::Message + Default,
    {
        let msg = self.collect()?;

        // It is not clear what is the best approach here. If there is no data,
        // should we error-out or return a default value? For the time being we
        // stick to the default approach, but if this proves to be not working
        // well in practice, it might be reconsidered.
        let data = match msg.data {
            Some(data) => data,
            None => return Ok(Default::default()),
        };

        prost::Message::decode(&data.value[..]).map_err(invalid_data_error)
    }

    fn emit(&mut self, msg: Message) -> Result<()> {
        let mut buf = Vec::new();
        prost::Message::encode(&msg, &mut buf).map_err(invalid_data_error)?;

        self.output.write_u32::<LittleEndian>(buf.len() as u32)?;
        self.output.write(&buf)?;
        self.output.write_u32::<LittleEndian>(MAGIC)?;
        self.output.flush()?;

        Ok(())
    }

    fn collect(&mut self) -> Result<Message> {
        let len = self.input.read_i32::<LittleEndian>()? as usize;
        let mut buf = vec!(0; len);
        self.input.read_exact(&mut buf[..])?;

        prost::Message::decode(&buf[..]).map_err(invalid_data_error)
    }

    fn handshake(&mut self) -> Result<()> {
        self.output.write_u32::<LittleEndian>(MAGIC)?;
        self.output.flush()?;

        let magic = self.input.read_u32::<LittleEndian>()?;
        if magic != MAGIC {
            let err = invalid_data_error(format!("invalid magic `{}`", magic));
            return Err(err);
        }

        Ok(())
    }

}

fn invalid_data_error<E>(err: E) -> std::io::Error
where
    E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
    return std::io::Error::new(std::io::ErrorKind::InvalidData, err);
}

const MAGIC: u32 = 0xf1ee1001;

fn open(var: &str) -> std::fs::File {
    let fd = match std::env::var(var) {
        Ok(fd) => fd,
        Err(err) => panic!("invalid variable `{}`: {}", var, err),
    };

    let fd = match fd.parse() {
        Ok(fd) => fd,
        Err(err) => panic!("failed to parse a file descriptor: {}", err),
    };

    // TODO: Add support for Windows.
    unsafe {
        std::os::unix::io::FromRawFd::from_raw_fd(fd)
    }
}

lazy_static! {
    static ref CONNECTION: Mutex<Connection<std::fs::File, std::fs::File>> = {
        let input = open("FLEETSPEAK_COMMS_CHANNEL_INFD");
        let output = open("FLEETSPEAK_COMMS_CHANNEL_OUTFD");

        let conn = Connection::new(input, output).expect("handshake failure");
        Mutex::new(conn)
    };
}

pub fn heartbeat() -> Result<()> {
    connected(|conn| conn.heartbeat())
}

pub fn startup(version: &str) -> Result<()> {
    connected(|conn| conn.startup(version))
}

pub fn send<M>(service: &str, kind: &str, data: M) -> Result<()>
where
    M: prost::Message,
{
    connected(|conn| conn.send(service, kind, data))
}

pub fn receive<M>() -> Result<M>
where
    M: prost::Message + Default,
{
    connected(|conn| conn.receive())
}

fn connected<F, T>(f: F) -> Result<T>
where
    F: FnOnce(&mut Connection<std::fs::File, std::fs::File>) -> Result<T>
{
    let mut conn = CONNECTION.lock().expect("poisoned connection mutex");
    f(&mut conn)
}
<commit_msg>Write tests for connection handshaking.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use fleetspeak_proto::common::{Message, Address};
use fleetspeak_proto::channel::{StartupData};
use lazy_static::lazy_static;
use prost;
use prost_types;
use std::io::{Read, Write, Result};
use std::marker::{Send, Sync};
use std::sync::Mutex;

pub struct Connection<R, W> {
    pub input: R,
    pub output: W,
}

impl<R: Read, W: Write> Connection<R, W> {

    pub fn new(input: R, output: W) -> Result<Self> {
        let mut conn = Connection {
            input: input,
            output: output,
        };
        conn.handshake()?;

        Ok(conn)
    }

    pub fn heartbeat(&mut self) -> Result<()> {
        let msg = Message {
            message_type: "Heartbeat".to_string(),
            destination: Some(Address {
                service_name: "system".to_string(),
                ..Default::default()
            }),
            ..Default::default()
        };

        self.emit(msg)
    }

    pub fn startup(&mut self, version: &str) -> Result<()> {
        let data = StartupData {
            pid: std::process::id() as i64,
            version: version.to_string(),
        };

        self.send("system", "StartupData", data)
    }

    pub fn send<M>(&mut self, service: &str, kind: &str, data: M) -> Result<()>
    where
        M: prost::Message,
    {
        let mut buf = Vec::new();
        prost::Message::encode(&data, &mut buf).map_err(invalid_data_error)?;

        let msg = Message {
            message_type: kind.to_string(),
            destination: Some(Address {
                service_name: service.to_string(),
                ..Default::default()
            }),
            data: Some(prost_types::Any {
                value: buf.to_vec(),
                ..Default::default()
            }),
            ..Default::default()
        };

        self.emit(msg)
    }

    pub fn receive<M>(&mut self) -> Result<M>
    where
        M: prost::Message + Default,
    {
        let msg = self.collect()?;

        // It is not clear what is the best approach here. If there is no data,
        // should we error-out or return a default value? For the time being we
        // stick to the default approach, but if this proves to be not working
        // well in practice, it might be reconsidered.
        let data = match msg.data {
            Some(data) => data,
            None => return Ok(Default::default()),
        };

        prost::Message::decode(&data.value[..]).map_err(invalid_data_error)
    }

    fn emit(&mut self, msg: Message) -> Result<()> {
        let mut buf = Vec::new();
        prost::Message::encode(&msg, &mut buf).map_err(invalid_data_error)?;

        self.output.write_u32::<LittleEndian>(buf.len() as u32)?;
        self.output.write(&buf)?;
        self.output.write_u32::<LittleEndian>(MAGIC)?;
        self.output.flush()?;

        Ok(())
    }

    fn collect(&mut self) -> Result<Message> {
        let len = self.input.read_i32::<LittleEndian>()? as usize;
        let mut buf = vec!(0; len);
        self.input.read_exact(&mut buf[..])?;

        prost::Message::decode(&buf[..]).map_err(invalid_data_error)
    }

    fn handshake(&mut self) -> Result<()> {
        self.output.write_u32::<LittleEndian>(MAGIC)?;
        self.output.flush()?;

        let magic = self.input.read_u32::<LittleEndian>()?;
        if magic != MAGIC {
            let err = invalid_data_error(format!("invalid magic `{}`", magic));
            return Err(err);
        }

        Ok(())
    }

}

fn invalid_data_error<E>(err: E) -> std::io::Error
where
    E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
    return std::io::Error::new(std::io::ErrorKind::InvalidData, err);
}

const MAGIC: u32 = 0xf1ee1001;

fn open(var: &str) -> std::fs::File {
    let fd = match std::env::var(var) {
        Ok(fd) => fd,
        Err(err) => panic!("invalid variable `{}`: {}", var, err),
    };

    let fd = match fd.parse() {
        Ok(fd) => fd,
        Err(err) => panic!("failed to parse a file descriptor: {}", err),
    };

    // TODO: Add support for Windows.
    unsafe {
        std::os::unix::io::FromRawFd::from_raw_fd(fd)
    }
}

lazy_static! {
    static ref CONNECTION: Mutex<Connection<std::fs::File, std::fs::File>> = {
        let input = open("FLEETSPEAK_COMMS_CHANNEL_INFD");
        let output = open("FLEETSPEAK_COMMS_CHANNEL_OUTFD");

        let conn = Connection::new(input, output).expect("handshake failure");
        Mutex::new(conn)
    };
}

pub fn heartbeat() -> Result<()> {
    connected(|conn| conn.heartbeat())
}

pub fn startup(version: &str) -> Result<()> {
    connected(|conn| conn.startup(version))
}

pub fn send<M>(service: &str, kind: &str, data: M) -> Result<()>
where
    M: prost::Message,
{
    connected(|conn| conn.send(service, kind, data))
}

pub fn receive<M>() -> Result<M>
where
    M: prost::Message + Default,
{
    connected(|conn| conn.receive())
}

fn connected<F, T>(f: F) -> Result<T>
where
    F: FnOnce(&mut Connection<std::fs::File, std::fs::File>) -> Result<T>
{
    let mut conn = CONNECTION.lock().expect("poisoned connection mutex");
    f(&mut conn)
}

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

    #[test]
    fn handshake_good_magic() {
        let mut buf_in = [0; 1024];
        let mut buf_out = [0; 1024];

        let mut cur = Cursor::new(&mut buf_in[..]);
        assert!(cur.write_u32::<LittleEndian>(MAGIC).is_ok());

        let cur_in = Cursor::new(&mut buf_in[..]);
        let cur_out = Cursor::new(&mut buf_out[..]);
        assert!(Connection::new(cur_in, cur_out).is_ok());

        let mut cur = Cursor::new(&mut buf_out[..]);
        assert_eq!(cur.read_u32::<LittleEndian>().unwrap(), MAGIC);
    }

    #[test]
    fn handshake_bad_magic() {
        let mut buf_in = [0; 1024];
        let mut buf_out = [0; 1024];

        let mut cur = Cursor::new(&mut buf_in[..]);
        assert!(cur.write_u32::<LittleEndian>(0xf1ee1337).is_ok());

        let cur_in = Cursor::new(&mut buf_in[..]);
        let cur_out = Cursor::new(&mut buf_out[..]);
        assert!(Connection::new(cur_in, cur_out).is_err());
    }
}
<|endoftext|>


<commit_before>extern crate ratelimit;

use std::io::Read;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
use std::sync::mpsc::SyncSender;

use chrono::datetime::DateTime;
use chrono::UTC;

use hyper::Client as WebClient;
use hyper::header::Headers;
use hyper::net::HttpsConnector;
use hyper_native_tls::NativeTlsClient;

use serde_json;

use account::*;
use account::details::*;
use account::summary::*;

use instrument::pricing_query::PricingQuery;

header! { (Authorization, "Authorization") => [String] }
header! { (AcceptDatetimeFormat, "AcceptDatetimeFormat") => [String] }
header! { (Connection, "Connection") => [String] }

pub struct Client<'a> {
    url: &'a str,
    api_key: &'a str,
    web_client: WebClient,
    sender: SyncSender<()>
}

/// The Client facilitates all requests to the Oanda API
impl<'a> Client<'a> {
    pub fn new(url: &'a str, api_key: &'a str) -> Client<'a> {
        let ssl = NativeTlsClient::new().unwrap();
        let connector = HttpsConnector::new(ssl);
        let web_client = WebClient::with_connector(connector);

        /// Client is allowed to have no more than 120 requests per second on average,
        /// with bursts of no more than 60 requests. Excess requests will be
        /// rejected. This restriction is applied for each access token,
        /// not for each individual connection.
        let mut ratelimit = ratelimit::Ratelimit::configure()
            .capacity(60) // number of tokens the bucket will hold
            .quantum(1)   // add one token per interval
            .interval(Duration::from_millis(9)) // TODO: allows for 111 per second 8.3 would be 120 (Quantum per 9 milliseconds)
            .build();

        let client = Client {
            url: url,
            api_key: api_key,
            web_client: web_client,
            sender: ratelimit.clone_sender()
        };

        thread::spawn(move || {
            loop {
                ratelimit.run();
            }
        });

        client
    }

    /// Get Account list for current auth token
    pub fn accounts(&self) -> Vec<Account> {
        let input = self.get("accounts");
        let mut result: Accounts = serde_json::from_str(&input).unwrap();

        for x in result.accounts.iter_mut() {
            x.client = Some(&self);
        }

        result.accounts
    }

    pub fn pricing_for(&self, instrument: String, from: DateTime<UTC>) -> PricingQuery {
        PricingQuery::new(&self, instrument, from)
    }

    pub fn get(&self, params: &str) -> String {
        self.sender.send(());

        let mut res = String::new();

        self.web_client
            .get(&format!("{}/{}", self.url, params))
            .headers(self.headers())
            .send()
            .unwrap()
            .read_to_string(&mut res)
            .unwrap();

        println!("*************************");
        println!("res: {}", res);
        println!("*************************");

        res
    }

    fn headers(&self) -> Headers {
        let mut headers = Headers::new();

        headers.set(Authorization(format!("Bearer {}", self.api_key)));
        headers.set(AcceptDatetimeFormat("RFC3339".to_string()));
        headers.set(Connection("Keep-Alive".to_string()));

        headers
    }
}

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

    /// # TODO: Move integration tests to `tests/`
    #[test]
    fn it_can_read_accounts() {
        let url = env::var("OANDA_API_URL").unwrap();
        let key = env::var("OANDA_API_KEY").unwrap();
        let account_id = env::var("OANDA_TEST_ACCOUNT_ID").unwrap();

        let client = Client::new(&url, &key);
        let info = client.accounts();

        assert_eq!(info.iter().any(|ref x| x.id == account_id), true)
    }
}
<commit_msg>Remove debugging code<commit_after>extern crate ratelimit;

use std::io::Read;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
use std::sync::mpsc::SyncSender;

use chrono::datetime::DateTime;
use chrono::UTC;

use hyper::Client as WebClient;
use hyper::header::Headers;
use hyper::net::HttpsConnector;
use hyper_native_tls::NativeTlsClient;

use serde_json;

use account::*;
use account::details::*;
use account::summary::*;

use instrument::pricing_query::PricingQuery;

header! { (Authorization, "Authorization") => [String] }
header! { (AcceptDatetimeFormat, "AcceptDatetimeFormat") => [String] }
header! { (Connection, "Connection") => [String] }

pub struct Client<'a> {
    url: &'a str,
    api_key: &'a str,
    web_client: WebClient,
    sender: SyncSender<()>
}

/// The Client facilitates all requests to the Oanda API
impl<'a> Client<'a> {
    pub fn new(url: &'a str, api_key: &'a str) -> Client<'a> {
        let ssl = NativeTlsClient::new().unwrap();
        let connector = HttpsConnector::new(ssl);
        let web_client = WebClient::with_connector(connector);

        /// Client is allowed to have no more than 120 requests per second on average,
        /// with bursts of no more than 60 requests. Excess requests will be
        /// rejected. This restriction is applied for each access token,
        /// not for each individual connection.
        let mut ratelimit = ratelimit::Ratelimit::configure()
            .capacity(60) // number of tokens the bucket will hold
            .quantum(1)   // add one token per interval
            .interval(Duration::from_millis(9)) // TODO: allows for 111 per second 8.3 would be 120 (Quantum per 9 milliseconds)
            .build();

        let client = Client {
            url: url,
            api_key: api_key,
            web_client: web_client,
            sender: ratelimit.clone_sender()
        };

        thread::spawn(move || {
            loop {
                ratelimit.run();
            }
        });

        client
    }

    /// Get Account list for current auth token
    pub fn accounts(&self) -> Vec<Account> {
        let input = self.get("accounts");
        let mut result: Accounts = serde_json::from_str(&input).unwrap();

        for x in result.accounts.iter_mut() {
            x.client = Some(&self);
        }

        result.accounts
    }

    pub fn pricing_for(&self, instrument: String, from: DateTime<UTC>) -> PricingQuery {
        PricingQuery::new(&self, instrument, from)
    }

    pub fn get(&self, params: &str) -> String {
        self.sender.send(());

        let mut res = String::new();

        self.web_client
            .get(&format!("{}/{}", self.url, params))
            .headers(self.headers())
            .send()
            .unwrap()
            .read_to_string(&mut res)
            .unwrap();

        res
    }

    fn headers(&self) -> Headers {
        let mut headers = Headers::new();

        headers.set(Authorization(format!("Bearer {}", self.api_key)));
        headers.set(AcceptDatetimeFormat("RFC3339".to_string()));
        headers.set(Connection("Keep-Alive".to_string()));

        headers
    }
}

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

    /// # TODO: Move integration tests to `tests/`
    #[test]
    fn it_can_read_accounts() {
        let url = env::var("OANDA_API_URL").unwrap();
        let key = env::var("OANDA_API_KEY").unwrap();
        let account_id = env::var("OANDA_TEST_ACCOUNT_ID").unwrap();

        let client = Client::new(&url, &key);
        let info = client.accounts();

        assert_eq!(info.iter().any(|ref x| x.id == account_id), true)
    }
}
<|endoftext|>