<commit_before>extern crate regex;

extern crate rusoto_core;
extern crate rusoto_sts;
extern crate rusoto_dynamodb;

pub mod SessionHandler {
    use std::fs::{File, OpenOptions};
    use std::io::BufReader;
    use std::io;
    use std::io::prelude::*;
    use std::io::{stdin, stdout};
    use std::env;
    use rusoto_core::{ProfileProvider, Region, default_tls_client};
    use rusoto_sts::{Sts, StsClient, GetSessionTokenRequest, GetSessionTokenResponse, GetSessionTokenError, Credentials};
    use std::collections::HashMap;

    struct AWSConfig {
        profiles: HashMap<String, AWSProfile>,
    }

    #[derive(Debug)]
    struct AWSProfile {
        source_profile: Option<String>,
        region: Option<String>,
        output: Option<String>,
        role_arn: Option<String>,
        mfa_serial: Option<String>,
        ot_session_name: Option<String>,
        ot_expiration: Option<String>,
        ot_source_profile: Option<String>,
        ot_role_arn: Option<String>,
    }

    impl AWSConfig {
        fn get_profile(&self, name: &str) -> &AWSProfile {
            &self.profiles[name]
        }
    }

    pub fn create(profile_name: &str) {
        println!("Loading config file");
        let aws_config = load_config();
        println!("Creating session for profile \"{}\"...", profile_name);
        let aws_profile = aws_config.get_profile(profile_name);

        let mut provider = ProfileProvider::new().unwrap();
        provider.set_profile(profile_name);
        // let region = &aws_profile.region;
        // match region {
        //     &Some(ref region) => region,
        //     &None => panic!("You must specify a region for profile {}", profile_name)
        // };
        // println!("Region: {:?}", region.as_ref().unwrap());
        let client = StsClient::new(default_tls_client().unwrap(), provider, Region::EuWest1);

        let mfa_serial = &aws_profile.mfa_serial;
        if mfa_serial.is_some() {
            print!("Enter AWS MFA code for profile [{}]: ", profile_name);
            stdout().flush();
            let mut token_code = String::new();
            stdin().read_line(&mut token_code)
                .ok()
                .expect("Couldn't read line");
            token_code.pop(); // Remove newline

            let request = GetSessionTokenRequest {
                duration_seconds: None,
                serial_number: Some(mfa_serial.as_ref().unwrap().to_owned()),
                token_code: Some(token_code),
            };
            let response = client.get_session_token(&request);
            match response {
                Ok(response) => {
                    match save_profile(profile_name, &aws_profile) {
                        Ok(_) => println!("Saved profile to config file."),
                        Err(err) => println!("Error saving profile config to file: {:?}", err)
                    };
                    let credentials = response.credentials.unwrap();
                    match save_credentials(profile_name, credentials) {
                        Ok(_) => println!("Saved to credentials file."),
                        Err(err) => println!("Error saving credentials to file: {:?}", err)
                    };
                },
                Err(err) => panic!("Failed to get session token for profile {}: {:?}", profile_name, err),
            };
        }
    }
    pub fn show(profile_name: &str) {
        println!("Showing config for profile {}...", profile_name);
    }

    pub fn refresh() {
        println!("Refreshing sessions...");
    }

    pub fn clean() {
        println!("Cleaning sessions...");
    }

    fn load_config() -> AWSConfig {
        let aws_config_file = read_aws_config_file();
        let profiles = split_config_file(aws_config_file);

        // Create an instance of the AWSConfig struct
        let mut aws_config = AWSConfig {
            profiles: HashMap::new()
        };

        // Iterate over profile chunks
        for profile in profiles {
            // Create a vector of string that holds each line
            let split = profile.split("\n");
            let lines: Vec<String> = split.map(|s| s.to_string()).collect();

            // Get the profile name from the first line
            let profile_line = lines[0].to_owned();

            // Lets ignore the default for now
            if profile_line == "[default]" {
                continue;
            }

            let split2 = profile_line.split(" ");
            let mut words: Vec<String> = split2.map(|s| s.to_string()).collect();
            let mut profile_name = String::new();
            if words.len() > 1 {
                words[1].pop();
                profile_name = words[1].trim_right().to_owned();
            }

            let mut aws_profile = AWSProfile{
                source_profile: None,
                region: None,
                output: None,
                role_arn: None,
                mfa_serial: None,
                ot_expiration: None,
                ot_session_name: None,
                ot_source_profile: None,
                ot_role_arn: None,
            };

            for i in 1..lines.len() {
                let split = lines[i].split(" = ");
                let config: Vec<String> = split.map(|s| s.to_string()).collect();
                let key = &config[0];
                let value = config[1].to_owned();
                match key.as_ref() {
                    "mfa_serial" => aws_profile.mfa_serial = Some(value),
                    "output" => aws_profile.output = Some(value),
                    "region" => aws_profile.region = Some(value),
                    "role_arn" => aws_profile.role_arn = Some(value),
                    "source_profile" => aws_profile.source_profile = Some(value),
                    "ot_session_name" => aws_profile.ot_session_name = Some(value),
                    "ot_expiration" => aws_profile.ot_expiration = Some(value),
                    "ot_source_profile" => aws_profile.ot_source_profile = Some(value),
                    "ot_role_arn" => aws_profile.ot_role_arn = Some(value),
                    _ => (),
                }
            }

            if profile_name != "" {
                aws_config.profiles.insert(profile_name.clone(), aws_profile);
            }
        }

        aws_config
    }

    fn read_aws_config_file() -> String {
        let path = match env::home_dir() {
            Some(path) => path,
            None => panic!("Could not retrieve user's home directory."),
        };
        let config_file_path = format!("{}/.aws/config", path.display());

        let f = File::open(config_file_path).expect("Could not find AWS config file.");
        let mut buf_reader = BufReader::new(f);
        let mut contents = String::new();
        buf_reader.read_to_string(&mut contents).expect("Found config file but could not read it.");

        contents
    }

    fn split_config_file(aws_config: String) -> Vec<String> {
        let split = aws_config.split("\n\n");
        let mut profiles: Vec<String> = split.map(|s| s.to_string()).collect();;

        profiles.pop(); // Remove last element as it is always empty
        profiles
    }

    fn save_credentials(profile_name: &str, credentials: Credentials) -> Result<(), io::Error> {
        let mut aws_credentials_path = env::home_dir().unwrap().display().to_string();
        aws_credentials_path.push_str("/.aws/credentials");
        let mut file = OpenOptions::new()
                       .write(true)
                       .append(true)
                       .open(aws_credentials_path)
                       .unwrap();

        let mut creds = String::new();
        creds.push_str(&format!("[{}-session]\n", profile_name));
        creds.push_str(&format!("aws_access_key_id = {}\n", credentials.access_key_id));
        creds.push_str(&format!("expiration = {}\n", credentials.expiration));
        creds.push_str(&format!("aws_secret_access_key = {}\n", credentials.secret_access_key));
        creds.push_str(&format!("aws_session_token = {}\n", credentials.session_token));
        try!(file.write_all(creds.as_bytes()));
        Ok(())
    }

    fn save_profile(profile_name: &str, aws_profile: &AWSProfile) -> Result<(), io::Error> {
        let mut aws_config_path = env::home_dir().unwrap().display().to_string();
        aws_config_path.push_str("/.aws/config");
        let mut file = OpenOptions::new()
                       .write(true)
                       .append(true)
                       .open(aws_config_path)
                       .unwrap();

        let region = &aws_profile.region;
        match region {
            &Some(ref region) => {
                let mut prof = String::new();
                prof.push_str(&format!("[profile {}-session]\n", profile_name));
                prof.push_str(&format!("region = {}\n", region));
                try!(file.write_all(prof.as_bytes()));
                Ok(())
            },
            &None => {
                let region = "us-east-1";
                let mut prof = String::new();
                prof.push_str(&format!("[{}-session]\n", profile_name));
                prof.push_str(&format!("region = {}\n", region));
                try!(file.write_all(prof.as_bytes()));
                Ok(())
            },
        }
    }
}
<commit_msg>Refactoring and check if session for profile already exists<commit_after>extern crate regex;

extern crate rusoto_core;
extern crate rusoto_sts;
extern crate rusoto_dynamodb;

pub mod SessionHandler {
    use std::fs::{File, OpenOptions};
    use std::io::BufReader;
    use std::io;
    use std::io::prelude::*;
    use std::io::{stdin, stdout};
    use std::env;
    use rusoto_core::{ProfileProvider, Region, default_tls_client};
    use rusoto_sts::{Sts, StsClient, GetSessionTokenRequest, GetSessionTokenResponse, GetSessionTokenError, Credentials};
    use std::collections::HashMap;

    struct AWSConfig {
        profiles: HashMap<String, AWSProfile>,
    }

    #[derive(Debug)]
    struct AWSProfile {
        source_profile: Option<String>,
        region: Option<String>,
        output: Option<String>,
        role_arn: Option<String>,
        mfa_serial: Option<String>,
        ot_session_name: Option<String>,
        ot_expiration: Option<String>,
        ot_source_profile: Option<String>,
        ot_role_arn: Option<String>,
    }

    impl AWSConfig {
        fn get_profile(&self, name: &str) -> &AWSProfile {
            if (&self.profiles).contains_key(name) {
                &self.profiles[name]
            }
            else {
                panic!("Could not find profile {}", name);
            }
        }

        fn contains_profile(&self, name: &str) -> bool {
            if (&self.profiles).contains_key(name) {
                true
            }
            else {
                false
            }
        }
    }

