<commit_before>use redox::ops::DerefMut;
use redox::string::*;
use redox::vec::Vec;
use redox::boxed::Box;
use redox::fs::*;
use redox::io::*;
use redox::console::*;
use redox::env::*;
use redox::time::Duration;
use redox::to_num::*;

/* Magic Macros { */
static mut application: *mut Application<'static> = 0 as *mut Application;

/// Execute a command
macro_rules! exec {
    ($cmd:expr) => ({
        unsafe {
            (*application).on_command(&$cmd.to_string());
        }
    })
}
/* } Magic Macros */

/// A command
pub struct Command<'a> {
    pub name: &'a str,
    pub main: Box<Fn(&Vec<String>)>,
}

impl<'a> Command<'a> {
    /// Return the vector of the commands
    // TODO: Use a more efficient collection instead
    pub fn vec() -> Vec<Self> {
        let mut commands: Vec<Self> = Vec::new();
        commands.push(Command {
            name: "echo",
            main: box |args: &Vec<String>| {
                let echo = args.iter()
                    .skip(1)
                    .fold(String::new(), |string, arg| string + " " + arg);
                println!("{}", echo.trim());
            },
        });

        commands.push(Command {
            name: "open",
            main: box |args: &Vec<String>| {
                if let Some(arg) = args.get(1) {
                    File::exec(arg);
                }
            },
        });

        commands.push(Command {
            name: "run",
            main: box |args: &Vec<String>| {
                if let Some(path) = args.get(1) {

                    let mut commands = String::new();
                    if let Some(mut file) = File::open(path) {
                        println!("URL: {:?}", file.path());

                        file.read_to_string(&mut commands);
                    }

                    for command in commands.split('\n') {
                        exec!(command);
                    }
                }
            },
        });

        commands.push(Command {
            name: "send",
            main: box |args: &Vec<String>| {
                if args.len() < 3 {
                    println!("Error: incorrect arguments");
                    println!("Usage: send [url] [data]");
                    return;
                }

                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    println!("URL: {:?}", file.path());

                    let string: String = args.iter()
                        .skip(2)
                        .fold(String::new(), |s, arg| s + " " + arg)
                        + "\r\n\r\n";

                    match file.write(string.trim_left().as_bytes()) {
                        Some(size) => println!("Wrote {} bytes", size),
                        None => println!("Failed to write"),
                    }

                    let mut string = String::new();
                    match file.read_to_string(&mut string) {
                        Some(_) => println!("{}", string),
                        None => println!("Failed to read"),
                    }
                }
            },
        });

        commands.push(Command {
            name: "sleep",
            main: box |args: &Vec<String>| {
                let secs = {
                    match args.get(1) {
                        Some(arg) => arg.to_num() as i64,
                        None => 0,
                    }
                };

                let nanos = {
                    match args.get(2) {
                        Some(arg) => arg.to_num() as i32,
                        None => 0,
                    }
                };

                println!("Sleep: {} {}", secs, nanos);
                let remaining = Duration::new(secs, nanos).sleep();
                println!("Remaining: {} {}", remaining.secs, remaining.nanos);
            },
        });

        commands.push(Command {
            name: "test_ht",
            main: box |args: &Vec<String>| {
                ::redox::hashmap::test();
            },
        });

        commands.push(Command {
            name: "url",
            main: box |args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    println!("URL: {:?}", file.path());

                    let mut string = String::new();
                    match file.read_to_string(&mut string) {
                        Some(_) => println!("{}", string),
                        None => println!("Failed to read"),
                    }
                }
            },
        });

        commands.push(Command {
            name: "url_hex",
            main: box |args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    println!("URL: {:?}", file.path());

                    let mut vec: Vec<u8> = Vec::new();
                    match file.read_to_end(&mut vec) {
                        Some(_) => {
                            let mut line = "HEX:".to_string();
                            for byte in vec.iter() {
                                line = line + " " + &format!("{:X}", *byte);
                            }
                            println!("{}", line);
                        }
                        None => println!("Failed to read"),
                    }
                }
            },
        });

        commands.push(Command {
            name: "wget",
            main: box |args: &Vec<String>| {
                if let Some(host) = args.get(1) {
                    if let Some(req) = args.get(2) {
                        if let Some(mut con) = File::open(&("tcp://".to_string() + host)) {
                            con.write(("GET ".to_string() + req + " HTTP/1.1").as_bytes());

                            let mut res = Vec::new();
                            con.read_to_end(&mut res);

                            if let Some(mut file) = File::open(&req) {
                                file.write(&res);
                            }
                        }
                    } else {
                        println!("No request given");
                    }
                } else {
                    println!("No url given");
                }
            },
        });

        // Simple command to create a file, in the current directory
        // The file has got the name given as the first argument of the command
        // If the command have no arguments, the command don't create the file
        commands.push(Command {
            name: "cfile",
            main: box |args: &Vec<String>| {
                match args.get(1) {
                    Some(file_name) => {
                        File::create(file_name);
                    }
                    None => {
                        println!("Could not create a file without a name");
                    }
                }
            }
        });

        commands.push(Command {
            name: "pwd",
            main: box |args: &Vec<String>| {
                let mut err = false;
                if let Some(file) = File::open("") {
                    if let Some(path) = file.path() {
                        println!("{}", path);
                    } else {
                        err = true;
                    }
                } else {
                    err = true;
                }
                if err {
                    println!("Could not get the path");
                }
            },
        });

        commands.push(Command {
            name: "cd",
            main: box |args: &Vec<String>| {
                match args.get(1) {
                    Some(path) => {
                        if !change_cwd(&path) {
                            println!("Bad path: {}", path);
                        }
                    }
                    None => println!("No path given")
                }
            },
        });

        let command_list = commands.iter().fold(String::new(), |l , c| l + " " + c.name) + " exit";

        commands.push(Command {
            name: "help",
            main: box move |args: &Vec<String>| {
                println!("Commands:{}", command_list);
            },
         });

        commands
    }
}

/// A (env) variable
pub struct Variable {
    pub name: String,
    pub value: String,
}

pub struct Mode {
    value: bool,
}

/// An application
pub struct Application<'a> {
    commands: Vec<Command<'a>>,
    variables: Vec<Variable>,
    modes: Vec<Mode>,
}

impl<'a> Application<'a> {
    /// Create a new empty application
    pub fn new() -> Self {
        return Application {
            commands: Command::vec(),
            variables: Vec::new(),
            modes: Vec::new(),
        };
    }

    fn on_command(&mut self, command_string: &str) {
        //Comment
        if command_string.starts_with('#') {
            return;
        }

        //Show variables
        if command_string == "$" {
            let variables = self.variables.iter()
                .fold(String::new(),
                      |string, variable| string + "\n" + &variable.name + "=" + &variable.value);
            println!("{}", variables);
            return;
        }

        //Explode into arguments, replace variables
        let mut args: Vec<String> = Vec::<String>::new();
        for arg in command_string.split(' ') {
            if !arg.is_empty() {
                if arg.starts_with('$') {
                    let name = arg[1 .. arg.len()].to_string();
                    for variable in self.variables.iter() {
                        if variable.name == name {
                            args.push(variable.value.clone());
                            break;
                        }
                    }
                } else {
                    args.push(arg.to_string());
                }
            }
        }

        //Execute commands
        if let Some(cmd) = args.get(0) {
            if cmd == "if" {
                let mut value = false;

                if let Some(left) = args.get(1) {
                    if let Some(cmp) = args.get(2) {
                        if let Some(right) = args.get(3) {
                            if cmp == "==" {
                                value = *left == *right;
                            } else if cmp == "!=" {
                                value = *left != *right;
                            } else if cmp == ">" {
                                value = left.to_num_signed() > right.to_num_signed();
                            } else if cmp == ">=" {
                                value = left.to_num_signed() >= right.to_num_signed();
                            } else if cmp == "<" {
                                value = left.to_num_signed() < right.to_num_signed();
                            } else if cmp == "<=" {
                                value = left.to_num_signed() <= right.to_num_signed();
                            } else {
                                println!("Unknown comparison: {}", cmp);
                            }
                        }
                    }
                }

                self.modes.insert(0, Mode { value: value });
                return;
            }

            if cmd == "else" {
                let mut syntax_error = false;
                match self.modes.get_mut(0) {
                    Some(mode) => mode.value = !mode.value,
                    None => syntax_error = true,
                }
                if syntax_error {
                    println!("Syntax error: else found with no previous if");
                }
                return;
            }

            if cmd == "fi" {
                let mut syntax_error = false;
                if !self.modes.is_empty() {
                    self.modes.remove(0);
                } else {
                    syntax_error = true;
                }
                if syntax_error {
                    println!("Syntax error: fi found with no previous if");
                }
                return;
            }

            for mode in self.modes.iter() {
                if !mode.value {
                    return;
                }
            }

            //Set variables
            if let Some(i) = cmd.find('=') {
                let name = cmd[0 .. i].to_string();
                let mut value = cmd[i + 1 .. cmd.len()].to_string();

                if name.is_empty() {
                    return;
                }

                for i in 1..args.len() {
                    if let Some(arg) = args.get(i) {
                        value = value + " " + &arg;
                    }
                }

                if value.is_empty() {
                    let mut remove = -1;
                    for i in 0..self.variables.len() {
                        match self.variables.get(i) {
                            Some(variable) => if variable.name == name {
                                remove = i as isize;
                                break;
                            },
                            None => break,
                        }
                    }

                    if remove >= 0 {
                        self.variables.remove(remove as usize);
                    }
                } else {
                    for variable in self.variables.iter_mut() {
                        if variable.name == name {
                            variable.value = value;
                            return;
                        }
                    }

                    self.variables.push(Variable {
                        name: name,
                        value: value,
                    });
                }
                return;
            }

            //Commands
            for command in self.commands.iter() {
                if &command.name == cmd {
                    (*command.main)(&args);
                    return;
                }
            }

            println!("Unknown command: '{}'", cmd);
        }
    }

    /// Run the application
    pub fn main(&mut self) {
        console_title("Terminal");

        println!("Type help for a command list");
        if let Some(arg) = args().get(1) {
            let command = "run ".to_string() + arg;
            println!("# {}", command);
            self.on_command(&command);
        }

        while let Some(command) = readln!() {
            println!("# {}", command);
            if command == "exit" {
                break;
            } else if !command.is_empty() {
                self.on_command(&command);
            }
        }
    }
}

pub fn main() {
    unsafe {
        let mut app = box Application::new();
        application = app.deref_mut();
        app.main();
    }
}
<commit_msg>Modification to improve the code for the command 'pwd'<commit_after>use redox::ops::DerefMut;
use redox::string::*;
use redox::vec::Vec;
use redox::boxed::Box;
use redox::fs::*;
use redox::io::*;
use redox::console::*;
use redox::env::*;
use redox::time::Duration;
use redox::to_num::*;

/* Magic Macros { */
static mut application: *mut Application<'static> = 0 as *mut Application;

/// Execute a command
macro_rules! exec {
    ($cmd:expr) => ({
        unsafe {
            (*application).on_command(&$cmd.to_string());
        }
    })
}
/* } Magic Macros */

/// A command
pub struct Command<'a> {
    pub name: &'a str,
    pub main: Box<Fn(&Vec<String>)>,
}

impl<'a> Command<'a> {
    /// Return the vector of the commands
    // TODO: Use a more efficient collection instead
    pub fn vec() -> Vec<Self> {
        let mut commands: Vec<Self> = Vec::new();
        commands.push(Command {
            name: "echo",
            main: box |args: &Vec<String>| {
                let echo = args.iter()
                    .skip(1)
                    .fold(String::new(), |string, arg| string + " " + arg);
                println!("{}", echo.trim());
            },
        });

        commands.push(Command {
            name: "open",
            main: box |args: &Vec<String>| {
                if let Some(arg) = args.get(1) {
                    File::exec(arg);
                }
            },
        });

        commands.push(Command {
            name: "run",
            main: box |args: &Vec<String>| {
                if let Some(path) = args.get(1) {

                    let mut commands = String::new();
                    if let Some(mut file) = File::open(path) {
                        println!("URL: {:?}", file.path());

                        file.read_to_string(&mut commands);
                    }

                    for command in commands.split('\n') {
                        exec!(command);
                    }
                }
            },
        });

        commands.push(Command {
            name: "send",
            main: box |args: &Vec<String>| {
                if args.len() < 3 {
                    println!("Error: incorrect arguments");
                    println!("Usage: send [url] [data]");
                    return;
                }

                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    println!("URL: {:?}", file.path());

                    let string: String = args.iter()
                        .skip(2)
                        .fold(String::new(), |s, arg| s + " " + arg)
                        + "\r\n\r\n";

                    match file.write(string.trim_left().as_bytes()) {
                        Some(size) => println!("Wrote {} bytes", size),
                        None => println!("Failed to write"),
                    }

                    let mut string = String::new();
                    match file.read_to_string(&mut string) {
                        Some(_) => println!("{}", string),
                        None => println!("Failed to read"),
                    }
                }
            },
        });

        commands.push(Command {
            name: "sleep",
            main: box |args: &Vec<String>| {
                let secs = {
                    match args.get(1) {
                        Some(arg) => arg.to_num() as i64,
                        None => 0,
                    }
                };

                let nanos = {
                    match args.get(2) {
                        Some(arg) => arg.to_num() as i32,
                        None => 0,
                    }
                };

                println!("Sleep: {} {}", secs, nanos);
                let remaining = Duration::new(secs, nanos).sleep();
                println!("Remaining: {} {}", remaining.secs, remaining.nanos);
            },
        });

        commands.push(Command {
            name: "test_ht",
            main: box |args: &Vec<String>| {
                ::redox::hashmap::test();
            },
        });

        commands.push(Command {
            name: "url",
            main: box |args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    println!("URL: {:?}", file.path());

                    let mut string = String::new();
                    match file.read_to_string(&mut string) {
                        Some(_) => println!("{}", string),
                        None => println!("Failed to read"),
                    }
                }
            },
        });

        commands.push(Command {
            name: "url_hex",
            main: box |args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    println!("URL: {:?}", file.path());

                    let mut vec: Vec<u8> = Vec::new();
                    match file.read_to_end(&mut vec) {
                        Some(_) => {
                            let mut line = "HEX:".to_string();
                            for byte in vec.iter() {
                                line = line + " " + &format!("{:X}", *byte);
                            }
                            println!("{}", line);
                        }
                        None => println!("Failed to read"),
                    }
                }
            },
        });

        commands.push(Command {
            name: "wget",
            main: box |args: &Vec<String>| {
                if let Some(host) = args.get(1) {
                    if let Some(req) = args.get(2) {
                        if let Some(mut con) = File::open(&("tcp://".to_string() + host)) {
                            con.write(("GET ".to_string() + req + " HTTP/1.1").as_bytes());

                            let mut res = Vec::new();
                            con.read_to_end(&mut res);

                            if let Some(mut file) = File::open(&req) {
                                file.write(&res);
                            }
                        }
                    } else {
                        println!("No request given");
                    }
                } else {
                    println!("No url given");
                }
            },
        });

        // Simple command to create a file, in the current directory
        // The file has got the name given as the first argument of the command
        // If the command have no arguments, the command don't create the file
        commands.push(Command {
            name: "cfile",
            main: box |args: &Vec<String>| {
                match args.get(1) {
                    Some(file_name) => {
                        File::create(file_name);
                    }
                    None => {
                        println!("Could not create a file without a name");
                    }
                }
            }
        });

        commands.push(Command {
            name: "pwd",
            main: box |args: &Vec<String>| {
                if let Some(file) = File::open("") {
                    if let Some(path) = file.path() {
                        println!("{}", path);
                    } else {
                        println!("Could not get the path");
                    }
                } else {
                    println!("Could not get the path");
                }
            },
        });

        commands.push(Command {
            name: "cd",
            main: box |args: &Vec<String>| {
                match args.get(1) {
                    Some(path) => {
                        if !change_cwd(&path) {
                            println!("Bad path: {}", path);
                        }
                    }
                    None => println!("No path given")
                }
            },
        });

        let command_list = commands.iter().fold(String::new(), |l , c| l + " " + c.name) + " exit";

        commands.push(Command {
            name: "help",
            main: box move |args: &Vec<String>| {
                println!("Commands:{}", command_list);
            },
         });

        commands
    }
}

/// A (env) variable
pub struct Variable {
    pub name: String,
    pub value: String,
}

pub struct Mode {
    value: bool,
}

/// An application
pub struct Application<'a> {
    commands: Vec<Command<'a>>,
    variables: Vec<Variable>,
    modes: Vec<Mode>,
}

impl<'a> Application<'a> {
    /// Create a new empty application
    pub fn new() -> Self {
        return Application {
            commands: Command::vec(),
            variables: Vec::new(),
            modes: Vec::new(),
        };
    }

    fn on_command(&mut self, command_string: &str) {
        //Comment
        if command_string.starts_with('#') {
            return;
        }

        //Show variables
        if command_string == "$" {
            let variables = self.variables.iter()
                .fold(String::new(),
                      |string, variable| string + "\n" + &variable.name + "=" + &variable.value);
            println!("{}", variables);
            return;
        }

        //Explode into arguments, replace variables
        let mut args: Vec<String> = Vec::<String>::new();
        for arg in command_string.split(' ') {
            if !arg.is_empty() {
                if arg.starts_with('$') {
                    let name = arg[1 .. arg.len()].to_string();
                    for variable in self.variables.iter() {
                        if variable.name == name {
                            args.push(variable.value.clone());
                            break;
                        }
                    }
                } else {
                    args.push(arg.to_string());
                }
            }
        }

        //Execute commands
        if let Some(cmd) = args.get(0) {
            if cmd == "if" {
                let mut value = false;

                if let Some(left) = args.get(1) {
                    if let Some(cmp) = args.get(2) {
                        if let Some(right) = args.get(3) {
                            if cmp == "==" {
                                value = *left == *right;
                            } else if cmp == "!=" {
                                value = *left != *right;
                            } else if cmp == ">" {
                                value = left.to_num_signed() > right.to_num_signed();
                            } else if cmp == ">=" {
                                value = left.to_num_signed() >= right.to_num_signed();
                            } else if cmp == "<" {
                                value = left.to_num_signed() < right.to_num_signed();
                            } else if cmp == "<=" {
                                value = left.to_num_signed() <= right.to_num_signed();
                            } else {
                                println!("Unknown comparison: {}", cmp);
                            }
                        }
                    }
                }

                self.modes.insert(0, Mode { value: value });
                return;
            }

            if cmd == "else" {
                let mut syntax_error = false;
                match self.modes.get_mut(0) {
                    Some(mode) => mode.value = !mode.value,
                    None => syntax_error = true,
                }
                if syntax_error {
                    println!("Syntax error: else found with no previous if");
                }
                return;
            }

            if cmd == "fi" {
                let mut syntax_error = false;
                if !self.modes.is_empty() {
                    self.modes.remove(0);
                } else {
                    syntax_error = true;
                }
                if syntax_error {
                    println!("Syntax error: fi found with no previous if");
                }
                return;
            }

            for mode in self.modes.iter() {
                if !mode.value {
                    return;
                }
            }

            //Set variables
            if let Some(i) = cmd.find('=') {
                let name = cmd[0 .. i].to_string();
                let mut value = cmd[i + 1 .. cmd.len()].to_string();

                if name.is_empty() {
                    return;
                }

                for i in 1..args.len() {
                    if let Some(arg) = args.get(i) {
                        value = value + " " + &arg;
                    }
                }

                if value.is_empty() {
                    let mut remove = -1;
                    for i in 0..self.variables.len() {
                        match self.variables.get(i) {
                            Some(variable) => if variable.name == name {
                                remove = i as isize;
                                break;
                            },
                            None => break,
                        }
                    }

                    if remove >= 0 {
                        self.variables.remove(remove as usize);
                    }
                } else {
                    for variable in self.variables.iter_mut() {
                        if variable.name == name {
                            variable.value = value;
                            return;
                        }
                    }

                    self.variables.push(Variable {
                        name: name,
                        value: value,
                    });
                }
                return;
            }

            //Commands
            for command in self.commands.iter() {
                if &command.name == cmd {
                    (*command.main)(&args);
                    return;
                }
            }

            println!("Unknown command: '{}'", cmd);
        }
    }

    /// Run the application
    pub fn main(&mut self) {
        console_title("Terminal");

        println!("Type help for a command list");
        if let Some(arg) = args().get(1) {
            let command = "run ".to_string() + arg;
            println!("# {}", command);
            self.on_command(&command);
        }

        while let Some(command) = readln!() {
            println!("# {}", command);
            if command == "exit" {
                break;
            } else if !command.is_empty() {
                self.on_command(&command);
            }
        }
    }
}