    pub fn create(profile_name: &str) {
        println!("Loading config file");
        let aws_config = load_config();

        let mut session_name = profile_name.clone().to_string();
        session_name.push_str("-session");
        if aws_config.contains_profile(&session_name) {
            println!("Session {} for profile {} already exists", session_name, profile_name);
        }

        println!("Creating session for profile \"{}\"...", profile_name);
        let aws_profile = aws_config.get_profile(profile_name);

        let mut provider = ProfileProvider::new().unwrap();
        provider.set_profile(profile_name);
        // let region = &aws_profile.region;
        // match region {
        //     &Some(ref region) => region,
        //     &None => panic!("You must specify a region for profile {}", profile_name)
        // };
        // println!("Region: {:?}", region.as_ref().unwrap());
        let client = StsClient::new(default_tls_client().unwrap(), provider, Region::EuWest1);

        let mfa_serial = &aws_profile.mfa_serial;
        if mfa_serial.is_some() {
            print!("Enter AWS MFA code for profile [{}]: ", profile_name);
            stdout().flush();
            let mut token_code = String::new();
            stdin().read_line(&mut token_code)
                .ok()
                .expect("Couldn't read line");
            token_code.pop(); // Remove newline

            let request = GetSessionTokenRequest {
                duration_seconds: None,
                serial_number: Some(mfa_serial.as_ref().unwrap().to_owned()),
                token_code: Some(token_code),
            };
            let response = client.get_session_token(&request);
            match response {
                Ok(response) => {
                    match save_profile(profile_name, &aws_profile) {
                        Ok(_) => println!("Saved profile to config file."),
                        Err(err) => println!("Error saving profile config to file: {:?}", err)
                    };
                    let credentials = response.credentials.unwrap();
                    match save_credentials(profile_name, credentials) {
                        Ok(_) => println!("Saved to credentials file."),
                        Err(err) => println!("Error saving credentials to file: {:?}", err)
                    };
                },
                Err(err) => panic!("Failed to get session token for profile {}: {:?}", profile_name, err),
            };
        }
    }
    pub fn show(profile_name: &str) {
        println!("Showing config for profile {}...", profile_name);
    }

    pub fn refresh() {
        println!("Refreshing sessions...");
    }

    pub fn clean() {
        println!("Cleaning sessions...");
    }

    fn load_config() -> AWSConfig {
        let aws_config_file = read_aws_config_file();
        let profiles = split_config_file(aws_config_file);

        // Create an instance of the AWSConfig struct
        let mut aws_config = AWSConfig {
            profiles: HashMap::new()
        };

        // Iterate over profile chunks
        for profile in profiles {
            // Create a vector of string that holds each line
            let split = profile.split("\n");
            let lines: Vec<String> = split.map(|s| s.to_string()).collect();

            // Get the profile name from the first line
            let profile_line = lines[0].to_owned();

            // Lets ignore the default for now
            if profile_line == "[default]" {
                continue;
            }

            let split2 = profile_line.split(" ");
            let mut words: Vec<String> = split2.map(|s| s.to_string()).collect();
            let mut profile_name = String::new();
            if words.len() > 1 {
                words[1].pop();
                profile_name = words[1].trim_right().to_owned();
            }

            let mut aws_profile = AWSProfile{
                source_profile: None,
                region: None,
                output: None,
                role_arn: None,
                mfa_serial: None,
                ot_expiration: None,
                ot_session_name: None,
                ot_source_profile: None,
                ot_role_arn: None,
            };

            for i in 1..lines.len() {
                let split = lines[i].split(" = ");
                let config: Vec<String> = split.map(|s| s.to_string()).collect();
                let key = &config[0];
                let value = config[1].to_owned();
                match key.as_ref() {
                    "mfa_serial" => aws_profile.mfa_serial = Some(value),
                    "output" => aws_profile.output = Some(value),
                    "region" => aws_profile.region = Some(value),
                    "role_arn" => aws_profile.role_arn = Some(value),
                    "source_profile" => aws_profile.source_profile = Some(value),
                    "ot_session_name" => aws_profile.ot_session_name = Some(value),
                    "ot_expiration" => aws_profile.ot_expiration = Some(value),
                    "ot_source_profile" => aws_profile.ot_source_profile = Some(value),
                    "ot_role_arn" => aws_profile.ot_role_arn = Some(value),
                    _ => (),
                }
            }

            if profile_name != "" {
                aws_config.profiles.insert(profile_name.clone(), aws_profile);
            }
        }

        aws_config
    }

    fn read_aws_config_file() -> String {
        let path = match env::home_dir() {
            Some(path) => path,
            None => panic!("Could not retrieve user's home directory."),
        };
        let config_file_path = format!("{}/.aws/config", path.display());

        let f = File::open(config_file_path).expect("Could not find AWS config file.");
        let mut buf_reader = BufReader::new(f);
        let mut contents = String::new();
        buf_reader.read_to_string(&mut contents).expect("Found config file but could not read it.");

        contents
    }

    fn split_config_file(aws_config: String) -> Vec<String> {
        let split = aws_config.split("\n\n");
        let mut profiles: Vec<String> = split.map(|s| s.to_string()).collect();;

        profiles.pop(); // Remove last element as it is always empty
        profiles
    }

    fn save_credentials(profile_name: &str, credentials: Credentials) -> Result<(), io::Error> {
        let mut aws_credentials_path = env::home_dir().unwrap().display().to_string();
        aws_credentials_path.push_str("/.aws/credentials");
        let mut file = OpenOptions::new()
                       .write(true)
                       .append(true)
                       .open(aws_credentials_path)
                       .unwrap();

        let mut creds = String::new();
        creds.push_str(&format!("[{}-session]\n", profile_name));
        creds.push_str(&format!("aws_access_key_id = {}\n", credentials.access_key_id));
        creds.push_str(&format!("expiration = {}\n", credentials.expiration));
        creds.push_str(&format!("aws_secret_access_key = {}\n", credentials.secret_access_key));
        creds.push_str(&format!("aws_session_token = {}\n\n", credentials.session_token));
        try!(file.write_all(creds.as_bytes()));
        Ok(())
    }

    fn save_profile(profile_name: &str, aws_profile: &AWSProfile) -> Result<(), io::Error> {
        let mut aws_config_path = env::home_dir().unwrap().display().to_string();
        aws_config_path.push_str("/.aws/config");
        let mut file = OpenOptions::new()
                       .write(true)
                       .append(true)
                       .open(aws_config_path)
                       .unwrap();

        let region = &aws_profile.region;
        match region {
            &Some(ref region) => {
                let mut prof = String::new();
                prof.push_str(&format!("[profile {}-session]\n", profile_name));
                prof.push_str(&format!("region = {}\n\n", region));
                try!(file.write_all(prof.as_bytes()));
                Ok(())
            },
            &None => {
                let region = "us-east-1";
                let mut prof = String::new();
                prof.push_str(&format!("[profile {}-session]\n", profile_name));
                prof.push_str(&format!("region = {}\n\n", region));
                try!(file.write_all(prof.as_bytes()));
                Ok(())
            },
        }
    }
}
<|endoftext|>


<commit_before>#![deny(missing_docs)]

//! A 3D game engine with built-in editor.

#[macro_use]
extern crate bitflags;
extern crate vecmath;
extern crate piston_window;
extern crate sdl2_window;
#[macro_use]
extern crate gfx;
extern crate gfx_device_gl;
extern crate piston_meta;
extern crate camera_controllers;
extern crate gfx_debug_draw;
extern crate gfx_text;
#[macro_use]
extern crate log;
extern crate range;
#[macro_use]
extern crate conrod;
extern crate find_folder;

pub use math::Vec3;
pub use math::Mat4;
pub use math::AABB;
pub use math::Ray;

pub use world::World;

pub mod math;
pub mod render;
pub mod logger;
pub mod world;
pub mod data;

/// Starts Turbine pointing it to a project folder.
pub fn start(project_folder: &str) {
    use piston_window::*;
    use sdl2_window::Sdl2Window;
    use camera_controllers::*;
    use conrod::{ Ui, Theme };
    use gfx_debug_draw::DebugRenderer;
    use math::{ Matrix, Vector };

    println!("
~~~~~~~~   TURBINE   ~~~~~~~~\n\
=============================\n\
Camera navigation (on/off): C\n\
Camera control: WASD\n\
");

    let mut window: PistonWindow<(), Sdl2Window> =
        WindowSettings::new("Turbine", [1024, 768])
        .exit_on_esc(true)
        .samples(4)
        .build()
        .unwrap();

    logger::init().unwrap();

    let mut capture_cursor = true;

    window.set_capture_cursor(capture_cursor);

    let fov = 90.0;
    let near = 0.1;
    let far = 1000.0;
    let get_projection = |draw_size: Size| {
        CameraPerspective {
            fov: fov, near_clip: near, far_clip: far,
            aspect_ratio: (draw_size.width as f32) / (draw_size.height as f32)
        }.projection()
    };

    let mut projection = get_projection(window.draw_size());
    let mut first_person = FirstPerson::new(
        [0.5, 0.5, 4.0],
        FirstPersonSettings::keyboard_wasd()
    );

    let mut debug_renderer = {
        let text_renderer =
            gfx_text::new(window.factory.borrow().clone()).unwrap();
        DebugRenderer::new(window.factory.borrow().clone(),
            text_renderer, 64).ok().unwrap()
    };

    let mut cursor_pos = [0.0, 0.0];
    let mut ground_pos = [0.0, 0.0, 0.0];
    let mut ortho = false;

    let mut ui = {
        let assets = find_folder::Search::ParentsThenKids(3, 3)
            .for_folder("assets").unwrap();
        let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf");
        let mut theme = Theme::default();
        theme.font_size_medium = 12;
        let glyph_cache = Glyphs::new(&font_path, window.factory.borrow().clone());
        Ui::new(glyph_cache.unwrap(), theme)
    };

    let mut world = World::new();

    {
        // Create folders if they do not exists.
        data::create_folders(project_folder).unwrap();

        // Load entities.
        let files = data::entities::files(project_folder).unwrap();
        data::entities::load(&mut world, &files).unwrap();
    }

    widget_ids!(REFRESH);

    for mut e in window {
        if capture_cursor {
            first_person.event(&e);
        }
        e.draw_3d(|stream| {
            let draw_size = e.draw_size();
            let draw_size = [draw_size.width, draw_size.height];
            let args = e.render_args().unwrap();
            let camera = first_person.camera(args.ext_dt);
            let mvp = model_view_projection(
                Matrix::id(),
                camera.orthogonal(),
                projection
            );
            render::clear(stream);

            render::entity_current_positions(&world, &mut debug_renderer);

            render::axes(&camera, projection, draw_size, &mut debug_renderer);
            let yellow = [1.0, 1.0, 0.0, 1.0];
            debug_renderer.draw_marker(ground_pos, 0.1, yellow);
            debug_renderer.render(stream, mvp).unwrap();
        });
        if !capture_cursor {
            use conrod::*;

            ui.handle_event(&e);
            e.update(|_| ui.set_widgets(|ui| {
                Button::new()
                    .color(color::blue())
                    .top_left()
                    .w_h(60.0, 30.0)
                    .label("refresh")
                    .react(|| {})
                    .set(REFRESH, ui);
            }));
            e.draw_2d(|c, g| {
                ui.draw(c, g);
            });
        }
        e.resize(|_, _| {
            if !ortho {
                projection = get_projection(e.draw_size());
            }
        });
        if let Some(pos) = e.mouse_cursor_args() {
            cursor_pos = pos;
        }
        if let Some(Button::Keyboard(Key::C)) = e.press_args() {
            capture_cursor = !capture_cursor;
            e.set_capture_cursor(capture_cursor);
        }
        if let Some(Button::Keyboard(Key::O)) = e.press_args() {
            ortho = !ortho;
            if ortho {
                projection = Matrix::id();
            } else {
                projection = get_projection(e.draw_size());
            }
        }
        if let Some(Button::Mouse(MouseButton::Left)) = e.press_args() {
            if !capture_cursor {
                let draw_size = e.draw_size();
                let draw_size = [draw_size.width, draw_size.height];
                let ray = Ray::from_2d(cursor_pos, draw_size, fov, near, far);
                let view_to_world = first_person.camera(0.0).orthogonal().inv();
                match view_to_world.ray(ray).ground_plane() {
                    None => info!("Click on the ground to add entity"),
                    Some(pos) => {
                        ground_pos = pos;
                        world.add_entity(pos);
                    }
                }
            }
        }
    }

    {
        // Save entities data.
        let entities_folder = data::entities::folder(project_folder);
        data::entities::save(&mut world, entities_folder).unwrap();
    }
}

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

    #[test]
    fn entity_syntax() {
        let _ = load_syntax_data("assets/entity/syntax.txt",
            "assets/entity/test-cube.txt");
    }
}
<commit_msg>Fix bug when UI appears first time<commit_after>#![deny(missing_docs)]