pub fn main() {
    unsafe {
        let mut app = box Application::new();
        application = app.deref_mut();
        app.main();
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::net::{IpAddr, ToSocketAddrs};
use std::pin::Pin;
use std::time::{Duration, Instant};

use futures::compat::Future01CompatExt;
use futures::future::{self, join_all, Future, FutureExt};

use hyper::header::HeaderName;
use hyper::Uri;
use hyper_tls::HttpsConnector;
use native_tls::{self, TlsConnector};

use super::{Check, CheckResult, CheckResultElement, CheckStatus, CheckTimeSpan};

mod hyper_helpers;
use self::hyper_helpers::{HttpsConnectorWrapped, StaticResolverSingle};

const CHECK_NAME: &str = "http";

#[derive(Clone, Debug)]
pub struct HttpTarget {
    pub url: Uri,
    pub extra_headers: Vec<(HeaderName, String)>,
}

#[derive(Clone, Debug)]
pub struct HttpCheck {
    pub latency_requirement: Duration,
    pub allow_client_error: bool,
}

fn invalid_target_missing_value(
    url: &str,
    field: &str,
) -> Pin<Box<dyn Future<Output = Result<CheckResult, io::Error>> + Send + 'static>> {
    let msg = format!("invalid target: URL {} is missing a {}", url, field);
    debug!("invalid_target_missing_value: {}", msg);
    future::err(io::Error::new(io::ErrorKind::Other, msg)).boxed()
}

fn invalid_target_invalid_value(
    url: &str,
    field: &str,
    value: &str,
) -> Pin<Box<dyn Future<Output = Result<CheckResult, io::Error>> + Send + 'static>> {
    let msg = format!(
        "invalid target: URL {} has invalid {}: {}",
        url, field, value
    );
    debug!("invalid_target_invalid_value: {}", msg);
    future::err(io::Error::new(io::ErrorKind::Other, msg)).boxed()
}

impl Check for HttpCheck {
    type Target = HttpTarget;
    type Future = Pin<Box<dyn Future<Output = Result<CheckResult, io::Error>> + Send>>;

    fn check(&self, target: Self::Target) -> Self::Future {
        use http::uri::Parts;
        let uri_string = target.url.to_string();

        let parts: Parts = target.url.clone().into();
        let scheme = match parts.scheme {
            Some(s) => s,
            None => return invalid_target_missing_value(&uri_string, "scheme"),
        };
        let authority = match parts.authority {
            Some(a) => a,
            None => return invalid_target_missing_value(&uri_string, "authority"),
        };

        let mut port;
        match scheme.as_str() {
            "http" => port = 80,
            "https" => port = 443,
            s => return invalid_target_invalid_value(&uri_string, "scheme", s),
        };
        if let Some(p) = authority.port_part() {
            port = p.as_u16()
        }

        let netloc = (authority.host().to_string(), port);
        check_impl(self.clone(), netloc, target.clone()).boxed()
    }
}

async fn check_impl(
    check: HttpCheck,
    netloc: (String, u16),
    target: HttpTarget,
) -> Result<CheckResult, io::Error> {
    // Do DNS lookup.  Check fails if DNS fails.
    // FIXME/XXX: this is blocking.
    let netloc: (&str, u16) = (&netloc.0, netloc.1);
    let addrs: Vec<_> = netloc.to_socket_addrs()?.map(|s| s.ip()).collect();
    debug!("check_impl: addrs={:?}", addrs);

    let results: Vec<CheckResult> = join_all(
        addrs
            .into_iter()
            .map(|s| connect_and_request(&check, s, target.clone())),
    )
    .await;

    Ok(CheckResult::merge(results.into_iter()))
}

async fn connect_and_request(
    check: &HttpCheck,
    ip_addr: IpAddr,
    target: HttpTarget,
) -> CheckResult {
    use hyper::body::Body;
    use hyper::client::{Client, HttpConnector};
    use hyper::header::USER_AGENT;
    use hyper::Request;

    let mut http_connector = HttpConnector::new_with_resolver(StaticResolverSingle::new(ip_addr));
    http_connector.enforce_http(false);

    let tls_connector = TlsConnector::builder().build().unwrap();
    let connector = HttpsConnector::from((http_connector, tls_connector));
    let connector = HttpsConnectorWrapped::from(connector);
    let conn_summary_handle = connector.summary_handle();

    let user_agent = format!("rcanary/{}", crate::CARGO_PKG_VERSION);

    let mut request = Request::get(target.url);
    request.header(USER_AGENT, user_agent);
    for (k, v) in target.extra_headers {
        request.header(k, v);
    }
    let request = request.body(Body::empty()).unwrap();

    let client: Client<_> = Client::builder().build(connector);
    let resp: hyper::Response<_> = match client.request(request).compat().await {
        Ok(r) => r,
        Err(err) => {
            let mut timeline = Vec::new();
            if let Some(s) = conn_summary_handle.summary() {
                if let Some(e) = s.connected_time() {
                    timeline.push(CheckTimeSpan {
                        name: "tls-handshake",
                        started_at: s.start_time(),
                        ended_at: e,
                    });
                }
            }

            return CheckResult::new(
                CHECK_NAME,
                CheckResultElement {
                    target: ip_addr,
                    check_status: CheckStatus::Failed,
                    status_code: 0,
                    err_msg: Some(format!("hyper error: {}", err)),
                    timeline,
                },
            );
        }
    };

    let finish_time = Instant::now();
    let mut timeline = Vec::new();

    let conn_summary = conn_summary_handle.summary().unwrap();
    let http_conn_time = conn_summary.connected_time().unwrap();
    timeline.push(CheckTimeSpan {
        name: "tls-handshake",
        started_at: conn_summary.start_time(),
        ended_at: http_conn_time,
    });
    timeline.push(CheckTimeSpan {
        name: "http",
        started_at: http_conn_time,
        ended_at: finish_time,
    });

    let status = resp.status();

    let mut is_failed = status.is_server_error();
    if !check.allow_client_error {
        is_failed |= status.is_client_error();
    }

    if is_failed {
        return CheckResult::new(
            CHECK_NAME,
            CheckResultElement {
                target: ip_addr,
                check_status: CheckStatus::Failed,
                status_code: status.as_u16(),
                err_msg: Some(format!("bad HTTP status {}", status)),
                timeline,
            },
        );
    }

    let mut check_status = CheckStatus::Alive;

    let mut err_msg = None;

    let total_latency = finish_time - conn_summary.start_time();
    if check.latency_requirement < total_latency {
        check_status = CheckStatus::Degraded;
        err_msg = Some(format!("High lantency: {:?}", total_latency));
    }

    CheckResult::new(
        CHECK_NAME,
        CheckResultElement {
            target: ip_addr,
            check_status,
            status_code: status.as_u16(),
            err_msg,
            timeline,
        },
    )
}
<commit_msg>Address @gyng's comment<commit_after>use std::io;
use std::net::{IpAddr, ToSocketAddrs};
use std::pin::Pin;
use std::time::{Duration, Instant};

use futures::compat::Future01CompatExt;
use futures::future::{self, join_all, Future, FutureExt};

use hyper::header::HeaderName;
use hyper::Uri;
use hyper_tls::HttpsConnector;
use native_tls::{self, TlsConnector};

use super::{Check, CheckResult, CheckResultElement, CheckStatus, CheckTimeSpan};

mod hyper_helpers;
use self::hyper_helpers::{HttpsConnectorWrapped, StaticResolverSingle};

const CHECK_NAME: &str = "http";

#[derive(Clone, Debug)]
pub struct HttpTarget {
    pub url: Uri,
    pub extra_headers: Vec<(HeaderName, String)>,
}

#[derive(Clone, Debug)]
pub struct HttpCheck {
    pub latency_requirement: Duration,
    pub allow_client_error: bool,
}

fn invalid_target_missing_value(
    url: &str,
    field: &str,
) -> Pin<Box<dyn Future<Output = Result<CheckResult, io::Error>> + Send + 'static>> {
    let msg = format!("invalid target: URL {} is missing a {}", url, field);
    debug!("invalid_target_missing_value: {}", msg);
    future::err(io::Error::new(io::ErrorKind::Other, msg)).boxed()
}

fn invalid_target_invalid_value(
    url: &str,
    field: &str,
    value: &str,
) -> Pin<Box<dyn Future<Output = Result<CheckResult, io::Error>> + Send + 'static>> {
    let msg = format!(
        "invalid target: URL {} has invalid {}: {}",
        url, field, value
    );
    debug!("invalid_target_invalid_value: {}", msg);
    future::err(io::Error::new(io::ErrorKind::Other, msg)).boxed()
}

impl Check for HttpCheck {
    type Target = HttpTarget;
    type Future = Pin<Box<dyn Future<Output = Result<CheckResult, io::Error>> + Send>>;

    fn check(&self, target: Self::Target) -> Self::Future {
        use http::uri::Parts;
        let uri_string = target.url.to_string();

        let parts: Parts = target.url.clone().into();
        let scheme = match parts.scheme {
            Some(s) => s,
            None => return invalid_target_missing_value(&uri_string, "scheme"),
        };
        let authority = match parts.authority {
            Some(a) => a,
            None => return invalid_target_missing_value(&uri_string, "authority"),
        };

        let mut port;
        match scheme.as_str() {
            "http" => port = 80,
            "https" => port = 443,
            s => return invalid_target_invalid_value(&uri_string, "scheme", s),
        };
        if let Some(p) = authority.port_part() {
            port = p.as_u16()
        }

        let netloc = (authority.host().to_string(), port);
        check_impl(self.clone(), netloc, target.clone()).boxed()
    }
}

async fn check_impl(
    check: HttpCheck,
    netloc: (String, u16),
    target: HttpTarget,
) -> Result<CheckResult, io::Error> {
    // Do DNS lookup.  Check fails if DNS fails.
    // FIXME/XXX: this is blocking.
    let netloc: (&str, u16) = (&netloc.0, netloc.1);
    let addrs: Vec<_> = netloc.to_socket_addrs()?.map(|s| s.ip()).collect();
    debug!("check_impl: addrs={:?}", addrs);

    let results: Vec<CheckResult> = join_all(
        addrs
            .into_iter()
            .map(|s| connect_and_request(&check, s, target.clone())),
    )
    .await;

    Ok(CheckResult::merge(results.into_iter()))
}

async fn connect_and_request(
    check: &HttpCheck,
    ip_addr: IpAddr,
    target: HttpTarget,
) -> CheckResult {
    use hyper::body::Body;
    use hyper::client::{Client, HttpConnector};
    use hyper::header::USER_AGENT;
    use hyper::Request;

    let mut http_connector = HttpConnector::new_with_resolver(StaticResolverSingle::new(ip_addr));
    http_connector.enforce_http(false);

    let tls_connector = TlsConnector::builder().build().unwrap();
    let connector = HttpsConnector::from((http_connector, tls_connector));
    let connector = HttpsConnectorWrapped::from(connector);
    let conn_summary_handle = connector.summary_handle();

    let user_agent = format!("rcanary/{}", crate::CARGO_PKG_VERSION);

    let mut request = Request::get(target.url);
    request.header(USER_AGENT, user_agent);
    for (k, v) in target.extra_headers {
        request.header(k, v);
    }
    let request = request.body(Body::empty()).unwrap();

    let client: Client<_> = Client::builder().build(connector);
    let resp: hyper::Response<_> = match client.request(request).compat().await {
        Ok(r) => r,
        Err(err) => {
            let mut timeline = Vec::new();
            if let Some(s) = conn_summary_handle.summary() {
                if let Some(e) = s.connected_time() {
                    timeline.push(CheckTimeSpan {
                        name: "tls-handshake",
                        started_at: s.start_time(),
                        ended_at: e,
                    });
                }
            }

            return CheckResult::new(
                CHECK_NAME,
                CheckResultElement {
                    target: ip_addr,
                    check_status: CheckStatus::Failed,
                    status_code: 0,
                    err_msg: Some(format!("hyper error: {}", err)),
                    timeline,
                },
            );
        }
    };

    let finish_time = Instant::now();
    let mut timeline = Vec::new();

    let conn_summary = conn_summary_handle.summary().unwrap();
    let http_conn_time = conn_summary.connected_time().unwrap();
    timeline.push(CheckTimeSpan {
        name: "tls-handshake",
        started_at: conn_summary.start_time(),
        ended_at: http_conn_time,
    });
    timeline.push(CheckTimeSpan {
        name: "http",
        started_at: http_conn_time,
        ended_at: finish_time,
    });

    let status = resp.status();

    let mut is_failed = status.is_server_error();
    if !check.allow_client_error {
        is_failed |= status.is_client_error();
    }

    if is_failed {
        return CheckResult::new(
            CHECK_NAME,
            CheckResultElement {
                target: ip_addr,
                check_status: CheckStatus::Failed,
                status_code: status.as_u16(),
                err_msg: Some(format!("bad HTTP status {}", status)),
                timeline,
            },
        );
    }

    let mut check_status = CheckStatus::Alive;

    let mut err_msg = None;

    let total_latency = finish_time - conn_summary.start_time();
    if check.latency_requirement < total_latency {
        check_status = CheckStatus::Degraded;
        err_msg = Some(format!("High latency: {:?}", total_latency));
    }

    CheckResult::new(
        CHECK_NAME,
        CheckResultElement {
            target: ip_addr,
            check_status,
            status_code: status.as_u16(),
            err_msg,
            timeline,
        },
    )
}
<|endoftext|>


<commit_before>extern crate arrayvec;

use arrayvec::ArrayVec;
use arrayvec::ArrayString;
use std::mem;

use std::collections::HashMap;

#[test]
fn test_simple() {
    use std::ops::Add;

    let mut vec: ArrayVec<[Vec<i32>; 3]> = ArrayVec::new();

    vec.push(vec![1, 2, 3, 4]);
    vec.push(vec![10]);
    vec.push(vec![-1, 13, -2]);

    for elt in &vec {
        assert_eq!(elt.iter().fold(0, Add::add), 10);
    }

    let sum_len = vec.into_iter().map(|x| x.len()).fold(0, Add::add);
    assert_eq!(sum_len, 8);
}

#[test]
fn test_u16_index() {
    const N: usize = 4096;
    let mut vec: ArrayVec<[_; N]> = ArrayVec::new();
    for _ in 0..N {
        assert!(vec.push(1u8).is_none());
    }
    assert!(vec.push(0).is_some());
    assert_eq!(vec.len(), N);
}

#[test]
fn test_iter() {
    let mut iter = ArrayVec::from([1, 2, 3]).into_iter();
    assert_eq!(iter.size_hint(), (3, Some(3)));
    assert_eq!(iter.next_back(), Some(3));
    assert_eq!(iter.next(), Some(1));
    assert_eq!(iter.next_back(), Some(2));
    assert_eq!(iter.size_hint(), (0, Some(0)));
    assert_eq!(iter.next_back(), None);
}

#[test]
fn test_drop() {
    use std::cell::Cell;

    let flag = &Cell::new(0);

    struct Bump<'a>(&'a Cell<i32>);

    impl<'a> Drop for Bump<'a> {
        fn drop(&mut self) {
            let n = self.0.get();
            self.0.set(n + 1);
        }
    }

    {
        let mut array = ArrayVec::<[Bump; 128]>::new();
        array.push(Bump(flag));
        array.push(Bump(flag));
    }
    assert_eq!(flag.get(), 2);

    // test something with the nullable pointer optimization
    flag.set(0);

    {
        let mut array = ArrayVec::<[_; 3]>::new();
        array.push(vec![Bump(flag)]);
        array.push(vec![Bump(flag), Bump(flag)]);
        array.push(vec![]);
        array.push(vec![Bump(flag)]);
        assert_eq!(flag.get(), 1);
        drop(array.pop());
        assert_eq!(flag.get(), 1);
        drop(array.pop());
        assert_eq!(flag.get(), 3);
    }

    assert_eq!(flag.get(), 4);

    // test into_inner
    flag.set(0);
    {
        let mut array = ArrayVec::<[_; 3]>::new();
        array.push(Bump(flag));
        array.push(Bump(flag));
        array.push(Bump(flag));
        let inner = array.into_inner();
        assert!(inner.is_ok());
        assert_eq!(flag.get(), 0);
        drop(inner);
        assert_eq!(flag.get(), 3);
    }

}

#[test]
fn test_extend() {
    let mut range = 0..10;

    let mut array: ArrayVec<[_; 5]> = range.by_ref().collect();
    assert_eq!(&array[..], &[0, 1, 2, 3, 4]);
    assert_eq!(range.next(), Some(5));

    array.extend(range.by_ref());
    assert_eq!(range.next(), Some(6));

    let mut array: ArrayVec<[_; 10]> = (0..3).collect();
    assert_eq!(&array[..], &[0, 1, 2]);
    array.extend(3..5);
    assert_eq!(&array[..], &[0, 1, 2, 3, 4]);
}

#[test]
fn test_is_send_sync() {
    let data = ArrayVec::<[Vec<i32>; 5]>::new();
    &data as &Send;
    &data as &Sync;
}

#[test]
fn test_compact_size() {
    // Future rust will kill these drop flags!
    // 4 elements size + 1 len + 1 enum tag + [1 drop flag]
    type ByteArray = ArrayVec<[u8; 4]>;
    println!("{}", mem::size_of::<ByteArray>());
    assert!(mem::size_of::<ByteArray>() <= 8);

    // 12 element size + 1 enum tag + 3 padding + 1 len + 1 drop flag + 2 padding
    type QuadArray = ArrayVec<[u32; 3]>;
    println!("{}", mem::size_of::<QuadArray>());
    assert!(mem::size_of::<QuadArray>() <= 24);
}

#[test]
fn test_drain() {
    let mut v = ArrayVec::from([0; 8]);
    v.pop();
    v.drain(0..7);
    assert_eq!(&v[..], &[]);

    v.extend(0..);
    v.drain(1..4);
    assert_eq!(&v[..], &[0, 4, 5, 6, 7]);
    let u: ArrayVec<[_; 3]> = v.drain(1..4).rev().collect();
    assert_eq!(&u[..], &[6, 5, 4]);
    assert_eq!(&v[..], &[0, 7]);
    v.drain(..);
    assert_eq!(&v[..], &[]);
}

#[test]
fn test_retain() {
    let mut v = ArrayVec::from([0; 8]);
    for (i, elt) in v.iter_mut().enumerate() {
        *elt = i;
    }
    v.retain(|_| true);
    assert_eq!(&v[..], &[0, 1, 2, 3, 4, 5, 6, 7]);
    v.retain(|elt| {
        *elt /= 2;
        *elt % 2 == 0
    });
    assert_eq!(&v[..], &[0, 0, 2, 2]);
    v.retain(|_| false);
    assert_eq!(&v[..], &[]);
}

#[test]
#[should_panic]
fn test_drain_oob() {
    let mut v = ArrayVec::from([0; 8]);
    v.pop();
    v.drain(0..8);
}

#[test]
#[should_panic]
fn test_drop_panic() {
    struct DropPanic;

    impl Drop for DropPanic {
        fn drop(&mut self) {
            panic!("drop");
        }
    }

    let mut array = ArrayVec::<[DropPanic; 1]>::new();
    array.push(DropPanic);
}

#[test]
#[should_panic]
fn test_drop_panic_into_iter() {
    struct DropPanic;

    impl Drop for DropPanic {
        fn drop(&mut self) {
            panic!("drop");
        }
    }

    let mut array = ArrayVec::<[DropPanic; 1]>::new();
    array.push(DropPanic);
    array.into_iter();
}

#[test]
fn test_insert() {
    let mut v = ArrayVec::from([]);
    assert_eq!(v.push(1), Some(1));
    assert_eq!(v.insert(0, 1), Some(1));

    let mut v = ArrayVec::<[_; 3]>::new();
    v.insert(0, 0);
    v.insert(1, 1);
    v.insert(2, 2);
    v.insert(3, 3);
    assert_eq!(&v[..], &[0, 1, 2]);
    v.insert(1, 9);
    assert_eq!(&v[..], &[0, 9, 1]);

    let mut v = ArrayVec::from([2]);
    assert_eq!(v.insert(1, 1), Some(1));
    assert_eq!(v.insert(2, 1), Some(1));
}

#[test]
fn test_in_option() {
    // Sanity check that we are sound w.r.t Option & non-nullable layout optimization.
    let mut v = Some(ArrayVec::<[&i32; 1]>::new());
    assert!(v.is_some());
    unsafe {
        *v.as_mut().unwrap().get_unchecked_mut(0) = mem::zeroed();
    }
    assert!(v.is_some());
}