//! A 3D game engine with built-in editor.

#[macro_use]
extern crate bitflags;
extern crate vecmath;
extern crate piston_window;
extern crate sdl2_window;
#[macro_use]
extern crate gfx;
extern crate gfx_device_gl;
extern crate piston_meta;
extern crate camera_controllers;
extern crate gfx_debug_draw;
extern crate gfx_text;
#[macro_use]
extern crate log;
extern crate range;
#[macro_use]
extern crate conrod;
extern crate find_folder;

pub use math::Vec3;
pub use math::Mat4;
pub use math::AABB;
pub use math::Ray;

pub use world::World;

pub mod math;
pub mod render;
pub mod logger;
pub mod world;
pub mod data;

/// Starts Turbine pointing it to a project folder.
pub fn start(project_folder: &str) {
    use piston_window::*;
    use sdl2_window::Sdl2Window;
    use camera_controllers::*;
    use conrod::{ Ui, Theme };
    use gfx_debug_draw::DebugRenderer;
    use math::{ Matrix, Vector };

    println!("
~~~~~~~~   TURBINE   ~~~~~~~~\n\
=============================\n\
Camera navigation (on/off): C\n\
Camera control: WASD\n\
");

    let mut window: PistonWindow<(), Sdl2Window> =
        WindowSettings::new("Turbine", [1024, 768])
        .exit_on_esc(true)
        .samples(4)
        .build()
        .unwrap();

    logger::init().unwrap();

    let mut capture_cursor = true;

    window.set_capture_cursor(capture_cursor);

    let fov = 90.0;
    let near = 0.1;
    let far = 1000.0;
    let get_projection = |draw_size: Size| {
        CameraPerspective {
            fov: fov, near_clip: near, far_clip: far,
            aspect_ratio: (draw_size.width as f32) / (draw_size.height as f32)
        }.projection()
    };

    let mut projection = get_projection(window.draw_size());
    let mut first_person = FirstPerson::new(
        [0.5, 0.5, 4.0],
        FirstPersonSettings::keyboard_wasd()
    );

    let mut debug_renderer = {
        let text_renderer =
            gfx_text::new(window.factory.borrow().clone()).unwrap();
        DebugRenderer::new(window.factory.borrow().clone(),
            text_renderer, 64).ok().unwrap()
    };

    let mut cursor_pos = [0.0, 0.0];
    let mut ground_pos = [0.0, 0.0, 0.0];
    let mut ortho = false;

    let mut ui = {
        let assets = find_folder::Search::ParentsThenKids(3, 3)
            .for_folder("assets").unwrap();
        let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf");
        let mut theme = Theme::default();
        theme.font_size_medium = 12;
        let glyph_cache = Glyphs::new(&font_path, window.factory.borrow().clone());
        Ui::new(glyph_cache.unwrap(), theme)
    };

    let mut world = World::new();

    {
        // Create folders if they do not exists.
        data::create_folders(project_folder).unwrap();

        // Load entities.
        let files = data::entities::files(project_folder).unwrap();
        data::entities::load(&mut world, &files).unwrap();
    }

    widget_ids!(REFRESH);

    for mut e in window {
        if capture_cursor {
            first_person.event(&e);
        }
        e.draw_3d(|stream| {
            let draw_size = e.draw_size();
            let draw_size = [draw_size.width, draw_size.height];
            let args = e.render_args().unwrap();
            let camera = first_person.camera(args.ext_dt);
            let mvp = model_view_projection(
                Matrix::id(),
                camera.orthogonal(),
                projection
            );
            render::clear(stream);

            render::entity_current_positions(&world, &mut debug_renderer);

            render::axes(&camera, projection, draw_size, &mut debug_renderer);
            let yellow = [1.0, 1.0, 0.0, 1.0];
            debug_renderer.draw_marker(ground_pos, 0.1, yellow);
            debug_renderer.render(stream, mvp).unwrap();
        });
        if capture_cursor {
            // Send render events to make Conrod update window size.
            if e.render_args().is_some() {
                ui.handle_event(&e);
            }
        } else {
            use conrod::*;

            ui.handle_event(&e);
            e.update(|_| ui.set_widgets(|ui| {
                Button::new()
                    .color(color::blue())
                    .top_left()
                    .w_h(60.0, 30.0)
                    .label("refresh")
                    .react(|| {})
                    .set(REFRESH, ui);
            }));
            e.draw_2d(|c, g| {
                ui.draw(c, g);
            });
        }
        e.resize(|_, _| {
            if !ortho {
                projection = get_projection(e.draw_size());
            }
        });
        if let Some(pos) = e.mouse_cursor_args() {
            cursor_pos = pos;
        }
        if let Some(Button::Keyboard(Key::C)) = e.press_args() {
            capture_cursor = !capture_cursor;
            e.set_capture_cursor(capture_cursor);
        }
        if let Some(Button::Keyboard(Key::O)) = e.press_args() {
            ortho = !ortho;
            if ortho {
                projection = Matrix::id();
            } else {
                projection = get_projection(e.draw_size());
            }
        }
        if let Some(Button::Mouse(MouseButton::Left)) = e.press_args() {
            if !capture_cursor {
                let draw_size = e.draw_size();
                let draw_size = [draw_size.width, draw_size.height];
                let ray = Ray::from_2d(cursor_pos, draw_size, fov, near, far);
                let view_to_world = first_person.camera(0.0).orthogonal().inv();
                match view_to_world.ray(ray).ground_plane() {
                    None => info!("Click on the ground to add entity"),
                    Some(pos) => {
                        ground_pos = pos;
                        world.add_entity(pos);
                    }
                }
            }
        }
    }

    {
        // Save entities data.
        let entities_folder = data::entities::folder(project_folder);
        data::entities::save(&mut world, entities_folder).unwrap();
    }
}

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

    #[test]
    fn entity_syntax() {
        let _ = load_syntax_data("assets/entity/syntax.txt",
            "assets/entity/test-cube.txt");
    }
}
<|endoftext|>


<commit_before>extern crate core;

pub mod json_option;

pub use json_option::JsonOption;

use std::collections::HashMap;
use std::iter::Peekable;
use std::str::Chars;

use core::fmt::{ Display, Formatter, Error };

pub struct Json;

impl Json {
    pub fn parse(text: &str) -> JsonOption {
        let mut char_slice = text.chars();
        Json::parse_slice(&mut (&mut char_slice).peekable())
    }

    pub fn parse_slice(slice: &mut Peekable<&mut Chars>) -> JsonOption {
        let mut content = JsonOption::None;

        'tokenizer: loop {
            let current = match slice.peek() {
                Some(chr) => *chr,
                _         => { break 'tokenizer; }
            };

            match current {
                ' ' | '\r' | '\n' | '\t' => {
                    slice.next();
                },
                '0'...'9' | '-' => {
                    content = Json::parse_number(slice);
                },
                '"' => {
                    content = Json::parse_string(slice);
                },
                '[' => {
                    content = Json::parse_array(slice);
                },
                '{' => {
                    content = Json::parse_object(slice);
                },
                _ => { break 'tokenizer; }
            }
        }

        content
    }

    fn parse_number(slice: &mut Peekable<&mut Chars>) -> JsonOption {
        let mut token    = String::from("");
        let mut is_float = false;

        'tokenizer: loop {
            let chr = match slice.peek() {
                Some(chr) => *chr,
                None      => break 'tokenizer,
            };

            match chr {
                '-' | '0'...'9' => {
                    token.push(chr);
                    slice.next();
                },
                '.' => {
                    token.push(chr);
                    is_float = true;
                    slice.next();
                },
                _ => { break 'tokenizer; }
            }
        }

        if is_float {
            JsonOption::Number(token.parse::<f32>().unwrap())
        } else {
            JsonOption::Integer(token.parse::<i32>().unwrap())
        }
    }

    fn parse_string(slice: &mut Peekable<&mut Chars>) -> JsonOption {
        let mut token      = String::from("");
        let mut proper_end = false;
        let mut is_escaped = false;

        // Consume starting "
        slice.next();

        'tokenizer: for chr in slice {
            match chr {
                '\\' => {
                    if is_escaped {
                        token.push(chr);
                        is_escaped = false;

                    } else {
                        is_escaped = true;
                    }
                },
                '"' => {
                    if is_escaped {
                        token.push(chr);
                        is_escaped = false;

                    } else {
                        proper_end = true;
                        break 'tokenizer;
                    }
                },
                _ => { token.push(chr); }
            }
        }

        if !proper_end {
            panic!("String wasn't properly terminated.");
        }

        JsonOption::String(token)
    }