#[test]
fn test_into_inner_1() {
    let mut v = ArrayVec::from([1, 2]);
    v.pop();
    let u = v.clone();
    assert_eq!(v.into_inner(), Err(u));
}

#[test]
fn test_into_inner_2() {
    let mut v = ArrayVec::<[String; 4]>::new();
    v.push("a".into());
    v.push("b".into());
    v.push("c".into());
    v.push("d".into());
    assert_eq!(v.into_inner().unwrap(), ["a", "b", "c", "d"]);
}

#[test]
fn test_into_inner_3_() {
    let mut v = ArrayVec::<[i32; 4]>::new();
    v.extend(1..);
    assert_eq!(v.into_inner().unwrap(), [1, 2, 3, 4]);
}

#[test]
fn test_write() {
    use std::io::Write;
    let mut v = ArrayVec::<[_; 8]>::new();
    write!(&mut v, "\x01\x02\x03").unwrap();
    assert_eq!(&v[..], &[1, 2, 3]);
    let r = v.write(&[9; 16]).unwrap();
    assert_eq!(r, 5);
    assert_eq!(&v[..], &[1, 2, 3, 9, 9, 9, 9, 9]);
}

#[test]
fn array_clone_from() {
    let mut v = ArrayVec::<[_; 4]>::new();
    v.push(vec![1, 2]);
    v.push(vec![3, 4, 5]);
    v.push(vec![6]);
    let reference = v.to_vec();
    let mut u = ArrayVec::<[_; 4]>::new();
    u.clone_from(&v);
    assert_eq!(&u, &reference[..]);

    let mut t = ArrayVec::<[_; 4]>::new();
    t.push(vec![97]);
    t.push(vec![]);
    t.push(vec![5, 6, 2]);
    t.push(vec![2]);
    t.clone_from(&v);
    assert_eq!(&t, &reference[..]);
    t.clear();
    t.clone_from(&v);
    assert_eq!(&t, &reference[..]);
}

#[test]
fn test_string() {
    use std::error::Error;

    let text = "hello world";
    let mut s = ArrayString::<[_; 16]>::new();
    s.push_str(text).unwrap();
    assert_eq!(&s, text);
    assert_eq!(text, &s);

    // Make sure Hash / Eq / Borrow match up so we can use HashMap
    let mut map = HashMap::new();
    map.insert(s, 1);
    assert_eq!(map[text], 1);

    let mut t = ArrayString::<[_; 2]>::new();
    assert!(t.push_str(text).is_err());
    assert_eq!(&t, "");

    t.push_str("ab").unwrap();
    // DerefMut
    let tmut: &mut str = &mut t;
    assert_eq!(tmut, "ab");

    // Test Error trait / try
    let t = || -> Result<(), Box<Error>> {
        let mut t = ArrayString::<[_; 2]>::new();
        try!(t.push_str(text));
        Ok(())
    }();
    assert!(t.is_err());
}

#[test]
fn test_string_from() {
    let text = "hello world";
	// Test `from` constructor
    let u = ArrayString::<[_; 11]>::from(text).unwrap();
    assert_eq!(&u, text);
    assert_eq!(u.len(), text.len());
}

#[test]
fn test_string_from_bytes() {
    let text = "hello world";
    let u = ArrayString::from_byte_string(b"hello world").unwrap();
    assert_eq!(&u, text);
    assert_eq!(u.len(), text.len());
}

#[test]
fn test_string_clone() {
    let text = "hi";
    let mut s = ArrayString::<[_; 4]>::new();
    s.push_str("abcd").unwrap();
    let t = ArrayString::<[_; 4]>::from(text).unwrap();
    s.clone_from(&t);
    assert_eq!(&t, &s);
}

#[test]
fn test_string_push() {
    let text = "abcαβγ";
    let mut s = ArrayString::<[_; 8]>::new();
    for c in text.chars() {
        if let Err(_) = s.push(c) {
            break;
        }
    }
    assert_eq!("abcαβ", &s[..]);
    s.push('x').ok();
    assert_eq!("abcαβx", &s[..]);
    assert!(s.push('x').is_err());
}

#[test]
fn test_insert_at_length() {
    let mut v = ArrayVec::<[_; 8]>::new();
    let result1 = v.insert(0, "a");
    let result2 = v.insert(1, "b");
    assert!(result1.is_none() && result2.is_none());
    assert_eq!(&v[..], &["a", "b"]);
}

#[test]
fn test_insert_out_of_bounds() {
    let mut v = ArrayVec::<[_; 8]>::new();
    let result = v.insert(1, "test");
    assert_eq!(result, Some("test"));
    assert_eq!(v.len(), 0);

    let mut u = ArrayVec::from([1, 2, 3, 4]);
    let ret = u.insert(3, 99);
    assert_eq!(&u[..], &[1, 2, 3, 99]);
    assert_eq!(ret, Some(4));
    let ret = u.insert(4, 77);
    assert_eq!(&u[..], &[1, 2, 3, 99]);
    assert_eq!(ret, Some(77));
}

#[test]
fn test_drop_in_insert() {
    use std::cell::Cell;

    let flag = &Cell::new(0);

    struct Bump<'a>(&'a Cell<i32>);

    impl<'a> Drop for Bump<'a> {
        fn drop(&mut self) {
            let n = self.0.get();
            self.0.set(n + 1);
        }
    }

    flag.set(0);

    {
        let mut array = ArrayVec::<[_; 2]>::new();
        array.push(Bump(flag));
        array.insert(0, Bump(flag));
        assert_eq!(flag.get(), 0);
        let ret = array.insert(1, Bump(flag));
        assert_eq!(flag.get(), 0);
        assert!(ret.is_some());
        drop(ret);
        assert_eq!(flag.get(), 1);
    }
    assert_eq!(flag.get(), 3);
}
<commit_msg>Remove failing test (with optimizations)<commit_after>extern crate arrayvec;

use arrayvec::ArrayVec;
use arrayvec::ArrayString;
use std::mem;

use std::collections::HashMap;

#[test]
fn test_simple() {
    use std::ops::Add;

    let mut vec: ArrayVec<[Vec<i32>; 3]> = ArrayVec::new();

    vec.push(vec![1, 2, 3, 4]);
    vec.push(vec![10]);
    vec.push(vec![-1, 13, -2]);

    for elt in &vec {
        assert_eq!(elt.iter().fold(0, Add::add), 10);
    }

    let sum_len = vec.into_iter().map(|x| x.len()).fold(0, Add::add);
    assert_eq!(sum_len, 8);
}

#[test]
fn test_u16_index() {
    const N: usize = 4096;
    let mut vec: ArrayVec<[_; N]> = ArrayVec::new();
    for _ in 0..N {
        assert!(vec.push(1u8).is_none());
    }
    assert!(vec.push(0).is_some());
    assert_eq!(vec.len(), N);
}

#[test]
fn test_iter() {
    let mut iter = ArrayVec::from([1, 2, 3]).into_iter();
    assert_eq!(iter.size_hint(), (3, Some(3)));
    assert_eq!(iter.next_back(), Some(3));
    assert_eq!(iter.next(), Some(1));
    assert_eq!(iter.next_back(), Some(2));
    assert_eq!(iter.size_hint(), (0, Some(0)));
    assert_eq!(iter.next_back(), None);
}

#[test]
fn test_drop() {
    use std::cell::Cell;

    let flag = &Cell::new(0);

    struct Bump<'a>(&'a Cell<i32>);

    impl<'a> Drop for Bump<'a> {
        fn drop(&mut self) {
            let n = self.0.get();
            self.0.set(n + 1);
        }
    }

    {
        let mut array = ArrayVec::<[Bump; 128]>::new();
        array.push(Bump(flag));
        array.push(Bump(flag));
    }
    assert_eq!(flag.get(), 2);

    // test something with the nullable pointer optimization
    flag.set(0);

    {
        let mut array = ArrayVec::<[_; 3]>::new();
        array.push(vec![Bump(flag)]);
        array.push(vec![Bump(flag), Bump(flag)]);
        array.push(vec![]);
        array.push(vec![Bump(flag)]);
        assert_eq!(flag.get(), 1);
        drop(array.pop());
        assert_eq!(flag.get(), 1);
        drop(array.pop());
        assert_eq!(flag.get(), 3);
    }

    assert_eq!(flag.get(), 4);

    // test into_inner
    flag.set(0);
    {
        let mut array = ArrayVec::<[_; 3]>::new();
        array.push(Bump(flag));
        array.push(Bump(flag));
        array.push(Bump(flag));
        let inner = array.into_inner();
        assert!(inner.is_ok());
        assert_eq!(flag.get(), 0);
        drop(inner);
        assert_eq!(flag.get(), 3);
    }

}

#[test]
fn test_extend() {
    let mut range = 0..10;

    let mut array: ArrayVec<[_; 5]> = range.by_ref().collect();
    assert_eq!(&array[..], &[0, 1, 2, 3, 4]);
    assert_eq!(range.next(), Some(5));

    array.extend(range.by_ref());
    assert_eq!(range.next(), Some(6));

    let mut array: ArrayVec<[_; 10]> = (0..3).collect();
    assert_eq!(&array[..], &[0, 1, 2]);
    array.extend(3..5);
    assert_eq!(&array[..], &[0, 1, 2, 3, 4]);
}

#[test]
fn test_is_send_sync() {
    let data = ArrayVec::<[Vec<i32>; 5]>::new();
    &data as &Send;
    &data as &Sync;
}

#[test]
fn test_compact_size() {
    // Future rust will kill these drop flags!
    // 4 elements size + 1 len + 1 enum tag + [1 drop flag]
    type ByteArray = ArrayVec<[u8; 4]>;
    println!("{}", mem::size_of::<ByteArray>());
    assert!(mem::size_of::<ByteArray>() <= 8);

    // 12 element size + 1 enum tag + 3 padding + 1 len + 1 drop flag + 2 padding
    type QuadArray = ArrayVec<[u32; 3]>;
    println!("{}", mem::size_of::<QuadArray>());
    assert!(mem::size_of::<QuadArray>() <= 24);
}

#[test]
fn test_drain() {
    let mut v = ArrayVec::from([0; 8]);
    v.pop();
    v.drain(0..7);
    assert_eq!(&v[..], &[]);

    v.extend(0..);
    v.drain(1..4);
    assert_eq!(&v[..], &[0, 4, 5, 6, 7]);
    let u: ArrayVec<[_; 3]> = v.drain(1..4).rev().collect();
    assert_eq!(&u[..], &[6, 5, 4]);
    assert_eq!(&v[..], &[0, 7]);
    v.drain(..);
    assert_eq!(&v[..], &[]);
}