    fn parse_array(slice: &mut Peekable<&mut Chars>) -> JsonOption {
        let mut content: Vec<JsonOption> = vec![];
        let mut proper_sep = true;
        let mut proper_end = false;

        // Consume starting [
        slice.next();

        'tokenizer: loop {
            loop {
                let chr = match slice.peek() {
                    Some(chr) => *chr,
                    None      => { println!("Broke"); break 'tokenizer },
                };

                match chr {
                    ' ' | '\n' | '\r' | '\t' => { slice.next(); },
                    ',' => {
                        if proper_sep {
                            panic!("Array has no elements between separator.");
                        }

                        proper_sep = true;
                        slice.next();
                    },
                    ']' => {
                        proper_sep = true;
                        proper_end = true;
                        slice.next();
                        break 'tokenizer;
                    },
                    _ => {
                        if !proper_sep {
                            break 'tokenizer;
                        }

                        content.push(Json::parse_slice(slice));
                        proper_sep = false;
                    }
                }
            }
        }

        if !proper_sep {
            panic!("Array has elements without separator.");
        }

        if !proper_end {
            panic!("Array wasn't properly terminated.");
        }

        JsonOption::Array(content)
    }

    fn parse_object(slice: &mut Peekable<&mut Chars>) -> JsonOption {
        let mut content    = HashMap::new();
        let mut proper_kv  = false;
        let mut proper_sep = true;
        let mut proper_end = false;

        let mut value;
        // Consume starting {
        slice.next();

        'tokenizer: loop {
            let mut key   = String::from("");

            'sepcheck: loop {
                let chr = match slice.peek() {
                    Some(chr) => *chr,
                    None      => { println!("Broke"); break 'tokenizer },
                };

                match chr {
                    ' ' | '\n' | '\r' | '\t' => { slice.next(); },
                    ',' => {
                        if proper_sep {
                            panic!("Object has no elements between separator.");
                        }

                        proper_sep = true;
                        slice.next();
                    },
                    ':' => {
                        if proper_kv {
                            panic!("Object has two or more key-value separators together.");
                        }

                        proper_kv = true;
                        slice.next();
                    },
                    '}' => {
                        proper_sep = true;
                        proper_end = true;
                        slice.next();
                        break 'tokenizer;
                    },
                    _ => {
                        if !proper_sep {
                            break 'tokenizer;
                        }

                        if !proper_kv && (key != "") {
                            panic!("Object has no separator between key and value.");
                        }

                        if !proper_kv {
                            key = match Json::parse_slice(slice) {
                                JsonOption::String(string) => string,
                                _ => panic!("Object has invalid element as key."),
                            };

                        } else {
                            value      = Json::parse_slice(slice);
                            proper_kv  = false;
                            proper_sep = false;
                            break 'sepcheck;
                        }
                    }
                }
            }

            content.insert(key, value);
        }

        if !proper_sep {
            panic!("Object has elements without separator.");
        }

        if !proper_end {
            panic!("Object wasn't properly terminated.");
        }

        println!("Token: {:?}", content);

        JsonOption::Object(content)
    }
}

impl Display for Json {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
        write!(formatter, "{}", self)
    }
}

<commit_msg>Removing debug println<commit_after>extern crate core;

pub mod json_option;

pub use json_option::JsonOption;

use std::collections::HashMap;
use std::iter::Peekable;
use std::str::Chars;

use core::fmt::{ Display, Formatter, Error };

pub struct Json;

impl Json {
    pub fn parse(text: &str) -> JsonOption {
        let mut char_slice = text.chars();
        Json::parse_slice(&mut (&mut char_slice).peekable())
    }

    pub fn parse_slice(slice: &mut Peekable<&mut Chars>) -> JsonOption {
        let mut content = JsonOption::None;

        'tokenizer: loop {
            let current = match slice.peek() {
                Some(chr) => *chr,
                _         => { break 'tokenizer; }
            };

            match current {
                ' ' | '\r' | '\n' | '\t' => {
                    slice.next();
                },
                '0'...'9' | '-' => {
                    content = Json::parse_number(slice);
                },
                '"' => {
                    content = Json::parse_string(slice);
                },
                '[' => {
                    content = Json::parse_array(slice);
                },
                '{' => {
                    content = Json::parse_object(slice);
                },
                _ => { break 'tokenizer; }
            }
        }

        content
    }

    fn parse_number(slice: &mut Peekable<&mut Chars>) -> JsonOption {
        let mut token    = String::from("");
        let mut is_float = false;

        'tokenizer: loop {
            let chr = match slice.peek() {
                Some(chr) => *chr,
                None      => break 'tokenizer,
            };

            match chr {
                '-' | '0'...'9' => {
                    token.push(chr);
                    slice.next();
                },
                '.' => {
                    token.push(chr);
                    is_float = true;
                    slice.next();
                },
                _ => { break 'tokenizer; }
            }
        }

        if is_float {
            JsonOption::Number(token.parse::<f32>().unwrap())
        } else {
            JsonOption::Integer(token.parse::<i32>().unwrap())
        }
    }

    fn parse_string(slice: &mut Peekable<&mut Chars>) -> JsonOption {
        let mut token      = String::from("");
        let mut proper_end = false;
        let mut is_escaped = false;

        // Consume starting "
        slice.next();

        'tokenizer: for chr in slice {
            match chr {
                '\\' => {
                    if is_escaped {
                        token.push(chr);
                        is_escaped = false;

                    } else {
                        is_escaped = true;
                    }
                },
                '"' => {
                    if is_escaped {
                        token.push(chr);
                        is_escaped = false;

                    } else {
                        proper_end = true;
                        break 'tokenizer;
                    }
                },
                _ => { token.push(chr); }
            }
        }

        if !proper_end {
            panic!("String wasn't properly terminated.");
        }

        JsonOption::String(token)
    }

    fn parse_array(slice: &mut Peekable<&mut Chars>) -> JsonOption {
        let mut content: Vec<JsonOption> = vec![];
        let mut proper_sep = true;
        let mut proper_end = false;

        // Consume starting [
        slice.next();

        'tokenizer: loop {
            loop {
                let chr = match slice.peek() {
                    Some(chr) => *chr,
                    None      => { println!("Broke"); break 'tokenizer },
                };

                match chr {
                    ' ' | '\n' | '\r' | '\t' => { slice.next(); },
                    ',' => {
                        if proper_sep {
                            panic!("Array has no elements between separator.");
                        }

                        proper_sep = true;
                        slice.next();
                    },
                    ']' => {
                        proper_sep = true;
                        proper_end = true;
                        slice.next();
                        break 'tokenizer;
                    },
                    _ => {
                        if !proper_sep {
                            break 'tokenizer;
                        }

                        content.push(Json::parse_slice(slice));
                        proper_sep = false;
                    }
                }
            }
        }

        if !proper_sep {
            panic!("Array has elements without separator.");
        }

        if !proper_end {
            panic!("Array wasn't properly terminated.");
        }

        JsonOption::Array(content)
    }

    fn parse_object(slice: &mut Peekable<&mut Chars>) -> JsonOption {
        let mut content    = HashMap::new();
        let mut proper_kv  = false;
        let mut proper_sep = true;
        let mut proper_end = false;

        let mut value;
        // Consume starting {
        slice.next();

        'tokenizer: loop {
            let mut key   = String::from("");

            'sepcheck: loop {
                let chr = match slice.peek() {
                    Some(chr) => *chr,
                    None      => { println!("Broke"); break 'tokenizer },
                };

                match chr {
                    ' ' | '\n' | '\r' | '\t' => { slice.next(); },
                    ',' => {
                        if proper_sep {
                            panic!("Object has no elements between separator.");
                        }

                        proper_sep = true;
                        slice.next();
                    },
                    ':' => {
                        if proper_kv {
                            panic!("Object has two or more key-value separators together.");
                        }

                        proper_kv = true;
                        slice.next();
                    },
                    '}' => {
                        proper_sep = true;
                        proper_end = true;
                        slice.next();
                        break 'tokenizer;
                    },
                    _ => {
                        if !proper_sep {
                            break 'tokenizer;
                        }

                        if !proper_kv && (key != "") {
                            panic!("Object has no separator between key and value.");
                        }

                        if !proper_kv {
                            key = match Json::parse_slice(slice) {
                                JsonOption::String(string) => string,
                                _ => panic!("Object has invalid element as key."),
                            };

                        } else {
                            value      = Json::parse_slice(slice);
                            proper_kv  = false;
                            proper_sep = false;
                            break 'sepcheck;
                        }
                    }
                }
            }

            content.insert(key, value);
        }

        if !proper_sep {
            panic!("Object has elements without separator.");
        }

        if !proper_end {
            panic!("Object wasn't properly terminated.");
        }

        JsonOption::Object(content)
    }
}

impl Display for Json {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
        write!(formatter, "{}", self)
    }
}

<|endoftext|>


<commit_before>extern crate rusoto;
// use rusoto::Region;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;