#[test]
fn test_retain() {
    let mut v = ArrayVec::from([0; 8]);
    for (i, elt) in v.iter_mut().enumerate() {
        *elt = i;
    }
    v.retain(|_| true);
    assert_eq!(&v[..], &[0, 1, 2, 3, 4, 5, 6, 7]);
    v.retain(|elt| {
        *elt /= 2;
        *elt % 2 == 0
    });
    assert_eq!(&v[..], &[0, 0, 2, 2]);
    v.retain(|_| false);
    assert_eq!(&v[..], &[]);
}

#[test]
#[should_panic]
fn test_drain_oob() {
    let mut v = ArrayVec::from([0; 8]);
    v.pop();
    v.drain(0..8);
}

#[test]
#[should_panic]
fn test_drop_panic() {
    struct DropPanic;

    impl Drop for DropPanic {
        fn drop(&mut self) {
            panic!("drop");
        }
    }

    let mut array = ArrayVec::<[DropPanic; 1]>::new();
    array.push(DropPanic);
}

#[test]
#[should_panic]
fn test_drop_panic_into_iter() {
    struct DropPanic;

    impl Drop for DropPanic {
        fn drop(&mut self) {
            panic!("drop");
        }
    }

    let mut array = ArrayVec::<[DropPanic; 1]>::new();
    array.push(DropPanic);
    array.into_iter();
}

#[test]
fn test_insert() {
    let mut v = ArrayVec::from([]);
    assert_eq!(v.push(1), Some(1));
    assert_eq!(v.insert(0, 1), Some(1));

    let mut v = ArrayVec::<[_; 3]>::new();
    v.insert(0, 0);
    v.insert(1, 1);
    v.insert(2, 2);
    v.insert(3, 3);
    assert_eq!(&v[..], &[0, 1, 2]);
    v.insert(1, 9);
    assert_eq!(&v[..], &[0, 9, 1]);

    let mut v = ArrayVec::from([2]);
    assert_eq!(v.insert(1, 1), Some(1));
    assert_eq!(v.insert(2, 1), Some(1));
}

#[test]
fn test_into_inner_1() {
    let mut v = ArrayVec::from([1, 2]);
    v.pop();
    let u = v.clone();
    assert_eq!(v.into_inner(), Err(u));
}

#[test]
fn test_into_inner_2() {
    let mut v = ArrayVec::<[String; 4]>::new();
    v.push("a".into());
    v.push("b".into());
    v.push("c".into());
    v.push("d".into());
    assert_eq!(v.into_inner().unwrap(), ["a", "b", "c", "d"]);
}

#[test]
fn test_into_inner_3_() {
    let mut v = ArrayVec::<[i32; 4]>::new();
    v.extend(1..);
    assert_eq!(v.into_inner().unwrap(), [1, 2, 3, 4]);
}

#[test]
fn test_write() {
    use std::io::Write;
    let mut v = ArrayVec::<[_; 8]>::new();
    write!(&mut v, "\x01\x02\x03").unwrap();
    assert_eq!(&v[..], &[1, 2, 3]);
    let r = v.write(&[9; 16]).unwrap();
    assert_eq!(r, 5);
    assert_eq!(&v[..], &[1, 2, 3, 9, 9, 9, 9, 9]);
}

#[test]
fn array_clone_from() {
    let mut v = ArrayVec::<[_; 4]>::new();
    v.push(vec![1, 2]);
    v.push(vec![3, 4, 5]);
    v.push(vec![6]);
    let reference = v.to_vec();
    let mut u = ArrayVec::<[_; 4]>::new();
    u.clone_from(&v);
    assert_eq!(&u, &reference[..]);

    let mut t = ArrayVec::<[_; 4]>::new();
    t.push(vec![97]);
    t.push(vec![]);
    t.push(vec![5, 6, 2]);
    t.push(vec![2]);
    t.clone_from(&v);
    assert_eq!(&t, &reference[..]);
    t.clear();
    t.clone_from(&v);
    assert_eq!(&t, &reference[..]);
}

#[test]
fn test_string() {
    use std::error::Error;

    let text = "hello world";
    let mut s = ArrayString::<[_; 16]>::new();
    s.push_str(text).unwrap();
    assert_eq!(&s, text);
    assert_eq!(text, &s);

    // Make sure Hash / Eq / Borrow match up so we can use HashMap
    let mut map = HashMap::new();
    map.insert(s, 1);
    assert_eq!(map[text], 1);

    let mut t = ArrayString::<[_; 2]>::new();
    assert!(t.push_str(text).is_err());
    assert_eq!(&t, "");

    t.push_str("ab").unwrap();
    // DerefMut
    let tmut: &mut str = &mut t;
    assert_eq!(tmut, "ab");

    // Test Error trait / try
    let t = || -> Result<(), Box<Error>> {
        let mut t = ArrayString::<[_; 2]>::new();
        try!(t.push_str(text));
        Ok(())
    }();
    assert!(t.is_err());
}

#[test]
fn test_string_from() {
    let text = "hello world";
	// Test `from` constructor
    let u = ArrayString::<[_; 11]>::from(text).unwrap();
    assert_eq!(&u, text);
    assert_eq!(u.len(), text.len());
}

#[test]
fn test_string_from_bytes() {
    let text = "hello world";
    let u = ArrayString::from_byte_string(b"hello world").unwrap();
    assert_eq!(&u, text);
    assert_eq!(u.len(), text.len());
}

#[test]
fn test_string_clone() {
    let text = "hi";
    let mut s = ArrayString::<[_; 4]>::new();
    s.push_str("abcd").unwrap();
    let t = ArrayString::<[_; 4]>::from(text).unwrap();
    s.clone_from(&t);
    assert_eq!(&t, &s);
}

#[test]
fn test_string_push() {
    let text = "abcαβγ";
    let mut s = ArrayString::<[_; 8]>::new();
    for c in text.chars() {
        if let Err(_) = s.push(c) {
            break;
        }
    }
    assert_eq!("abcαβ", &s[..]);
    s.push('x').ok();
    assert_eq!("abcαβx", &s[..]);
    assert!(s.push('x').is_err());
}

#[test]
fn test_insert_at_length() {
    let mut v = ArrayVec::<[_; 8]>::new();
    let result1 = v.insert(0, "a");
    let result2 = v.insert(1, "b");
    assert!(result1.is_none() && result2.is_none());
    assert_eq!(&v[..], &["a", "b"]);
}

#[test]
fn test_insert_out_of_bounds() {
    let mut v = ArrayVec::<[_; 8]>::new();
    let result = v.insert(1, "test");
    assert_eq!(result, Some("test"));
    assert_eq!(v.len(), 0);

    let mut u = ArrayVec::from([1, 2, 3, 4]);
    let ret = u.insert(3, 99);
    assert_eq!(&u[..], &[1, 2, 3, 99]);
    assert_eq!(ret, Some(4));
    let ret = u.insert(4, 77);
    assert_eq!(&u[..], &[1, 2, 3, 99]);
    assert_eq!(ret, Some(77));
}

#[test]
fn test_drop_in_insert() {
    use std::cell::Cell;

    let flag = &Cell::new(0);

    struct Bump<'a>(&'a Cell<i32>);

    impl<'a> Drop for Bump<'a> {
        fn drop(&mut self) {
            let n = self.0.get();
            self.0.set(n + 1);
        }
    }

    flag.set(0);

    {
        let mut array = ArrayVec::<[_; 2]>::new();
        array.push(Bump(flag));
        array.insert(0, Bump(flag));
        assert_eq!(flag.get(), 0);
        let ret = array.insert(1, Bump(flag));
        assert_eq!(flag.get(), 0);
        assert!(ret.is_some());
        drop(ret);
        assert_eq!(flag.get(), 1);
    }
    assert_eq!(flag.get(), 3);
}
<|endoftext|>


<commit_before>//! Layout handling

use rustwlc::handle::{WlcView, WlcOutput};
use std::rc::{Rc, Weak};

pub type Node = Box<Containable>;

#[derive(PartialEq, Clone, Copy)]
pub enum ContainerType {
    /// Root container, only one exists
    Root,
    /// WlcOutput/Monitor
    Output,
    /// A workspace
    Workspace,
    /// A view (window)
    View
}

/// Types of container
enum ContainerTypes {
    /// Root container, only one exists
    Root,
    /// WlcOutput/monitor
    Output,
    /// A workspace
    Workspace,
    /// A view (window)
    View
}

/// Layout mode for a container
enum Layout {
    None,
    Horizontal,
    Vertical,
    Stacked,
    Tabbed,
    Floating
}

/// Like i3, everything (workspaces, containers, views) are containable.
pub trait Containable {
    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Weak<Node>;

    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Option<Vec<Weak<Node>>>;

    /// Gets the type of the container
    fn get_type(&self) -> ContainerType;

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool;

    /// Removes this container and all of its children
    fn remove_container(&self) -> Result<(), &'static str>;

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibility: bool);

    /// Gets the X and Y dimensions of the container
    fn get_dimensions(&self) -> (u64, u64);

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64);

    /// Finds a parent container with the given type, if there is any
    fn get_parent_by_type(&self, type_: ContainerType) -> Option<Rc<Node>> {
        let mut container = self.get_parent().upgrade();
        loop {
            if let Some(parent) = container {
                if parent.get_type() == type_ {
                    return Some(parent);
                }
                container = parent.get_parent().upgrade();
            } else {
                return None;
            }
        }
    }
}

/// View specific functions
pub trait Viewable {
    /// Determines if the view is full screen
    fn is_fullscreen(&self) -> bool;

    /// Figures out if the view is focused
    fn is_active(&self) -> bool;

    /// Returns true if this view is a parent is an ancestor of the child
    fn is_parent_of<T: Containable>(&self, child: T) -> bool;

    /// Returns true if this view is a child is an decedent of the parent
    fn is_child_of<T: Containable>(&self, parent: T) -> bool;

    /// Gets the active workspace of the view
    fn active_workspace<T: Containable>(&self) -> T;
}

struct Container {
    handle: Option<WlcOutput>,

    parent: Weak<Node>,
    children: Vec<Rc<Node>>,
    type_: ContainerType,
    layout: Layout,

    width: u64,
    height: u64,

    x: i64,
    y: i64,

    visible: bool,
    is_focused: bool,
    is_floating: bool,
}