pub mod auth {
    use rusoto::{ChainProvider, ProfileProvider};
    // From:
    // https://github.comm/InQuicker/kaws/blob/master/src/aws.rs
    #[warn(dead_code)]
    pub fn credentials_provider(path: Option<&str>, profile: Option<&str>) -> ChainProvider {
        let mut profile_provider = ProfileProvider::new().expect(
            "Failed to create AWS credentials provider."
        );

        if let Some(path) = path {
            profile_provider.set_file_path(path);
        }

        if let Some(profile) = profile {
            profile_provider.set_profile(profile);
        }

        ChainProvider::with_profile_provider(profile_provider)
    }
}

// The general idea for saving and restoring paths will be that first the cache will be consulted
// when looking for a resource.  If the resource is found, bingo.
//
// In the case(s) where the resource can't be found, try the API, and if the API call is successful,
// record the updated data.  If it is not so successful, then avoid clobbering the current data.
//
// Also, add a way to generally indicate whether we do want to clobber the cached data

// Note: do I want to have a flag or a struct to define whether or not I should do some of these things?  Like a
// D_NO_CLOBBER_CACHE_FILE or something?
pub mod ec2_instances {
    use rusoto::ec2::{Instance, Reservation};
    use std::collections::HashMap;

    use std::fs::{File, rename};
    use std::path::Path;
    use std::error::Error;
    use std::io::prelude::*;
    use std::io;

    use serde_json;

    static cache_dir: &'static str= "/tmp/"; // In the future, this will be a config and a runtime option

    // A flat structure to make searching for an instance faster, with a
    // link back to the instance.

    #[derive(Clone, Debug, Serialize, Deserialize)]
    pub struct AshufInfo {
        pub instance_id: String,
        pub private_ip_addresses: Vec<String>,
        pub public_ip_addresses: Vec<String>,
        pub state_name: String,
        pub launch_time: String,
        pub availability_zone: String,
        pub image_ami: String,
        pub tags: HashMap<String, String>,
    }

    pub fn ec2_res_to_instances(reservations: Vec<Reservation>) -> Vec<Instance> {
        /// The ec2 `describe-instances` call returns a structure that describe
        /// reservations, and the reservations contain instances.
        /// Since I pretty much never, ever, ever need to know about reservations,
        /// and always care about instances, I am removing the reservations info
        /// and creating a vector of instances.

        let mut instances = Vec::new();
        for res in reservations {
            for res_instances in res.instances {
                for inst in res_instances {
                    instances.push(inst);
                }
            }
        }
        instances
    }

    // fn get_saved_json(cache_path_dir, api, age) -> Option(<File>) {
    //     /// Look at the file at the provided path, and if the age of the
    //     /// file is less than the specified age, get ec2 instance info
    //     /// from it instead of from the api.

    //     let specified_age = 600; // seconds
    // }

    pub fn write_saved_json(age: i32, data: &Vec<AshufInfo>) -> io::Result<()> {
        // Interesting: in rust you can concat a &str onto a String.
        // Deref coercecions may be an interesting topic?
        let pathname = cache_dir.clone().to_owned() + "ec2_instances.json";
        let tmp_pathname = pathname.to_owned() + "tmp";

        println!("starting");

        let mut cache_file_new = File::create(Path::new(&tmp_pathname))?;
        let json_bytes = match serde_json::to_string(data) {
            Err(_) => "{}".to_string(),
            Ok(output) => output
        };
        cache_file_new.write_all(json_bytes.as_bytes())?;
        // cache_file_new.write_all(serde_json::to_string(&data).unwrap().as_bytes());

        println!("Wrote the tmp cache file");

        rename(tmp_pathname, pathname)?;

        println!("Re-named the local cache file after getting new data");

        Ok(())

    }
}
<commit_msg>Enable caching<commit_after>extern crate rusoto_core;
extern crate rusoto_ec2;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate chrono;

use chrono::prelude::*;

pub mod auth {
    use rusoto_core::{ChainProvider, ProfileProvider};
    // From:
    // https://github.comm/InQuicker/kaws/blob/master/src/aws.rs
    #[warn(dead_code)]
    pub fn credentials_provider(path: Option<&str>, profile: Option<&str>) -> ChainProvider {
        let mut profile_provider = ProfileProvider::new().expect(
            "Failed to create AWS credentials provider."
        );

        if let Some(path) = path {
            profile_provider.set_file_path(path);
        }

        if let Some(profile) = profile {
            profile_provider.set_profile(profile);
        }

        ChainProvider::with_profile_provider(profile_provider)
    }
}

// The general idea for saving and restoring paths will be that first the cache will be consulted
// when looking for a resource.  If the resource is found, bingo.
//
// In the case(s) where the resource can't be found, try the API, and if the API call is successful,
// record the updated data.  If it is not so successful, then avoid clobbering the current data.
//
// Also, add a way to generally indicate whether we do want to clobber the cached data

// Note: do I want to have a flag or a struct to define whether or not I should do some of these things?  Like a
// D_NO_CLOBBER_CACHE_FILE or something?
pub mod ec2_instances {
    use rusoto_ec2::{Instance, Reservation};
    use std::collections::HashMap;

    use std::fs::{File, rename};
    use std::path::Path;
    use std::error::Error;
    use std::io::prelude::*;
    use std::io;

    use chrono::prelude::*;
    use chrono::Duration;

    use serde_json;

    // In the future, this will be a config and a runtime option
    // Also in the future, bless a tuple of environment variable that will
    // distinguish account data (Account, API, region) seems reasonable.
    // "global" seems like an appropriate choice for global APIs like IAM, route53.
    static cache_dir: &'static str= "/tmp/";

    // A flat structure to make searching for an instance faster, with a
    // link back to the instance.

    #[derive(Clone, Debug, Serialize, Deserialize)]
    pub struct AshufInfo {
        pub instance_id: String,
        pub private_ip_addresses: Vec<String>,
        pub public_ip_addresses: Vec<String>,
        pub state_name: String,
        pub launch_time: String,
        pub availability_zone: String,
        pub image_ami: String,
        pub tags: HashMap<String, String>,
    }

    #[derive(Clone, Debug, Serialize, Deserialize)]
    pub struct CacheData { // How we'll cache our data
        written_time: DateTime<Utc>,
        instance_data: Vec<AshufInfo>,
    }

    pub fn ec2_res_to_instances(reservations: Vec<Reservation>) -> Vec<Instance> {
        /// The ec2 `describe-instances` call returns a structure that describe
        /// reservations, and the reservations contain instances.
        /// Since I pretty much never, ever, ever need to know about reservations,
        /// and always care about instances, I am removing the reservations info
        /// and creating a vector of instances.

        let mut instances = Vec::new();
        for res in reservations {
            for res_instances in res.instances {
                for inst in res_instances {
                    instances.push(inst);
                }
            }
        }
        instances
    }

    pub fn ec2_cached_data(cache_path_dir: String, account: &String, cache_lifetime: i64) -> Result<Vec<AshufInfo>, String> {
        /// Look at the file at the provided path, and if the age of the
        /// file is less than the specified age, get ec2 instance info
        /// from it instead of from the api.
        /// Otherwise go through the API and return that data
        let data = match(read_saved_json(&account)) {
            Ok(saved_data) => saved_data,
            Err(error) => return Err(format!("{} while opening {}", error, "cache file"))
        };
        let difference = Utc::now().signed_duration_since(data.written_time); // Note that the order matters here.
        println!("Difference is {}", difference);

        if difference > Duration::seconds(cache_lifetime) {
            println!("Got data, and the time is valid");
            Ok(data.instance_data)
        } else {
            Err("Expired".to_string())
        }
    }

    /// This function is for saving the data from a call to the API. It's for
    /// this side-effect only
    // XXX: add support for (API, region)
    pub fn write_saved_json(account: &String, data: &Vec<AshufInfo>) -> io::Result<String> {
        // Interesting: in rust you can concat a &str onto a String.
        // Deref coercecions may be an interesting topic?
        let pathname = format!("{}/{}_ec2_instances.json", cache_dir, account);

        let tmp_pathname = pathname.to_owned() + "tmp";
        let utcnow = Utc::now();

        println!("starting");

        let mut cache_file_new = File::create(Path::new(&tmp_pathname))?;
        let cache_data = CacheData {
            written_time: Utc::now(),
            instance_data: data.to_owned(),
        };
        let json_bytes = match serde_json::to_string(&cache_data) {
            Err(_) => "{}".to_string(),
            Ok(output) => output
        };
        cache_file_new.write_all(json_bytes.as_bytes())?;
        // cache_file_new.write_all(serde_json::to_string(&data).unwrap().as_bytes());

        println!("Wrote the tmp cache file");

        rename(tmp_pathname, pathname)?;

        println!("Re-named the local cache file after getting new data");

        Ok("Cache written out".to_string())
    }

    pub fn read_saved_json(account: &String) -> io::Result<CacheData> {
        let pathname = format!("{}/{}_ec2_instances.json", cache_dir, account);
        let mut file_bytes = String::new();
        let mut cache_file = File::open(Path::new(&pathname))?;

        println!("starting read_saved_json; path to the instances file is known, and file is opened");
        let cache_file_read = cache_file.read_to_string(&mut file_bytes).expect("Something went wrong");
        let instance_data: CacheData = serde_json::from_str(&file_bytes)?;
        Ok(instance_data)
    }
}
<|endoftext|>


<commit_before>/// for HTTPS
extern crate hyper;
/// to decode JSON data
extern crate rustc_serialize;

use std::fmt;
use std::io::Read;
use std::result::{Result};
use rustc_serialize::json::Json;
use hyper::{Client, Url};

static TESTAPIKEY: &'static str = "YOUR API KEY FROM themoviedb.org";

/// the struct to keep
pub struct TheMovieDB {
	/// the all mighty api key we need for each request
	api_key: &'static str,
	/// the base url for themoviedb.org
	base_url: &'static str,
	/// the base url for all images, comes from the /configuration URL
	image_base_url: String,
	/// the HTTPS client we use for getting data from the API
	http_client: Client,
}

impl fmt::Debug for TheMovieDB {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "TheMovieDB => ApiKey: {}, BaseURL: {}, ImageBaseURL: {}",
		 self.api_key,
		 self.base_url,
		 self.image_base_url)
    }
}

impl TheMovieDB {
	/// Constructs a new TheMovieDB instance.
	///
	/// # Examples
	///
	/// ```
	/// use std::rc::Rc;
	///
	/// let five = Rc::new(5);
	/// ```
	fn new(themoviedb_api_key: &'static str) -> Result<TheMovieDB, String> {
		let mut new_instance = TheMovieDB{api_key: themoviedb_api_key,
			base_url: "https://api.themoviedb.org/3",
			image_base_url: "".to_string(),
			http_client: Client::new()};

		let configuration_data = match new_instance.get_json_data_for("/configuration".to_string()) {
			Ok(c) => c,
			Err(error_string) => return Err(error_string)
		};

		let json_obj = match configuration_data.as_object() {
			Some(o) => o,
			None => return Err("Error while getting JSON object".to_string())
		};

		new_instance.image_base_url = json_obj.get("images").unwrap().as_object().unwrap().get("secure_base_url").unwrap().to_string();

		Ok(new_instance)
	}

	fn search(& self, search_term: &'static str) -> Result<String, String> {
		let search_data = match self.get_json_data_for(format!("/search/multi?query={}", search_term)) {
			Ok(d) => d,
			Err(error_string) => return Err(error_string)
		};

		Ok("TEST".to_string())
	}

	/// Returns a URL as String for the given API method
	///
	/// It also automatically adds the api key
	fn get_url_for(& self, url_string: String) -> Result<Url, String> {
		if url_string.contains("?") {
			let result_url = match Url::parse(&format!("{}{}&api_key={}", self.base_url, url_string, self.api_key)) {
				Ok(u) => u,
				Err(_) => return Err(format!("Error buildign URL"))
			};
			Ok(result_url)
		 } else {
			 let result_url = match Url::parse(&format!("{}{}?api_key={}", self.base_url, url_string, self.api_key)) {
 				Ok(u) => u,
 				Err(_) => return Err(format!("Error buildign URL"))
 			};
 			Ok(result_url)
		}
	}

	/// Returns the data for a given API method or an error string if something has failed
	fn get_json_data_for(& self, url_string: String) -> Result<Json, String> {
		let call_url = match self.get_url_for(url_string) {
			Ok(u) => u,
			Err(error_string) => return Err(error_string)
		};

		let call_url_string = format!("{}", call_url);
		let mut response = match self.http_client.get(call_url).send() {
			Ok(r) => r,
			Err(_) => {
				return Err(format!("Error during HTTPS request with URL {}", call_url_string));
			}
 		};

		if hyper::Ok != response.status {
			return Err(format!("No HTTPS Status 200 (OK) for url: {}", call_url_string));
		}

		let mut result = String::new();
		if response.read_to_string(& mut result).is_err() {
			return Err("Error reading response".to_string());
		}

		let json_data = match Json::from_str(&result) {
			Ok(d) => d,
			Err(_) => return Err("Error while readin JSON data".to_string())
		};

		Ok(json_data)
	}
}

#[test]
fn initialization_test() {
	TheMovieDB::new(TESTAPIKEY).unwrap();
}

#[test]
fn search_test() {
	let themoviedb = TheMovieDB::new(TESTAPIKEY).unwrap();
	let search_result = themoviedb.search("The Avengers").unwrap();
}
<commit_msg>Improved structure and fixed multi search<commit_after>/// for HTTPS
extern crate hyper;
/// to decode JSON data
extern crate rustc_serialize;

static TESTAPIKEY: &'static str = "YOUR API KEY FROM themoviedb.org";

pub mod themoviedb_api {
	use std::fmt;
	use std::io::Read;
	use std::result::{Result};
	use rustc_serialize::json::{Json, Object};
	use hyper;

	/// the struct to keep
	pub struct TheMovieDB {
		/// the all mighty api key we need for each request
		api_key: &'static str,
		/// the base url for themoviedb.org
		base_url: &'static str,
		/// the base url for all images, comes from the /configuration URL
		image_base_url: String,
		/// the HTTPS client we use for getting data from the API
		http_client: hyper::Client,
	}

	#[derive(Default)]
	#[derive(Debug)]
	pub struct TheMovieDBMovie {
		pub title: Option<String>,
		pub original_title: Option<String>
	}

	pub enum TheMovieDBItem {
		Movie(TheMovieDBMovie),
		TVShow {original_title: String}
	}

	impl fmt::Debug for TheMovieDB {
	    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
	        write!(f, "TheMovieDB => ApiKey: {}, BaseURL: {}, ImageBaseURL: {}",
			 self.api_key,
			 self.base_url,
			 self.image_base_url)
	    }
	}

	impl TheMovieDB {
		/// Constructs a new TheMovieDB instance.
		///
		pub fn new(themoviedb_api_key: &'static str) -> Result<TheMovieDB, String> {
			let mut new_instance = TheMovieDB{api_key: themoviedb_api_key,
				base_url: "https://api.themoviedb.org/3",
				image_base_url: "".to_string(),
				http_client: hyper::Client::new()};

			let configuration_data = try!(new_instance.get_json_data_for("/configuration".to_string()));

			let json_obj = match configuration_data.as_object() {
				Some(o) => o,
				None => return Err("Error while getting JSON object".to_string())
			};

			new_instance.image_base_url = json_obj.get("images").unwrap().as_object().unwrap().get("secure_base_url").unwrap().to_string();

			Ok(new_instance)
		}

		pub fn search(& self, search_term: &'static str, maximum_results: usize) -> Result<Vec<TheMovieDBItem>, String> {
			// we start at the first page
			let mut page = 1;

			// find the higher number between number of results
			// requested and number returned from API
			let mut final_maximum_results = maximum_results;

			// here we save all results and return them
			let mut results: Vec<TheMovieDBItem> = vec![];

			// now loop until we have enough results
			// or if there aren't anymore
			loop {
				// download data
				let search_data = try!(self.get_json_data_for(format!("/search/multi?query={}&page={}", search_term, page)));

				// create a JSON object from the result
				let json_object = match search_data.as_object() {
					Some(o) => o,
					None => return Err("JSON Data is no object".to_string())
				};

				// get the total number of results
				let search_totals = match TheMovieDB::get_object_field_as_u64(json_object, "total_results") {
					Some(r) => r as usize,
					None => return Err("No results returned".to_string())
				};

				// get the total number of pages
				let page_totals = match TheMovieDB::get_object_field_as_u64(json_object, "total_pages") {
					Some(r) => r as usize,
					None => return Err("No page results returned".to_string())
				};

				if final_maximum_results > search_totals {
					final_maximum_results = search_totals;
				}

				// get the total number of results
				let search_results_json = match json_object.get("results") {
					Some(r) => r,
					None => return Err("No results returned".to_string())
				};

				let search_results = match search_results_json.as_array() {
					Some(r) => r,
					None => return Err("total_results not a number?!?!?".to_string())
				};

				for search_result in search_results {
					let result_object = match search_result.as_object() {
						Some(r) => r,
						None => continue
					};

					let mut new_movie: TheMovieDBMovie = Default::default();
					new_movie.original_title = TheMovieDB::get_object_field_as_string(result_object, "original_title");
					new_movie.title = TheMovieDB::get_object_field_as_string(result_object, "title");
					results.push(TheMovieDBItem::Movie(new_movie));

					if results.len() == final_maximum_results {
						return Ok(results);
					}
				}

				if page >= page_totals {
					return Ok(results);
				}

				page += 1;
			};
		}

		fn get_object_field_as_string(json_object: &Object, field_name: &'static str) -> Option<String> {
			let field = match json_object.get(field_name) {
				Some(f) => f,
				None => return None
			};

			let field_value = match field.as_string() {
				Some(v) => v,
				None => return None
			};

			Some(field_value.to_string())
		}

		fn get_object_field_as_u64(json_object: &Object, field_name: &'static str) -> Option<u64> {
			let field = match json_object.get(field_name) {
				Some(f) => f,
				None => return None
			};

			let field_value = match field.as_u64() {
				Some(v) => v,
				None => return None
			};

			Some(field_value)
		}

		/// Returns a URL as String for the given API method
		///
		/// It also automatically adds the api key
		fn get_url_for(& self, url_string: String) -> Result<hyper::Url, String> {
			if url_string.contains("?") {
				let result_url = match hyper::Url::parse(&format!("{}{}&api_key={}", self.base_url, url_string, self.api_key)) {
					Ok(u) => u,
					Err(_) => return Err(format!("Error buildign URL"))
				};
				Ok(result_url)
			 } else {
				 let result_url = match hyper::Url::parse(&format!("{}{}?api_key={}", self.base_url, url_string, self.api_key)) {
	 				Ok(u) => u,
	 				Err(_) => return Err(format!("Error buildign URL"))
	 			};
	 			Ok(result_url)
			}
		}

		/// Returns the data for a given API method or an error string if something has failed
		fn get_json_data_for(& self, url_string: String) -> Result<Json, String> {
			let call_url = try!(self.get_url_for(url_string));

			let call_url_string = format!("{}", call_url);
			let mut response = match self.http_client.get(call_url).send() {
				Ok(r) => r,
				Err(_) => {
					return Err(format!("Error during HTTPS request with URL {}", call_url_string));
				}
	 		};

			if hyper::Ok != response.status {
				return Err(format!("No HTTPS Status 200 (OK) for url: {}", call_url_string));
			}

			let mut result = String::new();
			if response.read_to_string(& mut result).is_err() {
				return Err("Error reading response".to_string());
			}

			let json_data = match Json::from_str(&result) {
				Ok(d) => d,
				Err(_) => return Err("Error while readin JSON data".to_string())
			};

			Ok(json_data)
		}
	}
}

// #[test]
// fn initialization_test() {
// 	TheMovieDB::new(TESTAPIKEY).unwrap();
// }

#[test]
fn search_test() {
	let themoviedb = themoviedb_api::TheMovieDB::new(TESTAPIKEY).unwrap();
	let search_result = themoviedb.search("Tinker Bell", 25).unwrap();

//	assert!(303 == search_result.len());

	for item in search_result {
		match item {
			themoviedb_api::TheMovieDBItem::Movie(movie) => {
				if movie.title.is_some() {
					println!("Found movie: {}", movie.title.unwrap());
				}
				if movie.original_title.is_some() {
					println!("Found original movie: {}", movie.original_title.unwrap());
				}
			},
			_ => continue
		}
	}
}
<|endoftext|>