impl Containable for Container {

    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Weak<Node> {
        self.parent.clone()
        /*
        match self.type_ {
            ContainerType::Root => {
                // such hack
                unsafe {
                    // very unsafe
                    use std::mem;
                    let _dummy: Node = mem::uninitialized();
                    // much dummy
                    let _rc_dummy = Rc::new(_dummy);
                    Rc::downgrade(&_rc_dummy)}
            },
            _ => self.parent.clone(),
        }*/
    }
    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Option<Vec<Weak<Node>>> {
        if self.children.len() == 0 {
            None
        } else {
            Some(self.children.iter().map(|child| Rc::downgrade(&child)).collect())
        }
    }

    fn get_type(&self) -> ContainerType {
        self.type_
    }

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool {
        self.is_focused
    }

    /// Removes this container and all of its children
    fn remove_container(&self) -> Result<(), &'static str> {
        for child in self.get_children().expect("No children") {
            if let Some(child) = child.upgrade() {
                if let Ok(child) = Rc::try_unwrap(child) {
                    child.remove_container().ok();
                    drop(child);
                }
            }
        }
        Ok(())
    }

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibility: bool) {
        self.visible = visibility
    }

    /// Gets the X (width) and Y (height) dimensions of the container
    fn get_dimensions(&self) -> (u64, u64) {
        (self.width, self.height)
    }

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64) {
        (self.x, self.y)
    }

}

struct View {
    handle: Option<Box<WlcView>>,
    parent: Weak<Node>,

    width: u64,
    height: u64,

    x: i64,
    y: i64,

    visible: bool,
    is_focused: bool,
    is_floating: bool,
}

impl Containable for View {
    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Weak<Node> {
        self.parent.clone()
    }

    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Option<Vec<Weak<Node>>> {
        None
    }

    /// Gets the type of the container
    fn get_type(&self) -> ContainerType {
        ContainerType::View
    }

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool {
        self.is_focused
    }

    /// Removes this container and all of its children
    fn remove_container(&self) -> Result<(), &'static str> {
        if let Some(ref handle) = self.handle {
            handle.close();
        }
        drop(self);
        Ok(())
    }

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibility: bool) {
        self.visible = visibility
    }

    /// Gets the X and Y dimensions of the container
    fn get_dimensions(&self) -> (u64, u64) {
        (self.width, self.height)
    }

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64) {
        (self.x, self.y)
    }
}
<commit_msg>Removed duplicate enum<commit_after>//! Layout handling

use rustwlc::handle::{WlcView, WlcOutput};
use std::rc::{Rc, Weak};

pub type Node = Box<Containable>;

#[derive(PartialEq, Clone, Copy)]
pub enum ContainerType {
    /// Root container, only one exists
    Root,
    /// WlcOutput/Monitor
    Output,
    /// A workspace
    Workspace,
    /// A view (window)
    View
}

/// Layout mode for a container
enum Layout {
    None,
    Horizontal,
    Vertical,
    Stacked,
    Tabbed,
    Floating
}

/// Like i3, everything (workspaces, containers, views) are containable.
pub trait Containable {
    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Weak<Node>;

    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Option<Vec<Weak<Node>>>;

    /// Gets the type of the container
    fn get_type(&self) -> ContainerType;

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool;

    /// Removes this container and all of its children
    fn remove_container(&self) -> Result<(), &'static str>;

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibility: bool);

    /// Gets the X and Y dimensions of the container
    fn get_dimensions(&self) -> (u64, u64);

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64);

    /// Finds a parent container with the given type, if there is any
    fn get_parent_by_type(&self, type_: ContainerType) -> Option<Rc<Node>> {
        let mut container = self.get_parent().upgrade();
        loop {
            if let Some(parent) = container {
                if parent.get_type() == type_ {
                    return Some(parent);
                }
                container = parent.get_parent().upgrade();
            } else {
                return None;
            }
        }
    }
}

/// View specific functions
pub trait Viewable {
    /// Determines if the view is full screen
    fn is_fullscreen(&self) -> bool;

    /// Figures out if the view is focused
    fn is_active(&self) -> bool;

    /// Returns true if this view is a parent is an ancestor of the child
    fn is_parent_of<T: Containable>(&self, child: T) -> bool;

    /// Returns true if this view is a child is an decedent of the parent
    fn is_child_of<T: Containable>(&self, parent: T) -> bool;

    /// Gets the active workspace of the view
    fn active_workspace<T: Containable>(&self) -> T;
}

struct Container {
    handle: Option<WlcOutput>,

    parent: Weak<Node>,
    children: Vec<Rc<Node>>,
    type_: ContainerType,
    layout: Layout,

    width: u64,
    height: u64,

    x: i64,
    y: i64,

    visible: bool,
    is_focused: bool,
    is_floating: bool,
}

impl Containable for Container {

    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Weak<Node> {
        self.parent.clone()
        /*
        match self.type_ {
            ContainerType::Root => {
                // such hack
                unsafe {
                    // very unsafe
                    use std::mem;
                    let _dummy: Node = mem::uninitialized();
                    // much dummy
                    let _rc_dummy = Rc::new(_dummy);
                    Rc::downgrade(&_rc_dummy)}
            },
            _ => self.parent.clone(),
        }*/
    }
    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Option<Vec<Weak<Node>>> {
        if self.children.len() == 0 {
            None
        } else {
            Some(self.children.iter().map(|child| Rc::downgrade(&child)).collect())
        }
    }

    fn get_type(&self) -> ContainerType {
        self.type_
    }

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool {
        self.is_focused
    }

    /// Removes this container and all of its children
    fn remove_container(&self) -> Result<(), &'static str> {
        for child in self.get_children().expect("No children") {
            if let Some(child) = child.upgrade() {
                if let Ok(child) = Rc::try_unwrap(child) {
                    child.remove_container().ok();
                    drop(child);
                }
            }
        }
        Ok(())
    }

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibility: bool) {
        self.visible = visibility
    }

    /// Gets the X (width) and Y (height) dimensions of the container
    fn get_dimensions(&self) -> (u64, u64) {
        (self.width, self.height)
    }

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64) {
        (self.x, self.y)
    }

}

struct View {
    handle: Option<Box<WlcView>>,
    parent: Weak<Node>,

    width: u64,
    height: u64,

    x: i64,
    y: i64,

    visible: bool,
    is_focused: bool,
    is_floating: bool,
}

impl Containable for View {
    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Weak<Node> {
        self.parent.clone()
    }

    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Option<Vec<Weak<Node>>> {
        None
    }

    /// Gets the type of the container
    fn get_type(&self) -> ContainerType {
        ContainerType::View
    }

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool {
        self.is_focused
    }

    /// Removes this container and all of its children
    fn remove_container(&self) -> Result<(), &'static str> {
        if let Some(ref handle) = self.handle {
            handle.close();
        }
        drop(self);
        Ok(())
    }

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibility: bool) {
        self.visible = visibility
    }

    /// Gets the X and Y dimensions of the container
    fn get_dimensions(&self) -> (u64, u64) {
        (self.width, self.height)
    }

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64) {
        (self.x, self.y)
    }
}
<|endoftext|>


<commit_before>use scroll::{self, ctx, Pread};
use scroll::ctx::SizeWith;
use error;
use container::{self, Container};
use mach::load_command;
use core::fmt::{self, Debug};

pub const NLIST_TYPE_MASK: u8 = 0xe;
pub const NLIST_TYPE_GLOBAL: u8 = 0x1;
pub const NLIST_TYPE_LOCAL: u8 = 0x0;

#[repr(C)]
#[derive(Clone, Copy, Pread, Pwrite, SizeWith)]
pub struct Nlist32 {
    /// index into the string table
    n_strx: u32,
    /// type flag, see below
    n_type: u8,
    /// section number or NO_SECT
    n_sect: u8,
    /// see <mach-o/stab.h>
    n_desc: u16,
    /// value of this symbol (or stab offset)
    n_value: u32,
}

pub const SIZEOF_NLIST_32: usize = 12;

impl Debug for Nlist32 {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "strx: {:04} type: {:#02x} sect: {:#x} desc: {:#03x} value: {:#x}",
               self.n_strx,
               self.n_type,
               self.n_sect,
               self.n_desc,
               self.n_value,
        )
    }
}

#[repr(C)]
#[derive(Clone, Copy, Pread, Pwrite, SizeWith)]
pub struct Nlist64 {
    /// index into the string table
    n_strx: u32,
    /// type flag, see below
    n_type: u8,
    /// section number or NO_SECT
    n_sect: u8,
    /// see <mach-o/stab.h>
    n_desc: u16,
    /// value of this symbol (or stab offset)
    n_value: u64,
}

pub const SIZEOF_NLIST_64: usize = 16;

impl Debug for Nlist64 {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "strx: {:04} type: {:#02x} sect: {:#x} desc: {:#03x} value: {:#x}",
               self.n_strx,
               self.n_type,
               self.n_sect,
               self.n_desc,
               self.n_value,
        )
    }
}

#[derive(Debug, Clone)]
pub struct Nlist {
    /// index into the string table
    n_strx: usize,
    /// type flag, see below
    n_type: u8,
    /// section number or NO_SECT
    n_sect: usize,
    /// see <mach-o/stab.h>
    n_desc: u16,
    /// value of this symbol (or stab offset)
    n_value: u64,
}

impl ctx::SizeWith<container::Ctx> for Nlist {
    type Units = usize;
    fn size_with(ctx: &container::Ctx) -> usize {
        use container::Container;
        match ctx.container {
            Container::Little => {
                SIZEOF_NLIST_32
            },
            Container::Big => {
                SIZEOF_NLIST_64
            },
        }
    }
}

impl From<Nlist32> for Nlist {
    fn from(nlist: Nlist32) -> Self {
        Nlist {
            n_strx: nlist.n_strx as usize,
            n_type: nlist.n_type,
            n_sect: nlist.n_sect as usize,
            n_desc: nlist.n_desc,
            n_value: nlist.n_value as u64,
        }
    }
}

impl From<Nlist64> for Nlist {
    fn from(nlist: Nlist64) -> Self {
        Nlist {
            n_strx: nlist.n_strx as usize,
            n_type: nlist.n_type,
            n_sect: nlist.n_sect as usize,
            n_desc: nlist.n_desc,
            n_value: nlist.n_value,
        }
    }
}

impl<'a> ctx::TryFromCtx<'a, (usize, container::Ctx)> for Nlist {
    type Error = scroll::Error;
    fn try_from_ctx(buffer: &'a [u8], (offset, container::Ctx { container, le }): (usize, container::Ctx)) -> scroll::Result<Self> {
        match container {
            Container::Little => {
                Ok(buffer.pread_with::<Nlist32>(offset, le)?.into())
            },
            Container::Big => {
                Ok(buffer.pread_with::<Nlist64>(offset, le)?.into())
            },
        }
    }
}

#[derive(Debug, Clone, Copy, Default)]
pub struct SymbolsCtx {
    pub nsyms: usize,
    pub strtab: usize,
    pub ctx: container::Ctx,
}

impl<'a, T: ?Sized> ctx::TryFromCtx<'a, (usize, SymbolsCtx), T> for Symbols<'a> where T: AsRef<[u8]> {
    type Error = scroll::Error;
    fn try_from_ctx(buffer: &'a T, (offset, SymbolsCtx {
        nsyms, strtab, ctx
    }): (usize, SymbolsCtx)) -> scroll::Result<Self> {
        Ok (Symbols {
            data: buffer.as_ref(),
            start: offset,
            nsyms: nsyms,
            strtab: strtab,
            ctx: ctx,
        })
    }
}

/// A zero-copy "nlist" style symbol table ("stab"), including the string table
pub struct Symbols<'a> {
    data: &'a [u8],
    start: usize,
    // TODO: add iterator and remove pub
    pub nsyms: usize,
    // TODO: we can use an actual strtab here and tie it to symbols lifetime
    strtab: usize,
    ctx: container::Ctx,
}

impl<'a> Symbols<'a> {
    /// Creates a new symbol table with `count` elements, from the `start` offset, using the string table at `strtab`, with a _default_ ctx.
    ////
    /// **Beware**, this will provide incorrect results if you construct this on a 32-bit mach binary, using a 64-bit machine; use `parse` instead if you want 32/64 bit support
    pub fn new<B: AsRef<[u8]>> (b: &B, start: usize, count: usize, strtab: usize) -> error::Result<Symbols> {
        let nsyms = count;
        Ok (Symbols {
            data: b.as_ref(),
            start: start,
            nsyms: nsyms,
            strtab: strtab,
            ctx: container::Ctx::default(),
        })
    }
    pub fn parse<'b, B: AsRef<[u8]>> (bytes: &'b B, symtab: &load_command::SymtabCommand, ctx: container::Ctx) -> error::Result<Symbols<'b>> {
        Ok(bytes.pread_with(symtab.symoff as usize, SymbolsCtx { nsyms: symtab.nsyms as usize, strtab: symtab.stroff as usize, ctx: ctx })?)
    }

    /// Parses a single Nlist symbol from the binary
    pub fn get(&self, index: usize) -> scroll::Result<Nlist> {
        self.data.pread_with(self.start + (index * Nlist::size_with(&self.ctx)), self.ctx)
    }
}

impl<'a> Debug for Symbols<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        writeln!(fmt, "Symbols: {{")?;
        for i in 0..self.nsyms {
            match self.get(i) {
                Ok(sym) => {
                    let name = self.data.pread::<&str>(self.strtab + sym.n_strx as usize);
                    writeln!(fmt, "  Name: {:?} {:?}", name, sym)?;
                },
                Err(error) => {
                    writeln!(fmt, "  Bad symbol index: {}, {:?}", i, error)?;
                }
            }
        }
        writeln!(fmt, "}}")
    }
}
<commit_msg>mach.symbols: return name str along with symbol; update debug printer<commit_after>use scroll::{self, ctx, Pread};
use scroll::ctx::SizeWith;
use error;
use container::{self, Container};
use mach::load_command;
use core::fmt::{self, Debug};

pub const NLIST_TYPE_MASK: u8 = 0xe;
pub const NLIST_TYPE_GLOBAL: u8 = 0x1;
pub const NLIST_TYPE_LOCAL: u8 = 0x0;

#[repr(C)]
#[derive(Clone, Copy, Pread, Pwrite, SizeWith)]
pub struct Nlist32 {
    /// index into the string table
    n_strx: u32,
    /// type flag, see below
    n_type: u8,
    /// section number or NO_SECT
    n_sect: u8,
    /// see <mach-o/stab.h>
    n_desc: u16,
    /// value of this symbol (or stab offset)
    n_value: u32,
}

pub const SIZEOF_NLIST_32: usize = 12;

impl Debug for Nlist32 {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "strx: {:04} type: {:#02x} sect: {:#x} desc: {:#03x} value: {:#x}",
               self.n_strx,
               self.n_type,
               self.n_sect,
               self.n_desc,
               self.n_value,
        )
    }
}

#[repr(C)]
#[derive(Clone, Copy, Pread, Pwrite, SizeWith)]
pub struct Nlist64 {
    /// index into the string table
    n_strx: u32,
    /// type flag, see below
    n_type: u8,
    /// section number or NO_SECT
    n_sect: u8,
    /// see <mach-o/stab.h>
    n_desc: u16,
    /// value of this symbol (or stab offset)
    n_value: u64,
}

pub const SIZEOF_NLIST_64: usize = 16;

impl Debug for Nlist64 {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "strx: {:04} type: {:#02x} sect: {:#x} desc: {:#03x} value: {:#x}",
               self.n_strx,
               self.n_type,
               self.n_sect,
               self.n_desc,
               self.n_value,
        )
    }
}

#[derive(Debug, Clone)]
pub struct Nlist {
    /// index into the string table
    n_strx: usize,
    /// type flag, see below
    n_type: u8,
    /// section number or NO_SECT
    n_sect: usize,
    /// see <mach-o/stab.h>
    n_desc: u16,
    /// value of this symbol (or stab offset)
    n_value: u64,
}

impl ctx::SizeWith<container::Ctx> for Nlist {
    type Units = usize;
    fn size_with(ctx: &container::Ctx) -> usize {
        use container::Container;
        match ctx.container {
            Container::Little => {
                SIZEOF_NLIST_32
            },
            Container::Big => {
                SIZEOF_NLIST_64
            },
        }
    }
}

impl From<Nlist32> for Nlist {
    fn from(nlist: Nlist32) -> Self {
        Nlist {
            n_strx: nlist.n_strx as usize,
            n_type: nlist.n_type,
            n_sect: nlist.n_sect as usize,
            n_desc: nlist.n_desc,
            n_value: nlist.n_value as u64,
        }
    }
}

impl From<Nlist64> for Nlist {
    fn from(nlist: Nlist64) -> Self {
        Nlist {
            n_strx: nlist.n_strx as usize,
            n_type: nlist.n_type,
            n_sect: nlist.n_sect as usize,
            n_desc: nlist.n_desc,
            n_value: nlist.n_value,
        }
    }
}

impl<'a> ctx::TryFromCtx<'a, (usize, container::Ctx)> for Nlist {
    type Error = scroll::Error;
    fn try_from_ctx(buffer: &'a [u8], (offset, container::Ctx { container, le }): (usize, container::Ctx)) -> scroll::Result<Self> {
        match container {
            Container::Little => {
                Ok(buffer.pread_with::<Nlist32>(offset, le)?.into())
            },
            Container::Big => {
                Ok(buffer.pread_with::<Nlist64>(offset, le)?.into())
            },
        }
    }
}

#[derive(Debug, Clone, Copy, Default)]
pub struct SymbolsCtx {
    pub nsyms: usize,
    pub strtab: usize,
    pub ctx: container::Ctx,
}

impl<'a, T: ?Sized> ctx::TryFromCtx<'a, (usize, SymbolsCtx), T> for Symbols<'a> where T: AsRef<[u8]> {
    type Error = scroll::Error;
    fn try_from_ctx(buffer: &'a T, (offset, SymbolsCtx {
        nsyms, strtab, ctx
    }): (usize, SymbolsCtx)) -> scroll::Result<Self> {
        Ok (Symbols {
            data: buffer.as_ref(),
            start: offset,
            nsyms: nsyms,
            strtab: strtab,
            ctx: ctx,
        })
    }
}

/// A zero-copy "nlist" style symbol table ("stab"), including the string table
pub struct Symbols<'a> {
    data: &'a [u8],
    start: usize,
    // TODO: add iterator and remove pub
    pub nsyms: usize,
    // TODO: we can use an actual strtab here and tie it to symbols lifetime
    strtab: usize,
    ctx: container::Ctx,
}

impl<'a> Symbols<'a> {
    /// Creates a new symbol table with `count` elements, from the `start` offset, using the string table at `strtab`, with a _default_ ctx.
    ////
    /// **Beware**, this will provide incorrect results if you construct this on a 32-bit mach binary, using a 64-bit machine; use `parse` instead if you want 32/64 bit support
    pub fn new<B: AsRef<[u8]>> (b: &B, start: usize, count: usize, strtab: usize) -> error::Result<Symbols> {
        let nsyms = count;
        Ok (Symbols {
            data: b.as_ref(),
            start: start,
            nsyms: nsyms,
            strtab: strtab,
            ctx: container::Ctx::default(),
        })
    }
    pub fn parse<'b, B: AsRef<[u8]>> (bytes: &'b B, symtab: &load_command::SymtabCommand, ctx: container::Ctx) -> error::Result<Symbols<'b>> {
        Ok(bytes.pread_with(symtab.symoff as usize, SymbolsCtx { nsyms: symtab.nsyms as usize, strtab: symtab.stroff as usize, ctx: ctx })?)
    }

    /// Parses a single Nlist symbol from the binary, with its accompanying name
    pub fn get(&self, index: usize) -> scroll::Result<(&str, Nlist)> {
        let sym: Nlist = self.data.pread_with(self.start + (index * Nlist::size_with(&self.ctx)), self.ctx)?;
        let name = self.data.pread(self.strtab + sym.n_strx)?;
        Ok((name, sym))
    }
}

impl<'a> Debug for Symbols<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        writeln!(fmt, "Symbols: {{")?;
        for i in 0..self.nsyms {
            match self.get(i) {
                Ok((name, nlist)) => {
                    writeln!(fmt, "{: >10x} {} sect: {:#x} type: {:#02x} desc: {:#03x}", nlist.n_value, name, nlist.n_sect, nlist.n_type, nlist.n_desc)?;
                },
                Err(error) => {
                    writeln!(fmt, "  Bad symbol, index: {}, sym: {:?}", i, error)?;
                }
            }
        }
        writeln!(fmt, "}}")
    }
}
<|endoftext|>