<commit_before>extern crate byteorder;
use byteorder::{LittleEndian, ReadBytesExt};

use std::collections::HashMap;
use std::io::{self, Read, Seek, SeekFrom, BufRead, BufReader};
use std::fs::File;
use std::cell::RefCell;

pub mod errors;
use errors::ReadError;

pub struct BigArchive<T: Read + Seek> {
    pub format: Format,
    pub size: u32,

    _buf_reader: RefCell<BufReader<T>>,
    _entries: HashMap<String, BigEntry>,
}

impl BigArchive<File> {
    pub fn new_from_path(path: &str) -> Result<Self, ReadError> {
        let f = try!(File::open(&path));
        let br = BufReader::new(f);
        Ok(try!(BigArchive::new(br)))
    }
}

impl<T: Read + Seek> BigArchive<T> {
    pub fn new(mut data: BufReader<T>) -> Result<Self, ReadError> {
        let format = read_format(&mut data).expect("Failed to read format");

        if let Format::Unknown(bytes) = format {
            return Err(ReadError::UnknownArchiveFormat(bytes));
        }

        let size = try!(data.read_u32::<LittleEndian>());
        let size = invert_endianness(size);

        let num_entries = try!(data.read_u32::<LittleEndian>());
        let num_entries = invert_endianness(num_entries);

        // Offset to the first entry, I think.
        let _ = data.read_u32::<LittleEndian>();

        let mut entries = HashMap::new();

        for i in 0..num_entries {
            let offset = try!(data.read_u32::<LittleEndian>());
            let offset = invert_endianness(offset);

            let size = try!(data.read_u32::<LittleEndian>());
            let size = invert_endianness(size);

            let mut buf = Vec::new();
            data.read_until(b'\0', &mut buf)
                .expect(&format!("Failed to read name for entry {}", i + 1));

            // Remove the trailing \0
            let name = String::from_utf8_lossy(&buf[..buf.len() - 1]);

            let entry = BigEntry {
                offset: offset,
                size: size,
                name: name.to_string(),
            };

            entries.insert(entry.name.clone(), entry);
        }

        Ok(BigArchive {
            format: format,
            size: size,
            _buf_reader: RefCell::new(data),
            _entries: entries,
        })
    }

    /// TODO: Don't return owned data, instead give the caller back a slice
    pub fn read_entry(&self, entry_name: &str) -> Option<Vec<u8>> {
        if let Some(entry) = self._entries.get(entry_name) {
            let br = &mut self._buf_reader.borrow_mut();

            if br.seek(SeekFrom::Start((*entry).offset as u64)).is_err() {
                return None;
            }

            let mut buf = vec![0; entry.size as usize];
            if br.read_exact(&mut buf).is_err() {
                return None;
            }

            Some(buf)
        } else {
            None
        }
    }

    pub fn contains(&self, entry_name: &str) -> bool {
        self._entries.contains_key(entry_name)
    }

    pub fn get_entry(&self, entry_name: &str) -> Option<&BigEntry> {
        self._entries.get(entry_name)
    }

    pub fn get_entry_mut(&mut self, entry_name: &str) -> Option<&mut BigEntry> {
        self._entries.get_mut(entry_name)
    }

    pub fn get_all_entry_names(&self) -> std::collections::hash_map::Keys<String, BigEntry> {
        self._entries.keys()
    }
}

#[derive(Debug)]
pub struct BigEntry {
    pub offset: u32,
    pub size: u32,
    pub name: String,
}

#[derive(Debug, PartialEq)]
pub enum Format {
    Unknown(Vec<u8>),
    Big4,
    BigF,
}

impl<'a> From<&'a mut BufRead> for Format {
    fn from(data: &'a mut BufRead) -> Self {
        let mut buf = [0; 4];
        let _ = data.read_exact(&mut buf);
        match &buf {
            b"BIG4" => Format::Big4,
            b"BIGF" => Format::BigF,
            _ => Format::Unknown(Vec::from(&buf[..])),
        }
    }
}

fn read_format(data: &mut BufRead) -> Result<Format, io::Error> {
    Ok(Format::from(data))
}

fn invert_endianness(v: u32) -> u32 {
    (v << 24) | (v << 8 & 0xff0000) | (v >> 8 & 0xff00) | (v >> 24)
}

#[cfg(test)]
mod tests_bytes {
    const TEST_BYTES: &'static [u8] = include_bytes!("../test.big");

    use std::io::{BufReader, Cursor};
    use super::{Format, BigArchive};

    #[test]
    fn is_big4() {
        let c = Cursor::new(TEST_BYTES);
        let br = BufReader::new(c);
        let archive = BigArchive::new(br).unwrap();
        assert_eq!(archive.format, Format::Big4);
    }

    #[test]
    fn has_two_entries() {
        let c = Cursor::new(TEST_BYTES);
        let br = BufReader::new(c);
        let archive = BigArchive::new(br).unwrap();
        assert_eq!(archive.get_all_entry_names().len(), 2);
    }

    #[test]
    fn contains_art_slash_image_dot_txt() {
        let c = Cursor::new(TEST_BYTES);
        let br = BufReader::new(c);
        let archive = BigArchive::new(br).unwrap();
        assert!(archive.contains("art/image.txt"));
    }

    #[test]
    fn contains_data_slash_test_dot_ini() {
        let c = Cursor::new(TEST_BYTES);
        let br = BufReader::new(c);
        let archive = BigArchive::new(br).unwrap();
        assert!(archive.contains("data/test.ini"));
    }
}

#[cfg(test)]
mod tests_file {
    use super::{Format, BigArchive};

    const ARCHIVE_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/test.big");

    #[test]
    fn is_big4() {
        let archive = BigArchive::new_from_path(&ARCHIVE_PATH).unwrap();
        assert_eq!(archive.format, Format::Big4);
    }

    #[test]
    fn has_two_entries() {
        let archive = BigArchive::new_from_path(&ARCHIVE_PATH).unwrap();
        assert_eq!(archive.get_all_entry_names().len(), 2);
    }

    #[test]
    fn contains_art_slash_image_dot_txt() {
        let archive = BigArchive::new_from_path(&ARCHIVE_PATH).unwrap();
        assert!(archive.contains("art/image.txt"));
    }

    #[test]
    fn contains_data_slash_test_dot_ini() {
        let archive = BigArchive::new_from_path(&ARCHIVE_PATH).unwrap();
        assert!(archive.contains("data/test.ini"));
    }
}
<commit_msg>Add tests to verify entry contents via BigArchive::read_entry<commit_after>extern crate byteorder;
use byteorder::{LittleEndian, ReadBytesExt};

use std::collections::HashMap;
use std::io::{self, Read, Seek, SeekFrom, BufRead, BufReader};
use std::fs::File;
use std::cell::RefCell;

pub mod errors;
use errors::ReadError;

pub struct BigArchive<T: Read + Seek> {
    pub format: Format,
    pub size: u32,

    _buf_reader: RefCell<BufReader<T>>,
    _entries: HashMap<String, BigEntry>,
}

impl BigArchive<File> {
    pub fn new_from_path(path: &str) -> Result<Self, ReadError> {
        let f = try!(File::open(&path));
        let br = BufReader::new(f);
        Ok(try!(BigArchive::new(br)))
    }
}

impl<T: Read + Seek> BigArchive<T> {
    pub fn new(mut data: BufReader<T>) -> Result<Self, ReadError> {
        let format = read_format(&mut data).expect("Failed to read format");

        if let Format::Unknown(bytes) = format {
            return Err(ReadError::UnknownArchiveFormat(bytes));
        }

        let size = try!(data.read_u32::<LittleEndian>());
        let size = invert_endianness(size);

        let num_entries = try!(data.read_u32::<LittleEndian>());
        let num_entries = invert_endianness(num_entries);

        // Offset to the first entry, I think.
        let _ = data.read_u32::<LittleEndian>();

        let mut entries = HashMap::new();

        for i in 0..num_entries {
            let offset = try!(data.read_u32::<LittleEndian>());
            let offset = invert_endianness(offset);

            let size = try!(data.read_u32::<LittleEndian>());
            let size = invert_endianness(size);

            let mut buf = Vec::new();
            data.read_until(b'\0', &mut buf)
                .expect(&format!("Failed to read name for entry {}", i + 1));

            // Remove the trailing \0
            let name = String::from_utf8_lossy(&buf[..buf.len() - 1]);

            let entry = BigEntry {
                offset: offset,
                size: size,
                name: name.to_string(),
            };

            entries.insert(entry.name.clone(), entry);
        }

        Ok(BigArchive {
            format: format,
            size: size,
            _buf_reader: RefCell::new(data),
            _entries: entries,
        })
    }

    /// TODO: Don't return owned data, instead give the caller back a slice
    pub fn read_entry(&self, entry_name: &str) -> Option<Vec<u8>> {
        if let Some(entry) = self._entries.get(entry_name) {
            let br = &mut self._buf_reader.borrow_mut();

            if br.seek(SeekFrom::Start((*entry).offset as u64)).is_err() {
                return None;
            }

            let mut buf = vec![0; entry.size as usize];
            if br.read_exact(&mut buf).is_err() {
                return None;
            }

            Some(buf)
        } else {
            None
        }
    }

    pub fn contains(&self, entry_name: &str) -> bool {
        self._entries.contains_key(entry_name)
    }

    pub fn get_entry(&self, entry_name: &str) -> Option<&BigEntry> {
        self._entries.get(entry_name)
    }

    pub fn get_entry_mut(&mut self, entry_name: &str) -> Option<&mut BigEntry> {
        self._entries.get_mut(entry_name)
    }

    pub fn get_all_entry_names(&self) -> std::collections::hash_map::Keys<String, BigEntry> {
        self._entries.keys()
    }
}

#[derive(Debug)]
pub struct BigEntry {
    pub offset: u32,
    pub size: u32,
    pub name: String,
}

#[derive(Debug, PartialEq)]
pub enum Format {
    Unknown(Vec<u8>),
    Big4,
    BigF,
}

impl<'a> From<&'a mut BufRead> for Format {
    fn from(data: &'a mut BufRead) -> Self {
        let mut buf = [0; 4];
        let _ = data.read_exact(&mut buf);
        match &buf {
            b"BIG4" => Format::Big4,
            b"BIGF" => Format::BigF,
            _ => Format::Unknown(Vec::from(&buf[..])),
        }
    }
}

fn read_format(data: &mut BufRead) -> Result<Format, io::Error> {
    Ok(Format::from(data))
}

fn invert_endianness(v: u32) -> u32 {
    (v << 24) | (v << 8 & 0xff0000) | (v >> 8 & 0xff00) | (v >> 24)
}

#[cfg(test)]
mod tests_bytes {
    const TEST_BYTES: &'static [u8] = include_bytes!("../test.big");

    use std::io::{BufReader, Cursor};
    use super::{Format, BigArchive};

    #[test]
    fn is_big4() {
        let c = Cursor::new(TEST_BYTES);
        let br = BufReader::new(c);
        let archive = BigArchive::new(br).unwrap();
        assert_eq!(archive.format, Format::Big4);
    }

    #[test]
    fn has_two_entries() {
        let c = Cursor::new(TEST_BYTES);
        let br = BufReader::new(c);
        let archive = BigArchive::new(br).unwrap();
        assert_eq!(archive.get_all_entry_names().len(), 2);
    }

    #[test]
    fn contains_art_slash_image_dot_txt() {
        let c = Cursor::new(TEST_BYTES);
        let br = BufReader::new(c);
        let archive = BigArchive::new(br).unwrap();
        assert!(archive.contains("art/image.txt"));
    }

    #[test]
    fn contains_data_slash_test_dot_ini() {
        let c = Cursor::new(TEST_BYTES);
        let br = BufReader::new(c);
        let archive = BigArchive::new(br).unwrap();
        assert!(archive.contains("data/test.ini"));
    }

    #[test]
    fn read_entry_art_slash_image_dot_txt() {
        let c = Cursor::new(TEST_BYTES);
        let br = BufReader::new(c);
        let archive = BigArchive::new(br).unwrap();
        let data = archive.read_entry("art/image.txt").unwrap();
        assert_eq!(data, vec![98, 108, 97, 98, 108, 98, 97]);
    }

    #[test]
    fn read_entry_data_slash_test_dot_ini() {
        let c = Cursor::new(TEST_BYTES);
        let br = BufReader::new(c);
        let archive = BigArchive::new(br).unwrap();
        let data = archive.read_entry("data/test.ini").unwrap();
        assert_eq!(data,
                   vec![84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 115, 105, 109, 112, 108,
                        101, 32, 116, 101, 115, 116, 32, 102, 105, 108, 101]);
    }
}

#[cfg(test)]
mod tests_file {
    use super::{Format, BigArchive};

    const ARCHIVE_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/test.big");

    #[test]
    fn is_big4() {
        let archive = BigArchive::new_from_path(&ARCHIVE_PATH).unwrap();
        assert_eq!(archive.format, Format::Big4);
    }

    #[test]
    fn has_two_entries() {
        let archive = BigArchive::new_from_path(&ARCHIVE_PATH).unwrap();
        assert_eq!(archive.get_all_entry_names().len(), 2);
    }

    #[test]
    fn contains_art_slash_image_dot_txt() {
        let archive = BigArchive::new_from_path(&ARCHIVE_PATH).unwrap();
        assert!(archive.contains("art/image.txt"));
    }

    #[test]
    fn contains_data_slash_test_dot_ini() {
        let archive = BigArchive::new_from_path(&ARCHIVE_PATH).unwrap();
        assert!(archive.contains("data/test.ini"));
    }

    #[test]
    fn read_entry_art_slash_image_dot_txt() {
        let archive = BigArchive::new_from_path(&ARCHIVE_PATH).unwrap();
        let data = archive.read_entry("art/image.txt").unwrap();
        assert_eq!(data, vec![98, 108, 97, 98, 108, 98, 97]);
    }

    #[test]
    fn read_entry_data_slash_test_dot_ini() {
        let archive = BigArchive::new_from_path(&ARCHIVE_PATH).unwrap();
        let data = archive.read_entry("data/test.ini").unwrap();
        assert_eq!(data,
                   vec![84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 115, 105, 109, 112, 108,
                        101, 32, 116, 101, 115, 116, 32, 102, 105, 108, 101]);
    }
}
<|endoftext|>


<commit_before>extern crate libc;

mod bindgen;

pub mod ffi_type;
pub mod low;

pub use bindgen::libffi as c;
pub use ffi_type::*;
use std::mem;

#[derive(Debug)]
pub struct Cif {
    cif:    c::ffi_cif,
    args:   FfiTypeArray,
    result: FfiType,
}

#[derive(Debug)]
pub struct Arg(*mut ::std::os::raw::c_void);

pub fn arg<T>(r: &T) -> Arg {
    Arg(unsafe { mem::transmute(r as *const T) })
}

impl Cif {
    pub fn new(args: FfiTypeArray, result: FfiType) -> Self {
        let mut cif: c::ffi_cif = Default::default();

        unsafe {
            low::prep_cif(&mut cif,
                          c::FFI_DEFAULT_ABI,
                          args.len(),
                          result.as_raw_ptr(),
                          args.as_raw_ptr())
        }.expect("low::prep_cif");

        // Note that cif retains references to args and result,
        // which is why we hold onto them here.
        Cif {
            cif: cif,
            args: args,
            result: result,
        }
    }

    pub unsafe fn call<R>(&self, f: usize, values: &[Arg]) -> R {
        use std::mem;

        assert!(self.cif.nargs as usize == values.len());

        low::call::<R>(mem::transmute(&self.cif),
                       mem::transmute(f),
                       mem::transmute(values.as_ptr()))
    }
}

pub struct Closure {
    cif:   Cif,
    alloc: *mut ::low::ffi_closure,
    code:  extern "C" fn(),
}

impl Drop for Closure {
    fn drop(&mut self) {
        unsafe {
            low::closure_free(self.alloc);
        }
    }
}

impl Closure {
    pub fn new<U>(mut cif: Cif,
                  fun: low::Callback<U>,
                  userdata: &mut U) -> Self
    {
        let (alloc, code) = low::closure_alloc();

        unsafe {
            low::prep_closure_loc(alloc, &mut cif.cif, fun, userdata, code)
        }.expect("Closure::new");

        Closure {
            cif:   cif,
            alloc: alloc,
            code:  code,
        }
    }
}

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

    #[test]
    fn ffi_call() {
        use ffi_type::*;

        let args = FfiTypeArray::new(vec![FfiType::sint64(),
                                          FfiType::sint64()]);
        let cif  = Cif::new(args, FfiType::sint64());
        let f    = |m: i64, n: i64| -> i64 {
            unsafe { cif.call(add_it as usize, &[arg(&m), arg(&n)]) }
        };

        assert_eq!(12, f(5, 7));
        assert_eq!(13, f(6, 7));
        assert_eq!(15, f(8, 7));
    }

    extern "C" fn add_it(n: i64, m: i64) -> i64 {
        return n + m;
    }
}
<commit_msg>Clone for Cif<commit_after>extern crate libc;

mod bindgen;

pub mod ffi_type;
pub mod low;

pub use bindgen::libffi as c;
pub use ffi_type::*;
use std::mem;

#[derive(Clone, Debug)]
pub struct Cif {
    cif:    c::ffi_cif,
    args:   FfiTypeArray,
    result: FfiType,
}

#[derive(Debug)]
pub struct Arg(*mut ::std::os::raw::c_void);

pub fn arg<T>(r: &T) -> Arg {
    Arg(unsafe { mem::transmute(r as *const T) })
}

impl Cif {
    pub fn new(args: FfiTypeArray, result: FfiType) -> Self {
        let mut cif: c::ffi_cif = Default::default();

        unsafe {
            low::prep_cif(&mut cif,
                          c::FFI_DEFAULT_ABI,
                          args.len(),
                          result.as_raw_ptr(),
                          args.as_raw_ptr())
        }.expect("low::prep_cif");

        // Note that cif retains references to args and result,
        // which is why we hold onto them here.
        Cif {
            cif: cif,
            args: args,
            result: result,
        }
    }

    pub unsafe fn call<R>(&self, f: usize, values: &[Arg]) -> R {
        use std::mem;

        assert!(self.cif.nargs as usize == values.len());

        low::call::<R>(mem::transmute(&self.cif),
                       mem::transmute(f),
                       mem::transmute(values.as_ptr()))
    }
}

pub struct Closure {
    cif:   Cif,
    alloc: *mut ::low::ffi_closure,
    code:  extern "C" fn(),
}

impl Drop for Closure {
    fn drop(&mut self) {
        unsafe {
            low::closure_free(self.alloc);
        }
    }
}

impl Closure {
    pub fn new<U>(mut cif: Cif,
                  fun: low::Callback<U>,
                  userdata: &mut U) -> Self
    {
        let (alloc, code) = low::closure_alloc();

        unsafe {
            low::prep_closure_loc(alloc, &mut cif.cif, fun, userdata, code)
        }.expect("Closure::new");

        Closure {
            cif:   cif,
            alloc: alloc,
            code:  code,
        }
    }
}

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

    #[test]
    fn ffi_call() {
        use ffi_type::*;

        let args = FfiTypeArray::new(vec![FfiType::sint64(),
                                          FfiType::sint64()]);
        let cif  = Cif::new(args, FfiType::sint64());
        let f    = |m: i64, n: i64| -> i64 {
            unsafe { cif.call(add_it as usize, &[arg(&m), arg(&n)]) }
        };

        assert_eq!(12, f(5, 7));
        assert_eq!(13, f(6, 7));
        assert_eq!(15, f(8, 7));
    }

    extern "C" fn add_it(n: i64, m: i64) -> i64 {
        return n + m;
    }
}
<|endoftext|>