<commit_before>use std::io::{BufRead, BufReader, Write};
use std::net::TcpStream;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError};
use std::thread;

use chatmap::*;
use command::{Command, parse_command};
use message::Message;

extern crate time;

pub fn create_client(stream: TcpStream,
                     sender_to_router: Sender<Message>,
                     chat_map: &Arc<Mutex<ChatMap>>) {

    println!("New client");
    let mut stream = stream;

    stream.write(&"Welcome to Smazy\n".to_string().into_bytes()).unwrap();
    stream.write(&"Please enter a username:\n".to_string().into_bytes()).unwrap();
    stream.flush().unwrap();

    let mut reader = BufReader::new(stream.try_clone().unwrap());
    let mut username = "".to_string();
    let (sender_to_client, receiver_from_router) = channel();

    loop {
        match reader.read_line(&mut username) {
            Ok(_) => {
                username = username.trim().to_string();

                {
                    let mut guard = chat_map.lock().unwrap();
                    if is_valid_username(&*guard, username.to_string()) {

                        let client_info = ClientInfo{
                            partner: None,
                            sender_to_client: sender_to_client
                        };
                        guard.insert(username.to_string(), client_info);

                        println!("New user: {}", username.to_string());
                        break;
                    }
                    else {
                        stream.write(&"Invalid username. Please try again.\n".to_string().into_bytes()).unwrap();
                        username = "".to_string();
                    }
                }
            },
            Err(e) => println!("{}", e)
        }
    }

    let partner = choose_chat_partner(stream.try_clone().unwrap(),
        username.to_string(), chat_map);

    chat(stream, username, partner, sender_to_router, receiver_from_router, chat_map);
}

fn display_available(stream: TcpStream, chat_map: &Arc<Mutex<ChatMap>>, username: String) {
    let mut stream = stream;

    let show_available_msg = "Here are the users available to chat:\n".to_string();
    stream.write(&show_available_msg.into_bytes()).unwrap();

    {
        let guard = chat_map.lock().unwrap();
        for (name, client_info) in guard.iter() {
            if &name[..] != &username[..] && client_info.partner == None {
                stream.write(&name.to_string().into_bytes()).unwrap();
                stream.write(&"\n".to_string().into_bytes()).unwrap();
            }
        }
    }
}

fn choose_chat_partner(stream: TcpStream,
                       username: String,
                       chat_map: &Arc<Mutex<ChatMap>>) -> String {

    display_available(stream.try_clone().unwrap(), &chat_map, username.to_string());

    let mut stream = stream;

    let select_msg = "Select who you want to chat with:\n".to_string();
    stream.write(&select_msg.into_bytes()).unwrap();

    let mut reader = BufReader::new(stream.try_clone().unwrap());
    let mut partner = "".to_string();

    match reader.read_line(&mut partner) {
        Ok(_) => {
            partner = partner.trim().to_string();

            if !try_select_partner(chat_map, username.to_string(), partner.to_string()) {
                return choose_chat_partner(stream, username, chat_map);
            }
        }
        Err(e) => println!("{}", e)
    }

    partner
}

fn try_select_partner(chat_map: &Arc<Mutex<ChatMap>>,
                      username: String,
                      partner: String) -> bool {

    let success: bool;
    {
        let mut guard = chat_map.lock().unwrap();

        match guard.get_mut(&partner) {
            Some(clientinfo) => {
                if &partner[..] != &username[..]
                    && (clientinfo.partner == None
                        || clientinfo.partner == Some(username.to_string())) {
                    clientinfo.partner = Some(username.to_string());
                    success = true;
                } else {
                    success = false;
                }
            },
            None => success = false
        }
    }

    if success {
        let mut guard = chat_map.lock().unwrap();
        println!("Assigning partner {} to {}", partner, username);
        guard.get_mut(&username).unwrap().partner = Some(partner.to_string());
    }
    success
}

fn chat(stream: TcpStream,
        username: String,
        partner: String,
        sender_to_router: Sender<Message>,
        receiver_from_router: Receiver<Message>,
        chat_map: &Arc<Mutex<ChatMap>>) {

    let mut partner = partner.to_string();

    // Send messages
    {
        let username = username.clone();
        let chat_map = chat_map.clone();
        let stream = stream.try_clone().unwrap();
        let reader = BufReader::new(stream.try_clone().unwrap());
        thread::spawn(move|| {
            let mut lines = reader.lines();
            while let Some(Ok(line)) = lines.next() {
                println!("{}",line);

                let msg = Message::new(time::now().asctime().to_string(),
                                       username.to_string(),
                                       partner.to_string(),
                                       line.to_string());

                if &line[0..1] == "/" {
                    match parse_command(msg.content().to_string()) {
                        Command::Quit => {
                            println!("Quit command");
                            quit_conversation(&chat_map, username.to_string(), partner.to_string());
                            partner = choose_chat_partner(stream.try_clone().unwrap(), username.to_string(), &chat_map);
                        },
                        Command::DisplayAvailable => {
                            display_available(stream.try_clone().unwrap(), &chat_map, username.to_string());
                        },
                        Command::Logoff => println!("Logoff command"),
                        Command::Unrecognized => println!("Unrecognized command")
                    }
                } else {
                    sender_to_router.send(msg).unwrap();
                }
            }
        });
    }

    // Receive Messages
    {
        let username = username.clone();
        thread::spawn(move|| {
            loop {
                let stream = stream.try_clone().unwrap();
                match receiver_from_router.try_recv() {
                    Ok(msg) => {
                        println!("User {} received message: {}",
                            &username, msg.content());

                        receive_message(stream, msg);
                    },

                    Err(TryRecvError::Empty) => continue,

                    Err(TryRecvError::Disconnected) =>
                        panic!("User {} disconnected from router", &username)
                }
            }
        });
    }
}

fn receive_message(stream: TcpStream, msg: Message) {
    let mut stream = stream;
    let output = msg.from().to_string() + ": " + &msg.content()[..] + "\n";
    stream.write(&output.into_bytes()).unwrap();
}

fn quit_conversation(chat_map: &Arc<Mutex<ChatMap>>, username: String, partner: String) {
    let mut guard = chat_map.lock().unwrap();
    guard.get_mut(&username).unwrap().partner = None;
    guard.get_mut(&partner).unwrap().partner = None;
}

#[cfg(test)]
mod client_tests {

    use std::sync::{Arc, Mutex};
    use std::sync::mpsc::channel;
    use chatmap::{ChatMap, ClientInfo};

    use super::try_select_partner;

    #[test]
    fn try_select_partner_test_success_1() {
        let cm = Arc::new(Mutex::new(fixture()));

        // Success: a and b become partners
        assert!(try_select_partner(&cm, "a".to_string(), "b".to_string()));
    }

    #[test]
    fn try_select_partner_test_success_2() {
        let cm = Arc::new(Mutex::new(fixture()));

        // Success: a and b become partners
        assert!(try_select_partner(&cm, "a".to_string(), "b".to_string()));

        // Success: c and d become partners
        assert!(try_select_partner(&cm, "c".to_string(), "d".to_string()));
    }

    #[test]
    fn try_select_partner_test_succes_3() {
        let cm = Arc::new(Mutex::new(fixture()));

        // Success: a and b become partners
        assert!(try_select_partner(&cm, "a".to_string(), "b".to_string()));

        // Success: b can choose a when they are already partners
        //   (This allows someone to confirm a chat request)
        assert!(try_select_partner(&cm, "b".to_string(), "a".to_string()));
    }

    #[test]
    fn try_select_partner_test_fail_1() {
        let cm = Arc::new(Mutex::new(fixture()));

        // Success: a and b become partners
        assert!(try_select_partner(&cm, "a".to_string(), "b".to_string()));

        // Fail: b already has a partner
        assert!(!try_select_partner(&cm, "c".to_string(), "b".to_string()));
    }

    #[test]
    fn try_select_partner_test_fail_2() {
        let cm = Arc::new(Mutex::new(fixture()));

        // Success: a and b become partners
        assert!(try_select_partner(&cm, "a".to_string(), "b".to_string()));

        // Fail: Can't choose self as partner
        assert!(!try_select_partner(&cm, "c".to_string(), "c".to_string()));
    }

    #[test]
    fn try_select_partner_test_fail_3() {
        let cm = Arc::new(Mutex::new(fixture()));

        // Success: a and b become partners
        assert!(try_select_partner(&cm, "a".to_string(), "b".to_string()));

        // Success: c and d become partners
        assert!(try_select_partner(&cm, "c".to_string(), "d".to_string()));

        // Fail: c already has a partner
        assert!(!try_select_partner(&cm, "e".to_string(), "c".to_string()));

        // Fail: d already has a partner
        assert!(!try_select_partner(&cm, "e".to_string(), "d".to_string()));
    }

    use super::quit_conversation;

    #[test]
    fn quit_conversation_test_1() {
        let cm = Arc::new(Mutex::new(fixture()));
        if try_select_partner(&cm, "a".to_string(), "b".to_string()) {
            quit_conversation(&cm, "a".to_string(), "b".to_string());
            let guard = cm.lock().unwrap();
            assert_eq!(None, guard.get(&"a".to_string()).unwrap().partner);
            assert_eq!(None, guard.get(&"b".to_string()).unwrap().partner);
        } else {
            assert!(false); // Don't pass if try_select_partner fails
        }
    }

    #[test]
    fn quit_conversation_test_2() {
        let cm = Arc::new(Mutex::new(fixture()));
        let ab = try_select_partner(&cm, "a".to_string(), "b".to_string());
        let cd = try_select_partner(&cm, "c".to_string(), "d".to_string());
        if ab && cd {
            quit_conversation(&cm, "a".to_string(), "b".to_string());
            let guard = cm.lock().unwrap();
            assert_eq!(None, guard.get(&"a".to_string()).unwrap().partner);
            assert_eq!(None, guard.get(&"b".to_string()).unwrap().partner);
            assert_eq!(Some("d".to_string()), guard.get(&"c".to_string()).unwrap().partner);
            assert_eq!(Some("c".to_string()), guard.get(&"d".to_string()).unwrap().partner);
        } else {
            assert!(false); // Don't pass if try_select_partner fails
        }
    }

    fn fixture() -> ChatMap {
        let mut cm = ChatMap::new();
        let (sender_to_a, _) = channel();
        cm.insert("a".to_string(), ClientInfo{
            partner: None,
            sender_to_client: sender_to_a
        });
        let (sender_to_b, _) = channel();
        cm.insert("b".to_string(), ClientInfo{
            partner: None,
            sender_to_client: sender_to_b
        });
        let (sender_to_c, _) = channel();
        cm.insert("c".to_string(), ClientInfo{
            partner: None,
            sender_to_client: sender_to_c
        });
        let (sender_to_d, _) = channel();
        cm.insert("d".to_string(), ClientInfo{
            partner: None,
            sender_to_client: sender_to_d
        });
        let (sender_to_e, _) = channel();
        cm.insert("e".to_string(), ClientInfo{
            partner: None,
            sender_to_client: sender_to_e
        });
        cm
    }
}<commit_msg>Added intentionally failing test for quit_conversation bc we need to sort that out<commit_after>use std::io::{BufRead, BufReader, Write};
use std::net::TcpStream;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError};
use std::thread;

use chatmap::*;
use command::{Command, parse_command};
use message::Message;

extern crate time;

pub fn create_client(stream: TcpStream,
                     sender_to_router: Sender<Message>,
                     chat_map: &Arc<Mutex<ChatMap>>) {

    println!("New client");
    let mut stream = stream;

    stream.write(&"Welcome to Smazy\n".to_string().into_bytes()).unwrap();
    stream.write(&"Please enter a username:\n".to_string().into_bytes()).unwrap();
    stream.flush().unwrap();

    let mut reader = BufReader::new(stream.try_clone().unwrap());
    let mut username = "".to_string();
    let (sender_to_client, receiver_from_router) = channel();

    loop {
        match reader.read_line(&mut username) {
            Ok(_) => {
                username = username.trim().to_string();

                {
                    let mut guard = chat_map.lock().unwrap();
                    if is_valid_username(&*guard, username.to_string()) {

                        let client_info = ClientInfo{
                            partner: None,
                            sender_to_client: sender_to_client
                        };
                        guard.insert(username.to_string(), client_info);

                        println!("New user: {}", username.to_string());
                        break;
                    }
                    else {
                        stream.write(&"Invalid username. Please try again.\n".to_string().into_bytes()).unwrap();
                        username = "".to_string();
                    }
                }
            },
            Err(e) => println!("{}", e)
        }
    }

    let partner = choose_chat_partner(stream.try_clone().unwrap(),
        username.to_string(), chat_map);

    chat(stream, username, partner, sender_to_router, receiver_from_router, chat_map);
}

fn display_available(stream: TcpStream, chat_map: &Arc<Mutex<ChatMap>>, username: String) {
    let mut stream = stream;

    let show_available_msg = "Here are the users available to chat:\n".to_string();
    stream.write(&show_available_msg.into_bytes()).unwrap();

    {
        let guard = chat_map.lock().unwrap();
        for (name, client_info) in guard.iter() {
            if &name[..] != &username[..] && client_info.partner == None {
                stream.write(&name.to_string().into_bytes()).unwrap();
                stream.write(&"\n".to_string().into_bytes()).unwrap();
            }
        }
    }
}

fn choose_chat_partner(stream: TcpStream,
                       username: String,
                       chat_map: &Arc<Mutex<ChatMap>>) -> String {

    display_available(stream.try_clone().unwrap(), &chat_map, username.to_string());

    let mut stream = stream;

    let select_msg = "Select who you want to chat with:\n".to_string();
    stream.write(&select_msg.into_bytes()).unwrap();

    let mut reader = BufReader::new(stream.try_clone().unwrap());
    let mut partner = "".to_string();

    match reader.read_line(&mut partner) {
        Ok(_) => {
            partner = partner.trim().to_string();

            if !try_select_partner(chat_map, username.to_string(), partner.to_string()) {
                return choose_chat_partner(stream, username, chat_map);
            }
        }
        Err(e) => println!("{}", e)
    }

    partner
}

fn try_select_partner(chat_map: &Arc<Mutex<ChatMap>>,
                      username: String,
                      partner: String) -> bool {

    let success: bool;
    {
        let mut guard = chat_map.lock().unwrap();

        match guard.get_mut(&partner) {
            Some(clientinfo) => {
                if &partner[..] != &username[..]
                    && (clientinfo.partner == None
                        || clientinfo.partner == Some(username.to_string())) {
                    clientinfo.partner = Some(username.to_string());
                    success = true;
                } else {
                    success = false;
                }
            },
            None => success = false
        }
    }

    if success {
        let mut guard = chat_map.lock().unwrap();
        println!("Assigning partner {} to {}", partner, username);
        guard.get_mut(&username).unwrap().partner = Some(partner.to_string());
    }
    success
}

fn chat(stream: TcpStream,
        username: String,
        partner: String,
        sender_to_router: Sender<Message>,
        receiver_from_router: Receiver<Message>,
        chat_map: &Arc<Mutex<ChatMap>>) {

    let mut partner = partner.to_string();

    // Send messages
    {
        let username = username.clone();
        let chat_map = chat_map.clone();
        let stream = stream.try_clone().unwrap();
        let reader = BufReader::new(stream.try_clone().unwrap());
        thread::spawn(move|| {
            let mut lines = reader.lines();
            while let Some(Ok(line)) = lines.next() {
                println!("{}",line);

                let msg = Message::new(time::now().asctime().to_string(),
                                       username.to_string(),
                                       partner.to_string(),
                                       line.to_string());

                if &line[0..1] == "/" {
                    match parse_command(msg.content().to_string()) {
                        Command::Quit => {
                            println!("Quit command");
                            quit_conversation(&chat_map, username.to_string(), partner.to_string());
                            partner = choose_chat_partner(stream.try_clone().unwrap(), username.to_string(), &chat_map);
                        },
                        Command::DisplayAvailable => {
                            display_available(stream.try_clone().unwrap(), &chat_map, username.to_string());
                        },
                        Command::Logoff => println!("Logoff command"),
                        Command::Unrecognized => println!("Unrecognized command")
                    }
                } else {
                    sender_to_router.send(msg).unwrap();
                }
            }
        });
    }

    // Receive Messages
    {
        let username = username.clone();
        thread::spawn(move|| {
            loop {
                let stream = stream.try_clone().unwrap();
                match receiver_from_router.try_recv() {
                    Ok(msg) => {
                        println!("User {} received message: {}",
                            &username, msg.content());

                        receive_message(stream, msg);
                    },

                    Err(TryRecvError::Empty) => continue,

                    Err(TryRecvError::Disconnected) =>
                        panic!("User {} disconnected from router", &username)
                }
            }
        });
    }
}

fn receive_message(stream: TcpStream, msg: Message) {
    let mut stream = stream;
    let output = msg.from().to_string() + ": " + &msg.content()[..] + "\n";
    stream.write(&output.into_bytes()).unwrap();
}

fn quit_conversation(chat_map: &Arc<Mutex<ChatMap>>, username: String, partner: String) {
    let mut guard = chat_map.lock().unwrap();
    guard.get_mut(&username).unwrap().partner = None;
    guard.get_mut(&partner).unwrap().partner = None;
}

#[cfg(test)]
mod client_tests {

    use std::sync::{Arc, Mutex};
    use std::sync::mpsc::channel;
    use chatmap::{ChatMap, ClientInfo};

    use super::try_select_partner;

    #[test]
    fn try_select_partner_test_success_1() {
        let cm = Arc::new(Mutex::new(fixture()));

        // Success: a and b become partners
        assert!(try_select_partner(&cm, "a".to_string(), "b".to_string()));
    }

    #[test]
    fn try_select_partner_test_success_2() {
        let cm = Arc::new(Mutex::new(fixture()));

        // Success: a and b become partners
        assert!(try_select_partner(&cm, "a".to_string(), "b".to_string()));

        // Success: c and d become partners
        assert!(try_select_partner(&cm, "c".to_string(), "d".to_string()));
    }

    #[test]
    fn try_select_partner_test_succes_3() {
        let cm = Arc::new(Mutex::new(fixture()));

        // Success: a and b become partners
        assert!(try_select_partner(&cm, "a".to_string(), "b".to_string()));

        // Success: b can choose a when they are already partners
        //   (This allows someone to confirm a chat request)
        assert!(try_select_partner(&cm, "b".to_string(), "a".to_string()));
    }

    #[test]
    fn try_select_partner_test_fail_1() {
        let cm = Arc::new(Mutex::new(fixture()));

        // Success: a and b become partners
        assert!(try_select_partner(&cm, "a".to_string(), "b".to_string()));

        // Fail: b already has a partner
        assert!(!try_select_partner(&cm, "c".to_string(), "b".to_string()));
    }

    #[test]
    fn try_select_partner_test_fail_2() {
        let cm = Arc::new(Mutex::new(fixture()));

        // Success: a and b become partners
        assert!(try_select_partner(&cm, "a".to_string(), "b".to_string()));

        // Fail: Can't choose self as partner
        assert!(!try_select_partner(&cm, "c".to_string(), "c".to_string()));
    }

    #[test]
    fn try_select_partner_test_fail_3() {
        let cm = Arc::new(Mutex::new(fixture()));

        // Success: a and b become partners
        assert!(try_select_partner(&cm, "a".to_string(), "b".to_string()));

        // Success: c and d become partners
        assert!(try_select_partner(&cm, "c".to_string(), "d".to_string()));

        // Fail: c already has a partner
        assert!(!try_select_partner(&cm, "e".to_string(), "c".to_string()));

        // Fail: d already has a partner
        assert!(!try_select_partner(&cm, "e".to_string(), "d".to_string()));
    }

    use super::quit_conversation;

    #[test]
    fn quit_conversation_test_1() {
        let cmap = Arc::new(Mutex::new(fixture()));
        let ab = try_select_partner(&cmap, "a".to_string(), "b".to_string());
        assert!(ab);
        quit_conversation(&cmap, "a".to_string(), "b".to_string());
        let guard = cmap.lock().unwrap();
        assert_eq!(None, guard.get(&"a".to_string()).unwrap().partner);
        assert_eq!(None, guard.get(&"b".to_string()).unwrap().partner);
    }

    #[test]
    fn quit_conversation_test_2() {
        let cmap = Arc::new(Mutex::new(fixture()));
        let ab = try_select_partner(&cmap, "a".to_string(), "b".to_string());
        let cd = try_select_partner(&cmap, "c".to_string(), "d".to_string());
        assert!(ab);
        assert!(cd);
        quit_conversation(&cmap, "a".to_string(), "b".to_string());
        let guard = cmap.lock().unwrap();
        assert_eq!(None, guard.get(&"a".to_string()).unwrap().partner);
        assert_eq!(None, guard.get(&"b".to_string()).unwrap().partner);
        assert_eq!(Some("d".to_string()), guard.get(&"c".to_string()).unwrap().partner);
        assert_eq!(Some("c".to_string()), guard.get(&"d".to_string()).unwrap().partner);
    }

    #[test]
    fn quit_conversation_test_3() {
        let cmap = Arc::new(Mutex::new(fixture()));
        let ab = try_select_partner(&cmap, "a".to_string(), "b".to_string());
        assert!(ab);
        quit_conversation(&cmap, "a".to_string(), "b".to_string());
        let ac = try_select_partner(&cmap, "a".to_string(), "c".to_string());
        assert!(ac);
        quit_conversation(&cmap, "b".to_string(), "a".to_string());
        let guard = cmap.lock().unwrap();
        assert_eq!(Some("c".to_string()), guard.get(&"a".to_string()).unwrap().partner);
        assert_eq!(None, guard.get(&"b".to_string()).unwrap().partner);
        assert_eq!(Some("a".to_string()), guard.get(&"c".to_string()).unwrap().partner);
    }

    fn fixture() -> ChatMap {
        let mut cm = ChatMap::new();
        let (sender_to_a, _) = channel();
        cm.insert("a".to_string(), ClientInfo{
            partner: None,
            sender_to_client: sender_to_a
        });
        let (sender_to_b, _) = channel();
        cm.insert("b".to_string(), ClientInfo{
            partner: None,
            sender_to_client: sender_to_b
        });
        let (sender_to_c, _) = channel();
        cm.insert("c".to_string(), ClientInfo{
            partner: None,
            sender_to_client: sender_to_c
        });
        let (sender_to_d, _) = channel();
        cm.insert("d".to_string(), ClientInfo{
            partner: None,
            sender_to_client: sender_to_d
        });
        let (sender_to_e, _) = channel();
        cm.insert("e".to_string(), ClientInfo{
            partner: None,
            sender_to_client: sender_to_e
        });
        cm
    }
}<|endoftext|>


<commit_before>#[derive(PartialEq, Debug)]
pub enum ExecutionMode {
    Class { class: String, args: Vec<String> },
    Exit { code: i32 },
    Help { code: i32 },
    JarFile { file: String, args: Vec<String> },
}

#[derive(PartialEq, Debug)]
pub enum Host {
    Tcp(String),
    Unix(String),
}

#[derive(PartialEq, Debug)]
pub struct JavaArgs {
    pub cp: Vec<String>,
    pub errors: Vec<String>,
    pub mode: ExecutionMode,
    pub props: Vec<(String, String)>,
    pub host: Host,
    pub version: bool,
    pub wait: bool,
}

pub fn parse_java_args<S: AsRef<str>>(args: &[S]) -> JavaArgs {
    // We want to aim to be a drop-in replacement for java, so we have to roll our own arg parser
    // because DocOpt/Clap/et al don't have the required features to match the rather strange java
    // arguments.

    let noop_flags = ["-server", "-d64", "-d32"];

    let mut jargs = JavaArgs {
        cp: vec![".".to_string()],
        errors: vec![],
        mode: ExecutionMode::Help { code: 1 },
        props: vec![],
        host: Host::Unix("/var/run/landlord/landlordd.sock".to_string()),
        version: false,
        wait: false,
    };

    let mut iter = args.iter().map(|r| r.as_ref());

    loop {
        let next = iter.next();

        match next {
            Some(entry) if !entry.starts_with("-") => {
                let mut items = vec![];

                while let Some(next) = iter.next() {
                    items.push(next.to_string());
                }

                jargs.mode = ExecutionMode::Class {
                    class: entry.to_string(),
                    args: items,
                }
            }

            Some(flag) if flag == "-jar" => {
                if let Some(file) = iter.next() {
                    let mut items = vec![];

                    while let Some(next) = iter.next() {
                        items.push(next.to_string());
                    }

                    jargs.mode = ExecutionMode::JarFile {
                        file: file.to_string(),
                        args: items,
                    };
                } else {
                    jargs
                        .errors
                        .push(format!("{} requires jar file specification", flag))
                }
            }

            Some(flag) if flag == "-?" || flag == "-help" => {
                jargs.mode = ExecutionMode::Help { code: 0 };
            }

            Some(flag) if flag == "-version" => {
                jargs.version = true;
                jargs.mode = ExecutionMode::Exit { code: 0 };
            }

            Some(flag) if flag == "-showversion" => {
                jargs.version = true;
            }

            Some(flag) if flag == "-cp" || flag == "-classpath" => {
                if let Some(cp) = iter.next() {
                    jargs.cp = cp.split(":").map(|s| s.to_string()).collect();
                } else {
                    jargs
                        .errors
                        .push(format!("{} requires class path specification", flag))
                }
            }

            Some(flag) if flag == "-H" || flag == "-host" => {
                if let Some(host) = iter.next() {
                    if host.starts_with("tcp://") {
                        jargs.host = Host::Tcp(host[6..].to_string());
                    } else if host.starts_with("unix://") {
                        jargs.host = Host::Unix(host[7..].to_string());
                    } else {
                        jargs.errors.push(format!(
                            "{} must begin with \"tcp://\" or \"unix://\"",
                            flag
                        ))
                    }
                } else {
                    jargs
                        .errors
                        .push(format!("{} requires host specification", flag))
                }
            }

            Some(flag) if flag.starts_with("-D") => {
                if let Some(s) = flag.get(2..) {
                    let parts: Vec<&str> = s.splitn(2, "=").collect();

                    if parts.len() == 2 {
                        jargs
                            .props
                            .push((parts[0].to_string(), parts[1].to_string()));
                    }
                }
            }

            Some(flag) if flag == "-wait" => {
                jargs.wait = true;
            }

            Some(flag) if noop_flags.contains(&flag) => {}

            Some(flag) => jargs.errors.push(format!("Unrecognized option: {}", flag)),

            None => {
                return jargs;
            }
        }
    }
}

#[test]
fn test_parse_java_args_help() {
    assert_eq!(
        parse_java_args(&["-?"]),
        JavaArgs {
            cp: vec![".".to_string()],
            errors: vec![],
            mode: ExecutionMode::Help { code: 0 },
            props: vec![],
            host: Host::Unix("/var/run/landlord/landlordd.sock".to_string()),
            version: false,
            wait: false,
        }
    );

    assert_eq!(
        parse_java_args(&["-help"]),
        JavaArgs {
            cp: vec![".".to_string()],
            errors: vec![],
            mode: ExecutionMode::Help { code: 0 },
            props: vec![],
            host: Host::Unix("/var/run/landlord/landlordd.sock".to_string()),
            version: false,
            wait: false,
        }
    );
}

#[test]
fn test_parse_java_version() {
    assert_eq!(
        parse_java_args(&["-version"]),
        JavaArgs {
            cp: vec![".".to_string()],
            errors: vec![],
            mode: ExecutionMode::Exit { code: 0 },
            props: vec![],
            host: Host::Unix("/var/run/landlord/landlordd.sock".to_string()),
            version: true,
            wait: false,
        }
    );
}

#[test]
fn test_parse_java_showversion() {
    assert_eq!(
        parse_java_args(&["-showversion", "-jar", "test.jar"]),
        JavaArgs {
            cp: vec![".".to_string()],
            errors: vec![],
            mode: ExecutionMode::JarFile {
                file: "test.jar".to_string(),
                args: vec![],
            },
            props: vec![],
            host: Host::Unix("/var/run/landlord/landlordd.sock".to_string()),
            version: true,
            wait: false,
        }
    );
}

#[test]
fn test_parse_java_jar() {
    assert_eq!(
        parse_java_args(&["-jar", "test.jar", "arg1", "arg2"]),
        JavaArgs {
            cp: vec![".".to_string()],
            errors: vec![],
            mode: ExecutionMode::JarFile {
                file: "test.jar".to_string(),
                args: vec!["arg1".to_string(), "arg2".to_string()],
            },
            props: vec![],
            host: Host::Unix("/var/run/landlord/landlordd.sock".to_string()),
            version: false,
            wait: false,
        }
    );
}

#[test]
fn test_parse_host() {
    assert_eq!(
        parse_java_args(&vec![
            "-H".to_string(),
            "tcp://1.2.3.4:5678".to_string(),
            "HelloWorld".to_string(),
        ]).host,
        Host::Tcp("1.2.3.4:5678".to_string())
    );

    assert_eq!(
        parse_java_args(&vec![
            "-host".to_string(),
            "tcp://1.2.3.4:5678".to_string(),
            "HelloWorld".to_string(),
        ]).host,
        Host::Tcp("1.2.3.4:5678".to_string())
    );

    assert_eq!(
        parse_java_args(&vec![
            "-host".to_string(),
            "tcp://".to_string(),
            "HelloWorld".to_string(),
        ]).host,
        Host::Tcp("".to_string())
    );

    assert_eq!(
        parse_java_args(&vec![
            "-host".to_string(),
            "unix:///my-file".to_string(),
            "HelloWorld".to_string(),
        ]).host,
        Host::Unix("/my-file".to_string())
    );

    assert_eq!(
        parse_java_args(&vec![
            "-host".to_string(),
            "unix://".to_string(),
            "HelloWorld".to_string(),
        ]).host,
        Host::Unix("".to_string())
    );
}

#[test]
fn test_all() {
    assert_eq!(
        parse_java_args(&[
            "-Dkey1=value1",
            "-Dkey2=value2",
            "-d32",
            "-d64",
            "-server",
            "-wait",
            "-host",
            "unix:///dev/null",
            "-cp",
            "/lib:/usr/lib",
            "com.hello.Example",
            "myarg one",
            "myargtwo",
        ]),
        JavaArgs {
            cp: vec!["/lib".to_string(), "/usr/lib".to_string()],
            errors: vec![],
            mode: ExecutionMode::Class {
                class: "com.hello.Example".to_string(),
                args: vec!["myarg one".to_string(), "myargtwo".to_string()],
            },
            props: vec![
                ("key1".to_string(), "value1".to_string()),
                ("key2".to_string(), "value2".to_string()),
            ],
            host: Host::Unix("/dev/null".to_string()),
            version: false,
            wait: true,
        }
    );
}

#[test]
fn test_invalid_flags() {
    assert_eq!(
        parse_java_args(&["-hello-world", "com.hello.Example"]),
        JavaArgs {
            cp: vec![".".to_string()],
            errors: vec!["Unrecognized option: -hello-world".to_string()],
            mode: ExecutionMode::Class {
                class: "com.hello.Example".to_string(),
                args: vec![],
            },
            props: vec![],
            host: Host::Unix("/var/run/landlord/landlordd.sock".to_string()),
            version: false,
            wait: false,
        }
    );
}
<commit_msg>Extract args::default to dedup<commit_after>#[derive(PartialEq, Debug)]
pub enum ExecutionMode {
    Class { class: String, args: Vec<String> },
    Exit { code: i32 },
    Help { code: i32 },
    JarFile { file: String, args: Vec<String> },
}

#[derive(PartialEq, Debug)]
pub enum Host {
    Tcp(String),
    Unix(String),
}

#[derive(PartialEq, Debug)]
pub struct JavaArgs {
    pub cp: Vec<String>,
    pub errors: Vec<String>,
    pub mode: ExecutionMode,
    pub props: Vec<(String, String)>,
    pub host: Host,
    pub version: bool,
    pub wait: bool,
}

fn default() -> JavaArgs {
    JavaArgs {
        cp: vec![".".to_string()],
        errors: vec![],
        mode: ExecutionMode::Help { code: 1 },
        props: vec![],
        host: Host::Unix("/var/run/landlord/landlordd.sock".to_string()),
        version: false,
        wait: false,
    }
}

pub fn parse_java_args<S: AsRef<str>>(args: &[S]) -> JavaArgs {
    // We want to aim to be a drop-in replacement for java, so we have to roll our own arg parser
    // because DocOpt/Clap/et al don't have the required features to match the rather strange java
    // arguments.

    let noop_flags = ["-server", "-d64", "-d32"];

    let mut jargs = default();

    let mut iter = args.iter().map(|r| r.as_ref());

    loop {
        let next = iter.next();

        match next {
            Some(entry) if !entry.starts_with("-") => {
                let mut items = vec![];

                while let Some(next) = iter.next() {
                    items.push(next.to_string());
                }

                jargs.mode = ExecutionMode::Class {
                    class: entry.to_string(),
                    args: items,
                }
            }

            Some(flag) if flag == "-jar" => {
                if let Some(file) = iter.next() {
                    let mut items = vec![];

                    while let Some(next) = iter.next() {
                        items.push(next.to_string());
                    }

                    jargs.mode = ExecutionMode::JarFile {
                        file: file.to_string(),
                        args: items,
                    };
                } else {
                    jargs
                        .errors
                        .push(format!("{} requires jar file specification", flag))
                }
            }

            Some(flag) if flag == "-?" || flag == "-help" => {
                jargs.mode = ExecutionMode::Help { code: 0 };
            }

            Some(flag) if flag == "-version" => {
                jargs.version = true;
                jargs.mode = ExecutionMode::Exit { code: 0 };
            }

            Some(flag) if flag == "-showversion" => {
                jargs.version = true;
            }

            Some(flag) if flag == "-cp" || flag == "-classpath" => {
                if let Some(cp) = iter.next() {
                    jargs.cp = cp.split(":").map(|s| s.to_string()).collect();
                } else {
                    jargs
                        .errors
                        .push(format!("{} requires class path specification", flag))
                }
            }

            Some(flag) if flag == "-H" || flag == "-host" => {
                if let Some(host) = iter.next() {
                    if host.starts_with("tcp://") {
                        jargs.host = Host::Tcp(host[6..].to_string());
                    } else if host.starts_with("unix://") {
                        jargs.host = Host::Unix(host[7..].to_string());
                    } else {
                        jargs.errors.push(format!(
                            "{} must begin with \"tcp://\" or \"unix://\"",
                            flag
                        ))
                    }
                } else {
                    jargs
                        .errors
                        .push(format!("{} requires host specification", flag))
                }
            }

            Some(flag) if flag.starts_with("-D") => {
                if let Some(s) = flag.get(2..) {
                    let parts: Vec<&str> = s.splitn(2, "=").collect();

                    if parts.len() == 2 {
                        jargs
                            .props
                            .push((parts[0].to_string(), parts[1].to_string()));
                    }
                }
            }

            Some(flag) if flag == "-wait" => {
                jargs.wait = true;
            }

            Some(flag) if noop_flags.contains(&flag) => {}

            Some(flag) => jargs.errors.push(format!("Unrecognized option: {}", flag)),

            None => {
                return jargs;
            }
        }
    }
}

#[test]
fn test_parse_java_args_help() {
    assert_eq!(
        parse_java_args(&["-?"]),
        JavaArgs {
            mode: ExecutionMode::Help { code: 0 },
            ..default()
        }
    );

    assert_eq!(
        parse_java_args(&["-help"]),
        JavaArgs {
            mode: ExecutionMode::Help { code: 0 },
            ..default()
        }
    );
}

#[test]
fn test_parse_java_version() {
    assert_eq!(
        parse_java_args(&["-version"]),
        JavaArgs {
            mode: ExecutionMode::Exit { code: 0 },
            version: true,
            ..default()
        }
    );
}

#[test]
fn test_parse_java_showversion() {
    assert_eq!(
        parse_java_args(&["-showversion", "-jar", "test.jar"]),
        JavaArgs {
            mode: ExecutionMode::JarFile {
                file: "test.jar".to_string(),
                args: vec![],
            },
            version: true,
            ..default()
        }
    );
}

#[test]
fn test_parse_java_jar() {
    assert_eq!(
        parse_java_args(&["-jar", "test.jar", "arg1", "arg2"]),
        JavaArgs {
            mode: ExecutionMode::JarFile {
                file: "test.jar".to_string(),
                args: vec!["arg1".to_string(), "arg2".to_string()],
            },
            ..default()
        }
    );
}

#[test]
fn test_parse_host() {
    assert_eq!(
        parse_java_args(&vec![
            "-H".to_string(),
            "tcp://1.2.3.4:5678".to_string(),
            "HelloWorld".to_string(),
        ]).host,
        Host::Tcp("1.2.3.4:5678".to_string())
    );

    assert_eq!(
        parse_java_args(&vec![
            "-host".to_string(),
            "tcp://1.2.3.4:5678".to_string(),
            "HelloWorld".to_string(),
        ]).host,
        Host::Tcp("1.2.3.4:5678".to_string())
    );

    assert_eq!(
        parse_java_args(&vec![
            "-host".to_string(),
            "tcp://".to_string(),
            "HelloWorld".to_string(),
        ]).host,
        Host::Tcp("".to_string())
    );

    assert_eq!(
        parse_java_args(&vec![
            "-host".to_string(),
            "unix:///my-file".to_string(),
            "HelloWorld".to_string(),
        ]).host,
        Host::Unix("/my-file".to_string())
    );

    assert_eq!(
        parse_java_args(&vec![
            "-host".to_string(),
            "unix://".to_string(),
            "HelloWorld".to_string(),
        ]).host,
        Host::Unix("".to_string())
    );
}

#[test]
fn test_all() {
    assert_eq!(
        parse_java_args(&[
            "-Dkey1=value1",
            "-Dkey2=value2",
            "-d32",
            "-d64",
            "-server",
            "-wait",
            "-host",
            "unix:///dev/null",
            "-cp",
            "/lib:/usr/lib",
            "com.hello.Example",
            "myarg one",
            "myargtwo",
        ]),
        JavaArgs {
            cp: vec!["/lib".to_string(), "/usr/lib".to_string()],
            errors: vec![],
            mode: ExecutionMode::Class {
                class: "com.hello.Example".to_string(),
                args: vec!["myarg one".to_string(), "myargtwo".to_string()],
            },
            props: vec![
                ("key1".to_string(), "value1".to_string()),
                ("key2".to_string(), "value2".to_string()),
            ],
            host: Host::Unix("/dev/null".to_string()),
            version: false,
            wait: true,
        }
    );
}

#[test]
fn test_invalid_flags() {
    assert_eq!(
        parse_java_args(&["-hello-world", "com.hello.Example"]),
        JavaArgs {
            errors: vec!["Unrecognized option: -hello-world".to_string()],
            mode: ExecutionMode::Class {
                class: "com.hello.Example".to_string(),
                args: vec![],
            },
            ..default()
        }
    );
}
<|endoftext|>


<commit_before>use ::cairo;
use ::glib::translate::*;
use ::glib_sys;
use ::libc;

#[cfg(test)]
use std::f64::consts::*;

use cairo::MatrixTrait;

use parse_transform::*;

use error::*;
use parsers::ParseError;
use parsers::Parse;

impl Parse for cairo::Matrix {
    type Data = ();
    type Err = AttributeError;

    fn parse (s: &str, _: ()) -> Result<cairo::Matrix, AttributeError> {
        parse_transform (s)
    }
}

pub fn parse_transform (s: &str) -> Result <cairo::Matrix, AttributeError> {
    let r = parse_TransformList (s);

    match r {
        Ok (m) => {
            m.try_invert ().map (|_| m)
                .map_err (|_| AttributeError::Value ("invalid transformation matrix".to_string ()))
        },

        Err (e) => {
            Err (AttributeError::Parse (ParseError::new (format! ("{:?}", e))))
        }
    }
}

#[cfg(test)]
fn make_rotation_matrix (angle_degrees: f64, tx: f64, ty: f64) -> cairo::Matrix {
    let angle = angle_degrees * PI / 180.0;

    let mut m = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, tx, ty);

    let mut r = cairo::Matrix::identity ();
    r.rotate (angle);
    m = cairo::Matrix::multiply (&r, &m);

    m = cairo::Matrix::multiply (&cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -tx, -ty), &m);
    m
}

#[no_mangle]
pub extern fn rsvg_parse_transform (out_matrix: *mut cairo::Matrix, s: *const libc::c_char) -> glib_sys::gboolean {
    assert! (!out_matrix.is_null ());
    assert! (!s.is_null ());

    let string = unsafe { String::from_glib_none (s) };
    let matrix: &mut cairo::Matrix = unsafe { &mut *out_matrix };

    match parse_transform (&string) {
        Ok (m) => {
            *matrix = m;
            true.to_glib ()
        },

        Err (_) => {
            *matrix = cairo::Matrix::identity ();
            false.to_glib ()
        }
    }
}

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

    #[test]
    fn parses_valid_transform () {
        let t = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
        let s = cairo::Matrix::new (10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
        let r = make_rotation_matrix (30.0, 10.0, 10.0);

        let a = cairo::Matrix::multiply (&s, &t);
        assert_eq! (parse_transform ("translate(20, 30), scale (10) rotate (30 10 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &a));
    }

    #[test]
    fn syntax_error_yields_parse_error () {
        match parse_transform ("foo") {
            Err (AttributeError::Parse (_)) => {},
            _ => { panic! (); }
        }

        match parse_transform ("matrix (1 2 3 4 5)") {
            Err (AttributeError::Parse (_)) => {},
            _ => { panic! (); }
        }
    }

    #[test]
    fn invalid_transform_yields_value_error () {
        match parse_transform ("matrix (0 0 0 0 0 0)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }

        match parse_transform ("scale (0), translate (10, 10)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }

        match parse_transform ("scale (0), skewX (90)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }
    }
}

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

    #[test]
    fn parses_numbers () {
        assert_eq! (parse_Num ("0"),          Ok (0.0));
        assert_eq! (parse_Num ("12345"),      Ok (12345.0));
        assert_eq! (parse_Num ("-123"),       Ok (-123.0));
        assert_eq! (parse_Num ("-123.25"),    Ok (-123.25));
        assert_eq! (parse_Num ("123.25"),     Ok (123.25));
        assert_eq! (parse_Num ("-.25"),       Ok (-0.25));
        assert_eq! (parse_Num (".25"),        Ok (0.25));
        assert_eq! (parse_Num ("-25."),       Ok (-25.0));
        assert_eq! (parse_Num ("25."),        Ok (25.0));
        assert_eq! (parse_Num ("22.5e1"),     Ok (225.0));
        assert_eq! (parse_Num ("-22.5e1"),    Ok (-225.0));
        assert_eq! (parse_Num ("-123.45e2"),  Ok (-12345.0));
        assert_eq! (parse_Num ("123.45E2"),   Ok (12345.0));
        assert_eq! (parse_Num ("-123.25e-2"), Ok (-1.2325));
        assert_eq! (parse_Num ("123.25E-2"),  Ok (1.2325));
    }

    #[test]
    fn parses_matrix () {
        assert_eq! (parse_TransformList ("matrix (1 2 3 4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.0, 3.0, 4.0, 5.0, 6.0));

        assert_eq! (parse_TransformList ("matrix (1,2,3,4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.0, 3.0, 4.0, 5.0, 6.0));

        assert_eq! (parse_TransformList ("matrix (1,2.25,-3.25e2,4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.25, -325.0, 4.0, 5.0, 6.0));
    }

    #[test]
    fn parses_translate () {
        assert_eq! (parse_TransformList ("translate(-1 -2)").unwrap (),
                    cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, -2.0));

        assert_eq! (parse_TransformList ("translate(-1, -2)").unwrap (),
                    cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, -2.0));

        assert_eq! (parse_TransformList ("translate(-1)").unwrap (),
                    cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, 0.0));
    }

    #[test]
    fn parses_scale () {
        assert_eq! (parse_TransformList ("scale(-1 -2)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -2.0, 0.0, 0.0));

        assert_eq! (parse_TransformList ("scale(-1, -2)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -2.0, 0.0, 0.0));

        assert_eq! (parse_TransformList ("scale(-1)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -1.0, 0.0, 0.0));
    }

    #[test]
    fn parses_rotate () {
        assert_eq! (parse_TransformList ("rotate (30)").unwrap (), make_rotation_matrix (30.0, 0.0, 0.0));
        assert_eq! (parse_TransformList ("rotate (30,-1,-2)").unwrap (), make_rotation_matrix (30.0, -1.0, -2.0));
        assert_eq! (parse_TransformList ("rotate (30, -1, -2)").unwrap (), make_rotation_matrix (30.0, -1.0, -2.0));
    }

    fn make_skew_x_matrix (angle_degrees: f64) -> cairo::Matrix {
        let a = angle_degrees * PI / 180.0;
        cairo::Matrix::new (1.0,      0.0,
                            a.tan (), 1.0,
                            0.0, 0.0)
    }

    fn make_skew_y_matrix (angle_degrees: f64) -> cairo::Matrix {
        let mut m = make_skew_x_matrix (angle_degrees);
        m.yx = m.xy;
        m.xy = 0.0;
        m
    }

    #[test]
    fn parses_skew_x () {
        assert_eq! (parse_TransformList ("skewX (30)").unwrap (), make_skew_x_matrix (30.0));
    }

    #[test]
    fn parses_skew_y () {
        assert_eq! (parse_TransformList ("skewY (30)").unwrap (), make_skew_y_matrix (30.0));
    }

    #[test]
    fn parses_transform_list () {
        let t = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
        let s = cairo::Matrix::new (10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
        let r = make_rotation_matrix (30.0, 10.0, 10.0);

        assert_eq! (parse_TransformList ("scale(10)rotate(30, 10, 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &s));

        assert_eq! (parse_TransformList ("translate(20, 30), scale (10)").unwrap (),
                    cairo::Matrix::multiply (&s, &t));

        let a = cairo::Matrix::multiply (&s, &t);
        assert_eq! (parse_TransformList ("translate(20, 30), scale (10) rotate (30 10 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &a));
    }
}
<commit_msg>Test the toplevel parse_transform(), not the lalrpop-generated implementation<commit_after>use ::cairo;
use ::glib::translate::*;
use ::glib_sys;
use ::libc;

#[cfg(test)]
use std::f64::consts::*;

use cairo::MatrixTrait;

use parse_transform::*;

use error::*;
use parsers::ParseError;
use parsers::Parse;

impl Parse for cairo::Matrix {
    type Data = ();
    type Err = AttributeError;

    fn parse (s: &str, _: ()) -> Result<cairo::Matrix, AttributeError> {
        parse_transform (s)
    }
}

pub fn parse_transform (s: &str) -> Result <cairo::Matrix, AttributeError> {
    let r = parse_TransformList (s);

    match r {
        Ok (m) => {
            m.try_invert ().map (|_| m)
                .map_err (|_| AttributeError::Value ("invalid transformation matrix".to_string ()))
        },

        Err (e) => {
            Err (AttributeError::Parse (ParseError::new (format! ("{:?}", e))))
        }
    }
}

#[cfg(test)]
fn make_rotation_matrix (angle_degrees: f64, tx: f64, ty: f64) -> cairo::Matrix {
    let angle = angle_degrees * PI / 180.0;

    let mut m = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, tx, ty);

    let mut r = cairo::Matrix::identity ();
    r.rotate (angle);
    m = cairo::Matrix::multiply (&r, &m);

    m = cairo::Matrix::multiply (&cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -tx, -ty), &m);
    m
}

#[no_mangle]
pub extern fn rsvg_parse_transform (out_matrix: *mut cairo::Matrix, s: *const libc::c_char) -> glib_sys::gboolean {
    assert! (!out_matrix.is_null ());
    assert! (!s.is_null ());

    let string = unsafe { String::from_glib_none (s) };
    let matrix: &mut cairo::Matrix = unsafe { &mut *out_matrix };

    match parse_transform (&string) {
        Ok (m) => {
            *matrix = m;
            true.to_glib ()
        },

        Err (_) => {
            *matrix = cairo::Matrix::identity ();
            false.to_glib ()
        }
    }
}

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

    #[test]
    fn parses_valid_transform () {
        let t = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
        let s = cairo::Matrix::new (10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
        let r = make_rotation_matrix (30.0, 10.0, 10.0);

        let a = cairo::Matrix::multiply (&s, &t);
        assert_eq! (parse_transform ("translate(20, 30), scale (10) rotate (30 10 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &a));
    }

    #[test]
    fn syntax_error_yields_parse_error () {
        match parse_transform ("foo") {
            Err (AttributeError::Parse (_)) => {},
            _ => { panic! (); }
        }

        match parse_transform ("matrix (1 2 3 4 5)") {
            Err (AttributeError::Parse (_)) => {},
            _ => { panic! (); }
        }
    }

    #[test]
    fn invalid_transform_yields_value_error () {
        match parse_transform ("matrix (0 0 0 0 0 0)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }

        match parse_transform ("scale (0), translate (10, 10)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }

        match parse_transform ("scale (0), skewX (90)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }
    }
}

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

    #[test]
    fn parses_numbers () {
        assert_eq! (parse_Num ("0"),          Ok (0.0));
        assert_eq! (parse_Num ("12345"),      Ok (12345.0));
        assert_eq! (parse_Num ("-123"),       Ok (-123.0));
        assert_eq! (parse_Num ("-123.25"),    Ok (-123.25));
        assert_eq! (parse_Num ("123.25"),     Ok (123.25));
        assert_eq! (parse_Num ("-.25"),       Ok (-0.25));
        assert_eq! (parse_Num (".25"),        Ok (0.25));
        assert_eq! (parse_Num ("-25."),       Ok (-25.0));
        assert_eq! (parse_Num ("25."),        Ok (25.0));
        assert_eq! (parse_Num ("22.5e1"),     Ok (225.0));
        assert_eq! (parse_Num ("-22.5e1"),    Ok (-225.0));
        assert_eq! (parse_Num ("-123.45e2"),  Ok (-12345.0));
        assert_eq! (parse_Num ("123.45E2"),   Ok (12345.0));
        assert_eq! (parse_Num ("-123.25e-2"), Ok (-1.2325));
        assert_eq! (parse_Num ("123.25E-2"),  Ok (1.2325));
    }

    #[test]
    fn parses_matrix () {
        assert_eq! (parse_transform ("matrix (1 2 3 4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.0, 3.0, 4.0, 5.0, 6.0));

        assert_eq! (parse_transform ("matrix (1,2,3,4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.0, 3.0, 4.0, 5.0, 6.0));

        assert_eq! (parse_transform ("matrix (1,2.25,-3.25e2,4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.25, -325.0, 4.0, 5.0, 6.0));
    }

    #[test]
    fn parses_translate () {
        assert_eq! (parse_transform ("translate(-1 -2)").unwrap (),
                    cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, -2.0));

        assert_eq! (parse_transform ("translate(-1, -2)").unwrap (),
                    cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, -2.0));

        assert_eq! (parse_transform ("translate(-1)").unwrap (),
                    cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, 0.0));
    }

    #[test]
    fn parses_scale () {
        assert_eq! (parse_transform ("scale(-1 -2)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -2.0, 0.0, 0.0));

        assert_eq! (parse_transform ("scale(-1, -2)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -2.0, 0.0, 0.0));

        assert_eq! (parse_transform ("scale(-1)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -1.0, 0.0, 0.0));
    }

    #[test]
    fn parses_rotate () {
        assert_eq! (parse_transform ("rotate (30)").unwrap (), make_rotation_matrix (30.0, 0.0, 0.0));
        assert_eq! (parse_transform ("rotate (30,-1,-2)").unwrap (), make_rotation_matrix (30.0, -1.0, -2.0));
        assert_eq! (parse_transform ("rotate (30, -1, -2)").unwrap (), make_rotation_matrix (30.0, -1.0, -2.0));
    }

    fn make_skew_x_matrix (angle_degrees: f64) -> cairo::Matrix {
        let a = angle_degrees * PI / 180.0;
        cairo::Matrix::new (1.0,      0.0,
                            a.tan (), 1.0,
                            0.0, 0.0)
    }

    fn make_skew_y_matrix (angle_degrees: f64) -> cairo::Matrix {
        let mut m = make_skew_x_matrix (angle_degrees);
        m.yx = m.xy;
        m.xy = 0.0;
        m
    }

    #[test]
    fn parses_skew_x () {
        assert_eq! (parse_transform ("skewX (30)").unwrap (), make_skew_x_matrix (30.0));
    }

    #[test]
    fn parses_skew_y () {
        assert_eq! (parse_transform ("skewY (30)").unwrap (), make_skew_y_matrix (30.0));
    }

    #[test]
    fn parses_transform_list () {
        let t = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
        let s = cairo::Matrix::new (10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
        let r = make_rotation_matrix (30.0, 10.0, 10.0);

        assert_eq! (parse_transform ("scale(10)rotate(30, 10, 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &s));

        assert_eq! (parse_transform ("translate(20, 30), scale (10)").unwrap (),
                    cairo::Matrix::multiply (&s, &t));

        let a = cairo::Matrix::multiply (&s, &t);
        assert_eq! (parse_transform ("translate(20, 30), scale (10) rotate (30 10 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &a));
    }
}
<|endoftext|>


<commit_before>use ffi::*;

use std::mem;
use std::ptr;

pub struct Buffer{
    buffer: *mut GstBuffer
}

impl Drop for Buffer{
    fn drop(&mut self){
        unsafe{
       		gst_mini_object_unref(self.buffer as *mut GstMiniObject);
        }
    }
}

macro_rules! gst_buffer_flag {
    ($getter:ident, $setter:ident, $flag:path) => (
        pub fn $getter(&self) -> bool {
            let flags = unsafe { (*self.gst_buffer()).mini_object.flags };
            flags & $flag != 0
        }

        pub fn $setter(&mut self, value: bool) {
            if !value {
                /* Clear */
                unsafe {
                    (*self.gst_buffer_mut()).mini_object.flags &= !$flag;
                }
            } else {
                /* Set */
                unsafe {
                    (*self.gst_buffer_mut()).mini_object.flags |= $flag;
                }
            }
        }
    )
}

impl Buffer{
    pub unsafe fn new(buffer: *mut GstBuffer, owned: bool) -> Option<Buffer>{
		if buffer != ptr::null_mut(){
	    	if !owned{
        		gst_mini_object_ref(buffer as *mut GstMiniObject);
        	}
	        let buff = Buffer{ buffer: buffer };
	        Some(buff)
	    }else{
	        None
	    }
    }

    pub fn map_read<'a,F:FnMut(&::MapInfo)->U,U>(&'a self, mut f: F ) -> Result<U,()>{
        unsafe{
	        let mut mapinfo = ::MapInfo::new();
	        if gst_buffer_map(self.buffer, &mut mapinfo, GST_MAP_READ) != 0{
	        	let ret = f(&mapinfo);
        		gst_buffer_unmap(self.buffer, &mut mapinfo);
        		Ok(ret)
        	}else{
        	    Err(())
        	}
	    }
    }

    pub fn map_write<'a,F:FnMut(&mut ::MapInfo)->U,U>(&'a mut self, mut f: F ) -> Result<U,()>{
        unsafe{
	        let mut mapinfo = ::MapInfo::new();
	        if gst_buffer_map(self.buffer, &mut mapinfo, GST_MAP_WRITE) != 0{
	        	let ret = f(&mut mapinfo);
        		gst_buffer_unmap(self.buffer, &mut mapinfo);
        		Ok(ret)
        	}else{
        	    Err(())
        	}
	    }
    }

    pub fn map<'a,F:FnMut(&mut ::MapInfo)->U,U>(&'a mut self, flags: ::Map, mut f: F ) -> Result<U,()>{
        unsafe{
	        let mut mapinfo = ::MapInfo::new();
	        if gst_buffer_map(self.buffer, &mut mapinfo, flags as u32) != 0{
	        	let ret = f(&mut mapinfo);
        		gst_buffer_unmap(self.buffer, &mut mapinfo);
        		Ok(ret)
        	}else{
        	    Err(())
        	}
	    }
    }

    pub fn size(&self) -> u64{
        unsafe{ gst_buffer_get_size(self.buffer) }
    }

	pub fn len<T>(&self) -> usize{
		(self.size() / mem::size_of::<T>() as u64)  as usize
	}

    pub fn gst_buffer(&self) -> *const GstBuffer{
        self.buffer
    }

    pub fn gst_buffer_mut(&mut self) -> *mut GstBuffer{
        self.buffer
    }

    pub fn flags(&self) -> guint {
        unsafe { (*self.gst_buffer()).mini_object.flags }
    }

    gst_buffer_flag!(is_live, set_live, GST_BUFFER_FLAG_LIVE);
    gst_buffer_flag!(is_decode_only, set_decode_only, GST_BUFFER_FLAG_DECODE_ONLY);
    gst_buffer_flag!(is_discont, set_discont, GST_BUFFER_FLAG_DISCONT);
    gst_buffer_flag!(is_resync, set_resync, GST_BUFFER_FLAG_RESYNC);
    gst_buffer_flag!(is_corrupted, set_corrupted, GST_BUFFER_FLAG_CORRUPTED);
    gst_buffer_flag!(is_marker, set_marker, GST_BUFFER_FLAG_MARKER);
    gst_buffer_flag!(is_header, set_header, GST_BUFFER_FLAG_HEADER);
    gst_buffer_flag!(is_gap, set_gap, GST_BUFFER_FLAG_GAP);
    gst_buffer_flag!(is_droppable, set_droppable, GST_BUFFER_FLAG_DROPPABLE);
    gst_buffer_flag!(is_delta_unit, set_delta_unit, GST_BUFFER_FLAG_DECODE_ONLY);
    gst_buffer_flag!(is_tag_memory, set_tag_memory, GST_BUFFER_FLAG_TAG_MEMORY);
}

impl ::Transfer<GstBuffer> for Buffer{
    unsafe fn transfer(self) ->  *mut GstBuffer{
        let buffer = self.buffer;
		mem::forget(self);
        buffer
    }
}
<commit_msg>buffer: impl Debug for Buffer<commit_after>use ffi::*;

use std::mem;
use std::ptr;
use std::fmt::{Debug, Formatter, Error};

pub struct Buffer{
    buffer: *mut GstBuffer
}

impl Drop for Buffer{
    fn drop(&mut self){
        unsafe{
       		gst_mini_object_unref(self.buffer as *mut GstMiniObject);
        }
    }
}

macro_rules! gst_buffer_flag {
    ($getter:ident, $setter:ident, $flag:path) => (
        pub fn $getter(&self) -> bool {
            let flags = unsafe { (*self.gst_buffer()).mini_object.flags };
            flags & $flag != 0
        }

        pub fn $setter(&mut self, value: bool) {
            if !value {
                /* Clear */
                unsafe {
                    (*self.gst_buffer_mut()).mini_object.flags &= !$flag;
                }
            } else {
                /* Set */
                unsafe {
                    (*self.gst_buffer_mut()).mini_object.flags |= $flag;
                }
            }
        }
    )
}

impl Buffer{
    pub unsafe fn new(buffer: *mut GstBuffer, owned: bool) -> Option<Buffer>{
		if buffer != ptr::null_mut(){
	    	if !owned{
        		gst_mini_object_ref(buffer as *mut GstMiniObject);
        	}
	        let buff = Buffer{ buffer: buffer };
	        Some(buff)
	    }else{
	        None
	    }
    }

    pub fn map_read<'a,F:FnMut(&::MapInfo)->U,U>(&'a self, mut f: F ) -> Result<U,()>{
        unsafe{
	        let mut mapinfo = ::MapInfo::new();
	        if gst_buffer_map(self.buffer, &mut mapinfo, GST_MAP_READ) != 0{
	        	let ret = f(&mapinfo);
        		gst_buffer_unmap(self.buffer, &mut mapinfo);
        		Ok(ret)
        	}else{
        	    Err(())
        	}
	    }
    }

    pub fn map_write<'a,F:FnMut(&mut ::MapInfo)->U,U>(&'a mut self, mut f: F ) -> Result<U,()>{
        unsafe{
	        let mut mapinfo = ::MapInfo::new();
	        if gst_buffer_map(self.buffer, &mut mapinfo, GST_MAP_WRITE) != 0{
	        	let ret = f(&mut mapinfo);
        		gst_buffer_unmap(self.buffer, &mut mapinfo);
        		Ok(ret)
        	}else{
        	    Err(())
        	}
	    }
    }

    pub fn map<'a,F:FnMut(&mut ::MapInfo)->U,U>(&'a mut self, flags: ::Map, mut f: F ) -> Result<U,()>{
        unsafe{
	        let mut mapinfo = ::MapInfo::new();
	        if gst_buffer_map(self.buffer, &mut mapinfo, flags as u32) != 0{
	        	let ret = f(&mut mapinfo);
        		gst_buffer_unmap(self.buffer, &mut mapinfo);
        		Ok(ret)
        	}else{
        	    Err(())
        	}
	    }
    }

    pub fn size(&self) -> u64{
        unsafe{ gst_buffer_get_size(self.buffer) }
    }

	pub fn len<T>(&self) -> usize{
		(self.size() / mem::size_of::<T>() as u64)  as usize
	}

    pub fn gst_buffer(&self) -> *const GstBuffer{
        self.buffer
    }

    pub fn gst_buffer_mut(&mut self) -> *mut GstBuffer{
        self.buffer
    }

    pub fn flags(&self) -> guint {
        unsafe { (*self.gst_buffer()).mini_object.flags }
    }

    gst_buffer_flag!(is_live, set_live, GST_BUFFER_FLAG_LIVE);
    gst_buffer_flag!(is_decode_only, set_decode_only, GST_BUFFER_FLAG_DECODE_ONLY);
    gst_buffer_flag!(is_discont, set_discont, GST_BUFFER_FLAG_DISCONT);
    gst_buffer_flag!(is_resync, set_resync, GST_BUFFER_FLAG_RESYNC);
    gst_buffer_flag!(is_corrupted, set_corrupted, GST_BUFFER_FLAG_CORRUPTED);
    gst_buffer_flag!(is_marker, set_marker, GST_BUFFER_FLAG_MARKER);
    gst_buffer_flag!(is_header, set_header, GST_BUFFER_FLAG_HEADER);
    gst_buffer_flag!(is_gap, set_gap, GST_BUFFER_FLAG_GAP);
    gst_buffer_flag!(is_droppable, set_droppable, GST_BUFFER_FLAG_DROPPABLE);
    gst_buffer_flag!(is_delta_unit, set_delta_unit, GST_BUFFER_FLAG_DECODE_ONLY);
    gst_buffer_flag!(is_tag_memory, set_tag_memory, GST_BUFFER_FLAG_TAG_MEMORY);
}

impl ::Transfer<GstBuffer> for Buffer{
    unsafe fn transfer(self) ->  *mut GstBuffer{
        let buffer = self.buffer;
		mem::forget(self);
        buffer
    }
}

macro_rules! fmt_buffer_flag {
    ($buffer:ident, $fmt:ident, $getter:ident) => (
        if $buffer.$getter() {
            try!($fmt.write_fmt(format_args!(", {}", stringify!($getter))));
        }
    )
}

impl Debug for Buffer {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        try!(fmt.write_fmt(format_args!("GstBuffer<{} bytes", self.size())));
        fmt_buffer_flag!(self, fmt, is_live);
        fmt_buffer_flag!(self, fmt, is_decode_only);
        fmt_buffer_flag!(self, fmt, is_discont);
        fmt_buffer_flag!(self, fmt, is_resync);
        fmt_buffer_flag!(self, fmt, is_corrupted);
        fmt_buffer_flag!(self, fmt, is_marker);
        fmt_buffer_flag!(self, fmt, is_header);
        fmt_buffer_flag!(self, fmt, is_gap);
        fmt_buffer_flag!(self, fmt, is_droppable);
        fmt_buffer_flag!(self, fmt, is_delta_unit);
        fmt_buffer_flag!(self, fmt, is_tag_memory);
        try!(fmt.write_str(">"));
        Ok(())
    }
}
<|endoftext|>


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

use crate::error::FatalError;
use toml;
use toml::value::Table;
use toml::Value;

///
/// `Cargo.toml` key under `package.metadata.sphinx` for specifying a default
/// location for the project Sphinx documentation files.
///
pub static DOCS_PATH: &str = "docs-path";

///
/// `Cargo.toml` key under `package.metadata.sphinx` for specifying a default
/// Git commit message for documentation commits.
///
pub static COMMIT_MESSAGE: &str = "commit-message";

///
/// `Cargo.toml` key under `package.metadata.sphinx` for specifying a default
/// boolean value for whether to sign documentation commits.
///
pub static SIGN_COMMIT: &str = "sign-commit";

///
/// `Cargo.toml` key under `package.metadata.sphinx` for specifying a default
/// Git remote name for pushing documentation commits.
///
pub static PUSH_REMOTE: &str = "push-remote";

///
/// `Cargo.toml` key under `package.metadata.sphinx` for specifying a default
/// branch for pushing documentation commits.
///
pub static PUSH_BRANCH: &str = "push-branch";

pub struct Config {
    toml: Table,
}

impl Config {
    fn load_from_file(path: &Path) -> io::Result<String> {
        let mut file = File::open(path)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(contents)
    }

    fn value_to_table(value: Value) -> Option<Table> {
        match value {
            Value::Table(s) => Some(s),
            _ => None,
        }
    }

    ///
    /// Parse the `Cargo.toml` file in the current directory and extract the keys
    /// under `package.metadata.sphinx`. This contains execution parameter defaults
    /// for the project using this cargo plugin.
    ///
    pub fn from(path: &str) -> Result<Config, FatalError> {
        let path = Path::new(path);
        let contents = Config::load_from_file(path).map_err(FatalError::IO)?;

        let parsed: Option<Table> = toml::from_str(&contents).ok();

        // Verify parsed TOML is valid.
        let mut toml: Table = parsed.ok_or(FatalError::InvalidCargoFileFormat)?;

        let config: Table = toml
            .remove("package")
            .and_then(Config::value_to_table)
            .and_then(|mut table| table.remove("metadata"))
            .and_then(Config::value_to_table)
            .and_then(|mut table| table.remove("sphinx"))
            .and_then(Config::value_to_table)
            .unwrap_or_default();

        // Verify the Cargo Sphinx TOML configuration.
        let valid_keys = vec![
            DOCS_PATH,
            COMMIT_MESSAGE,
            SIGN_COMMIT,
            PUSH_REMOTE,
            PUSH_BRANCH,
        ];

        for key in config.keys() {
            if !valid_keys.contains(&key.as_ref()) {
                return Err(FatalError::UnknownCargoFileKey {
                    key: key.to_string(),
                });
            }
        }

        Ok(Config { toml: config })
    }

    ///
    /// Get a string property from this configuration.
    ///
    pub fn get_str(&self, key: &'static str) -> Option<&str> {
        self.toml.get(key).and_then(|value| value.as_str())
    }

    ///
    /// Get a boolean property from this configuration.
    ///
    pub fn get_bool(&self, key: &'static str) -> Option<bool> {
        self.toml.get(key).and_then(|value| value.as_bool())
    }
}

#[test]
fn test_docs_path_config() {
    // Check docs_path is set to "docs" in Cargo.toml of this repository.
    let result: Result<Config, FatalError> = Config::from("Cargo.toml");
    let config: Config = result.expect("Parse cargo file failed.");

    assert_eq!(config.get_str("docs-path"), Some("docs"));
}

#[test]
fn test_commit_message_config() {
    // Check commit_message is set to "(cargo-sphinx) Generate docs." in
    // Cargo.toml of this repository.
    let result: Result<Config, FatalError> = Config::from("Cargo.toml");
    let config: Config = result.expect("Parse cargo file failed.");

    assert_eq!(
        config.get_str("commit-message"),
        Some("(cargo-sphinx) Generate docs.")
    );
}

#[test]
fn test_sign_commit_config() {
    // Check sign-commit is set to false in Cargo.toml of this repository.
    let result: Result<Config, FatalError> = Config::from("Cargo.toml");
    let config: Config = result.expect("Parse cargo file failed.");

    assert_eq!(config.get_bool("sign-commit"), Some(false));
}

#[test]
fn test_push_remote_config() {
    // Check push-remote is set to "origin" in Cargo.toml of this repository.
    let result: Result<Config, FatalError> = Config::from("Cargo.toml");
    let config: Config = result.expect("Parse cargo file failed.");

    assert_eq!(config.get_str("push-remote"), Some("origin"));
}

#[test]
fn test_push_branch_config() {
    // Check push-branch is set to "gh-pages" in Cargo.toml of this repository.
    let result: Result<Config, FatalError> = Config::from("Cargo.toml");
    let config: Config = result.expect("Parse cargo file failed.");

    assert_eq!(config.get_str("push-branch"), Some("gh-pages"));
}
<commit_msg>Remove single component path import to fix clippy break. (#133)<commit_after>use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::path::Path;

use crate::error::FatalError;
use toml::value::Table;
use toml::Value;

///
/// `Cargo.toml` key under `package.metadata.sphinx` for specifying a default
/// location for the project Sphinx documentation files.
///
pub static DOCS_PATH: &str = "docs-path";

///
/// `Cargo.toml` key under `package.metadata.sphinx` for specifying a default
/// Git commit message for documentation commits.
///
pub static COMMIT_MESSAGE: &str = "commit-message";

///
/// `Cargo.toml` key under `package.metadata.sphinx` for specifying a default
/// boolean value for whether to sign documentation commits.
///
pub static SIGN_COMMIT: &str = "sign-commit";

///
/// `Cargo.toml` key under `package.metadata.sphinx` for specifying a default
/// Git remote name for pushing documentation commits.
///
pub static PUSH_REMOTE: &str = "push-remote";

///
/// `Cargo.toml` key under `package.metadata.sphinx` for specifying a default
/// branch for pushing documentation commits.
///
pub static PUSH_BRANCH: &str = "push-branch";

pub struct Config {
    toml: Table,
}

impl Config {
    fn load_from_file(path: &Path) -> io::Result<String> {
        let mut file = File::open(path)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(contents)
    }

    fn value_to_table(value: Value) -> Option<Table> {
        match value {
            Value::Table(s) => Some(s),
            _ => None,
        }
    }

    ///
    /// Parse the `Cargo.toml` file in the current directory and extract the keys
    /// under `package.metadata.sphinx`. This contains execution parameter defaults
    /// for the project using this cargo plugin.
    ///
    pub fn from(path: &str) -> Result<Config, FatalError> {
        let path = Path::new(path);
        let contents = Config::load_from_file(path).map_err(FatalError::IO)?;

        let parsed: Option<Table> = toml::from_str(&contents).ok();

        // Verify parsed TOML is valid.
        let mut toml: Table = parsed.ok_or(FatalError::InvalidCargoFileFormat)?;

        let config: Table = toml
            .remove("package")
            .and_then(Config::value_to_table)
            .and_then(|mut table| table.remove("metadata"))
            .and_then(Config::value_to_table)
            .and_then(|mut table| table.remove("sphinx"))
            .and_then(Config::value_to_table)
            .unwrap_or_default();

        // Verify the Cargo Sphinx TOML configuration.
        let valid_keys = vec![
            DOCS_PATH,
            COMMIT_MESSAGE,
            SIGN_COMMIT,
            PUSH_REMOTE,
            PUSH_BRANCH,
        ];

        for key in config.keys() {
            if !valid_keys.contains(&key.as_ref()) {
                return Err(FatalError::UnknownCargoFileKey {
                    key: key.to_string(),
                });
            }
        }

        Ok(Config { toml: config })
    }

    ///
    /// Get a string property from this configuration.
    ///
    pub fn get_str(&self, key: &'static str) -> Option<&str> {
        self.toml.get(key).and_then(|value| value.as_str())
    }

    ///
    /// Get a boolean property from this configuration.
    ///
    pub fn get_bool(&self, key: &'static str) -> Option<bool> {
        self.toml.get(key).and_then(|value| value.as_bool())
    }
}

#[test]
fn test_docs_path_config() {
    // Check docs_path is set to "docs" in Cargo.toml of this repository.
    let result: Result<Config, FatalError> = Config::from("Cargo.toml");
    let config: Config = result.expect("Parse cargo file failed.");

    assert_eq!(config.get_str("docs-path"), Some("docs"));
}

#[test]
fn test_commit_message_config() {
    // Check commit_message is set to "(cargo-sphinx) Generate docs." in
    // Cargo.toml of this repository.
    let result: Result<Config, FatalError> = Config::from("Cargo.toml");
    let config: Config = result.expect("Parse cargo file failed.");

    assert_eq!(
        config.get_str("commit-message"),
        Some("(cargo-sphinx) Generate docs.")
    );
}

#[test]
fn test_sign_commit_config() {
    // Check sign-commit is set to false in Cargo.toml of this repository.
    let result: Result<Config, FatalError> = Config::from("Cargo.toml");
    let config: Config = result.expect("Parse cargo file failed.");

    assert_eq!(config.get_bool("sign-commit"), Some(false));
}

#[test]
fn test_push_remote_config() {
    // Check push-remote is set to "origin" in Cargo.toml of this repository.
    let result: Result<Config, FatalError> = Config::from("Cargo.toml");
    let config: Config = result.expect("Parse cargo file failed.");

    assert_eq!(config.get_str("push-remote"), Some("origin"));
}

#[test]
fn test_push_branch_config() {
    // Check push-branch is set to "gh-pages" in Cargo.toml of this repository.
    let result: Result<Config, FatalError> = Config::from("Cargo.toml");
    let config: Config = result.expect("Parse cargo file failed.");

    assert_eq!(config.get_str("push-branch"), Some("gh-pages"));
}
<|endoftext|>


<commit_before>extern crate envy;

use std::env::var;

const BASE_URL_KEY: &'static str = "K2_BASE_URL";
const BASE_URL: &'static str = "http://localhost:8080/k2/ctapi/";

#[derive(Deserialize)]
struct CtnPn {
    #[serde(rename="K2_CTN")]
    ctn: u16,

    #[serde(rename="K2_PN")]
    pn: u16,
}

pub fn base_url() -> String {
    let mut url = var(BASE_URL_KEY).unwrap_or(BASE_URL.to_string());
    if !url.trim().ends_with("/") {
        url.push_str("/");
    }

    url
}

pub fn ctn_or(ctn: u16) -> u16 {
    match envy::from_env::<CtnPn>() {
        Ok(config) => {
            debug!("From env: ctn {}", config.ctn);
            config.ctn
        }
        _ => ctn,
    }
}

pub fn pn_or(pn: u16) -> u16 {
    match envy::from_env::<CtnPn>() {
        Ok(config) => {
            debug!("From env: pn {}", config.pn);
            config.pn
        }
        _ => pn,
    }
}

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

    #[test]
    fn ctn_or_return_env_value_if_pn_is_set_too() {
        env::set_var("K2_CTN", "2");
        env::set_var("K2_PN", "4");
        let ctn = ctn_or(1);

        assert_eq!(ctn, 2);
    }

    #[test]
    fn ctn_or_return_given_value_if_pn_not_set() {
        env::remove_var("K2_CTN");
        env::remove_var("K2_PN");
        let ctn = ctn_or(1);

        assert_eq!(ctn, 1);

        env::set_var("K2_CTN", "2");
        let ctn = ctn_or(1);

        assert_eq!(ctn, 1);
    }

    #[test]
    fn pn_or_return_env_value_if_ctn_is_set_too() {
        env::set_var("K2_CTN", "2");
        env::set_var("K2_PN", "4");
        let pn = pn_or(1);

        assert_eq!(pn, 4);
    }

    #[test]
    fn pn_or_return_given_value_if_ctn_not_set() {
        env::remove_var("K2_CTN");
        env::remove_var("K2_PN");
        let pn = pn_or(1);

        assert_eq!(pn, 1);

        env::set_var("K2_PN", "2");
        let pn = pn_or(1);

        assert_eq!(pn, 1);
    }

    #[test]
    fn base_url_return_env_value() {
        env::set_var("K2_BASE_URL", "a/");
        let url = base_url();

        assert_eq!(url, "a/");

        env::set_var("K2_BASE_URL", "1");
        let url = base_url();

        assert_eq!(url, "1/");
    }

    #[test]
    fn base_url_return_default_value_if_no_env() {
        env::remove_var("K2_BASE_URL");
        let url = base_url();

        assert_eq!(url, BASE_URL);
    }
}
<commit_msg>rename variables and make static<commit_after>extern crate envy;

use std::env::var;

static BASE_URL_KEY: &str = "K2_BASE_URL";
static DEFAULT_BASE_URL: &str = "http://localhost:8080/k2/ctapi/";

#[derive(Deserialize)]
struct CtnPn {
    #[serde(rename="K2_CTN")]
    ctn: u16,

    #[serde(rename="K2_PN")]
    pn: u16,
}

pub fn base_url() -> String {
    let mut url = var(BASE_URL_KEY).unwrap_or(DEFAULT_BASE_URL.to_string());
    if !url.trim().ends_with("/") {
        url.push_str("/");
    }

    url
}

pub fn ctn_or(ctn: u16) -> u16 {
    match envy::from_env::<CtnPn>() {
        Ok(config) => {
            debug!("From env: ctn {}", config.ctn);
            config.ctn
        }
        _ => ctn,
    }
}

pub fn pn_or(pn: u16) -> u16 {
    match envy::from_env::<CtnPn>() {
        Ok(config) => {
            debug!("From env: pn {}", config.pn);
            config.pn
        }
        _ => pn,
    }
}

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

    #[test]
    fn ctn_or_return_env_value_if_pn_is_set_too() {
        env::set_var("K2_CTN", "2");
        env::set_var("K2_PN", "4");
        let ctn = ctn_or(1);

        assert_eq!(ctn, 2);
    }

    #[test]
    fn ctn_or_return_given_value_if_pn_not_set() {
        env::remove_var("K2_CTN");
        env::remove_var("K2_PN");
        let ctn = ctn_or(1);

        assert_eq!(ctn, 1);

        env::set_var("K2_CTN", "2");
        let ctn = ctn_or(1);

        assert_eq!(ctn, 1);
    }

    #[test]
    fn pn_or_return_env_value_if_ctn_is_set_too() {
        env::set_var("K2_CTN", "2");
        env::set_var("K2_PN", "4");
        let pn = pn_or(1);

        assert_eq!(pn, 4);
    }

    #[test]
    fn pn_or_return_given_value_if_ctn_not_set() {
        env::remove_var("K2_CTN");
        env::remove_var("K2_PN");
        let pn = pn_or(1);

        assert_eq!(pn, 1);

        env::set_var("K2_PN", "2");
        let pn = pn_or(1);

        assert_eq!(pn, 1);
    }

    #[test]
    fn base_url_return_env_value() {
        env::set_var(BASE_URL_KEY, "a/");
        let url = base_url();

        assert_eq!(url, "a/");

        env::set_var(BASE_URL_KEY, "1");
        let url = base_url();

        assert_eq!(url, "1/");
    }

    #[test]
    fn base_url_return_default_value_if_no_env() {
        env::remove_var(BASE_URL_KEY);
        let url = base_url();

        assert_eq!(url, DEFAULT_BASE_URL);
    }
}
<|endoftext|>


<commit_before>//! Keep configuration data in this module.

// Constants for defining a standard x-y coordinate system
/// Maximum pressure plotted on skew-t (bottom edge)
pub const MAXP: f32 = 1050.0; // mb
/// Minimum pressure plotted on skew-t (top edge)
pub const MINP: f32 = 99.0; // mb
/// Coldest temperature plotted at max pressure, on the bottom edge.
pub const MINT: f32 = -46.5; // C - at MAXP
/// Warmest temperature plotted at max pressure, on the bottom edge.
pub const MAXT: f32 = 50.5; // C - at MAXP

/// Line width in pixels for skew-t background lines.
pub const BACKGROUND_LINE_WIDTH: f64 = 1.0;

/// Background color
pub const BACKGROUND_RGB: (f64, f64, f64) = (1.0, 1.0, 1.0);
//// Background banding color
pub const BACKGROUND_BAND_RGB: (f64, f64, f64) = (0.933333333, 0.964705882, 0.917647059);
/// Color used to fill the dendritic snow growth zone
pub const DENDRTITIC_ZONE_RGB: (f64, f64, f64) = (0.0, 0.466666667, 0.780392157);
/// Color used to fill the hail growth zone
pub const HAIL_ZONE_RGB: (f64, f64, f64) = (0.0, 0.803921569, 0.803921569);
/// Color used for isotherms
pub const ISOTHERM_RGBA: (f64, f64, f64, f64) = (0.862745098, 0.388235294, 0.156862745, 1.0);
/// Color used for isobars
pub const ISOBAR_RGBA: (f64, f64, f64, f64) = (0.862745098, 0.388235294, 0.156862745, 1.0);
/// Color used for isentrops
pub const ISENTROP_RGBA: (f64, f64, f64, f64) = (0.862745098, 0.388235294, 0.156862745, 1.0);
/// Color used for isopleths of mixing ration
pub const ISO_MIXING_RATIO_RGBA: (f64, f64, f64, f64) = (0.090196078, 0.050980392, 0.360784314, 1.0);
/// Color used for isopleths of theta-e
pub const ISO_THETA_E_RGBA: (f64, f64, f64, f64) = (0.333333333, 0.662745098, 0.278431373, 1.0);

/// Color used for temperature plot
pub const TEMPERATURE_RGBA: (f64, f64, f64, f64) = (1.0, 0.0, 0.0, 1.0);
/// Line width for Temperature Plot
pub const TEMPERATURE_LINE_WIDTH: f64 = 2.0;
/// Color used for dew point plot
pub const DEW_POINT_RGBA: (f64, f64, f64, f64) = (0.0, 1.0, 0.0, 1.0);
/// Line width for Dew point Plot
pub const DEW_POINT_LINE_WIDTH: f64 = 2.0;
/// Color used for wet bulb plot
pub const WET_BULB_RGBA: (f64, f64, f64, f64) = (0.0, 1.0, 1.0, 1.0);
/// Line width for Dew point Plot
pub const WET_BULB_LINE_WIDTH: f64 = 1.0;

/// Isotherms to label on the chart.
pub const ISOTHERMS: [f32; 31] = [
    -150.0,
    -140.0,
    -130.0,
    -120.0,
    -110.0,
    -100.0,
    -90.0,
    -80.0,
    -70.0,
    -60.0,
    -50.0,
    -40.0,
    -30.0,
    -25.0,
    -20.0,
    -15.0,
    -10.0,
    -5.0,
    0.0,
    5.0,
    10.0,
    15.0,
    20.0,
    25.0,
    30.0,
    35.0,
    40.0,
    45.0,
    50.0,
    55.0,
    60.0,
];

/// Isobars to plot on the chart background.
pub const ISOBARS: [f32; 9] = [
    1050.0,
    1000.0,
    925.0,
    850.0,
    700.0,
    500.0,
    300.0,
    200.0,
    100.0,
];

/* ------------------------------------------------------------------------------------------------
Values below this line are automatically calculated based on the configuration values above and
should not be altered.
------------------------------------------------------------------------------------------------ */
include!("background_data.rs");
<commit_msg>Change data plot to black.<commit_after>//! Keep configuration data in this module.

// Constants for defining a standard x-y coordinate system
/// Maximum pressure plotted on skew-t (bottom edge)
pub const MAXP: f32 = 1050.0; // mb
/// Minimum pressure plotted on skew-t (top edge)
pub const MINP: f32 = 99.0; // mb
/// Coldest temperature plotted at max pressure, on the bottom edge.
pub const MINT: f32 = -46.5; // C - at MAXP
/// Warmest temperature plotted at max pressure, on the bottom edge.
pub const MAXT: f32 = 50.5; // C - at MAXP

/// Line width in pixels for skew-t background lines.
pub const BACKGROUND_LINE_WIDTH: f64 = 1.0;

/// Background color
pub const BACKGROUND_RGB: (f64, f64, f64) = (1.0, 1.0, 1.0);
//// Background banding color
pub const BACKGROUND_BAND_RGB: (f64, f64, f64) = (0.933333333, 0.964705882, 0.917647059);
/// Color used to fill the dendritic snow growth zone
pub const DENDRTITIC_ZONE_RGB: (f64, f64, f64) = (0.0, 0.466666667, 0.780392157);
/// Color used to fill the hail growth zone
pub const HAIL_ZONE_RGB: (f64, f64, f64) = (0.0, 0.803921569, 0.803921569);
/// Color used for isotherms
pub const ISOTHERM_RGBA: (f64, f64, f64, f64) = (0.862745098, 0.388235294, 0.156862745, 1.0);
/// Color used for isobars
pub const ISOBAR_RGBA: (f64, f64, f64, f64) = (0.862745098, 0.388235294, 0.156862745, 1.0);
/// Color used for isentrops
pub const ISENTROP_RGBA: (f64, f64, f64, f64) = (0.862745098, 0.388235294, 0.156862745, 1.0);
/// Color used for isopleths of mixing ration
pub const ISO_MIXING_RATIO_RGBA: (f64, f64, f64, f64) = (0.090196078, 0.050980392, 0.360784314, 1.0);
/// Color used for isopleths of theta-e
pub const ISO_THETA_E_RGBA: (f64, f64, f64, f64) = (0.333333333, 0.662745098, 0.278431373, 1.0);

/// Color used for temperature plot
pub const TEMPERATURE_RGBA: (f64, f64, f64, f64) = (0.0, 0.0, 0.0, 1.0);
/// Line width for Temperature Plot
pub const TEMPERATURE_LINE_WIDTH: f64 = 2.0;
/// Color used for dew point plot
pub const DEW_POINT_RGBA: (f64, f64, f64, f64) = (0.0, 0.0, 0.0, 1.0);
/// Line width for Dew point Plot
pub const DEW_POINT_LINE_WIDTH: f64 = 2.0;
/// Color used for wet bulb plot
pub const WET_BULB_RGBA: (f64, f64, f64, f64) = (0.0, 0.0, 0.0, 1.0);
/// Line width for Dew point Plot
pub const WET_BULB_LINE_WIDTH: f64 = 1.0;

/// Isotherms to label on the chart.
pub const ISOTHERMS: [f32; 31] = [
    -150.0,
    -140.0,
    -130.0,
    -120.0,
    -110.0,
    -100.0,
    -90.0,
    -80.0,
    -70.0,
    -60.0,
    -50.0,
    -40.0,
    -30.0,
    -25.0,
    -20.0,
    -15.0,
    -10.0,
    -5.0,
    0.0,
    5.0,
    10.0,
    15.0,
    20.0,
    25.0,
    30.0,
    35.0,
    40.0,
    45.0,
    50.0,
    55.0,
    60.0,
];

/// Isobars to plot on the chart background.
pub const ISOBARS: [f32; 9] = [
    1050.0,
    1000.0,
    925.0,
    850.0,
    700.0,
    500.0,
    300.0,
    200.0,
    100.0,
];

/* ------------------------------------------------------------------------------------------------
Values below this line are automatically calculated based on the configuration values above and
should not be altered.
------------------------------------------------------------------------------------------------ */
include!("background_data.rs");
<|endoftext|>


<commit_before>//  based on:
//  http://shootout.alioth.debian.org/
//   u64q/program.php?test=mandelbrot&lang=python3&id=2
//
//  takes 3 optional args:
//   square image size, defaults to 80_u
//   yield frequency, defaults to 10_u (yield every 10 spawns)
//   output path, default is "" (no output), "-" means stdout
//
//  in the shootout, they use 16000 as image size
//  yield frequency doesn't seem to have much effect
//
//  writes pbm image to output path

#[legacy_modes];

extern mod std;
use io::WriterUtil;
use std::map::HashMap;

struct cmplx {
    re: f64,
    im: f64
}

#[cfg(stage0)]
impl cmplx : ops::Mul<cmplx,cmplx> {
    pure fn mul(x: cmplx) -> cmplx {
        cmplx {
            re: self.re*x.re - self.im*x.im,
            im: self.re*x.im + self.im*x.re
        }
    }
}
#[cfg(stage1)]
#[cfg(stage2)]
impl cmplx : ops::Mul<cmplx,cmplx> {
    pure fn mul(x: &cmplx) -> cmplx {
        cmplx {
            re: self.re*(*x).re - self.im*(*x).im,
            im: self.re*(*x).im + self.im*(*x).re
        }
    }
}

#[cfg(stage0)]
impl cmplx : ops::Add<cmplx,cmplx> {
    pure fn add(x: cmplx) -> cmplx {
        cmplx {
            re: self.re + x.re,
            im: self.im + x.im
        }
    }
}
#[cfg(stage1)]
#[cfg(stage2)]
impl cmplx : ops::Add<cmplx,cmplx> {
    pure fn add(x: &cmplx) -> cmplx {
        cmplx {
            re: self.re + (*x).re,
            im: self.im + (*x).im
        }
    }
}

type line = {i: uint, b: ~[u8]};

pure fn cabs(x: cmplx) -> f64
{
    x.re*x.re + x.im*x.im
}

fn mb(x: cmplx) -> bool
{
    let mut z = cmplx {re: 0f64, im: 0f64};
    let mut i = 0;
    let mut in = true;
    while i < 50 {
        z = z*z + x;
        if cabs(z) >= 4f64 {
            in = false;
            break;
        }
        i += 1;
    }
    in
}

fn fillbyte(x: cmplx, incr: f64) -> u8 {
    let mut rv = 0_u8;
    let mut i = 0_u8;
    while i < 8_u8 {
        let z = cmplx {re: x.re + (i as f64)*incr, im: x.im};
        if mb(z) {
            rv += 1_u8 << (7_u8 - i);
        }
        i += 1_u8;
    }
    rv
}

fn chanmb(i: uint, size: uint, ch: comm::Chan<line>) -> ()
{
    let mut crv = ~[];
    let incr = 2f64/(size as f64);
    let y = incr*(i as f64) - 1f64;
    let xincr = 8f64*incr;
    for uint::range(0_u, size/8_u) |j| {
        let x = cmplx {re: xincr*(j as f64) - 1.5f64, im: y};
        vec::push(crv, fillbyte(x, incr));
    };
    comm::send(ch, {i:i, b:crv});
}

type devnull = {dn: int};

impl devnull: io::Writer {
    fn write(_b: &[const u8]) {}
    fn seek(_i: int, _s: io::SeekStyle) {}
    fn tell() -> uint {0_u}
    fn flush() -> int {0}
    fn get_type() -> io::WriterType { io::File }
}

fn writer(path: ~str, writech: comm::Chan<comm::Chan<line>>, size: uint)
{
    let p: comm::Port<line> = comm::Port();
    let ch = comm::Chan(p);
    comm::send(writech, ch);
    let cout: io::Writer = match path {
        ~"" => {
            {dn: 0} as io::Writer
        }
        ~"-" => {
            io::stdout()
        }
        _ => {
            result::get(
                io::file_writer(&Path(path),
                ~[io::Create, io::Truncate]))
        }
    };
    cout.write_line(~"P4");
    cout.write_line(fmt!("%u %u", size, size));
    let lines: HashMap<uint, ~[u8]> = HashMap();
    let mut done = 0_u;
    let mut i = 0_u;
    while i < size {
        let aline = comm::recv(p);
        if aline.i == done {
            debug!("W %u", aline.i);
            cout.write(aline.b);
            done += 1_u;
            let mut prev = done;
            while prev <= i {
                if lines.contains_key(prev) {
                    debug!("WS %u", prev);
                    cout.write(lines.get(prev));
                    done += 1_u;
                    lines.remove(prev);
                    prev += 1_u;
                }
                else {
                    break
                }
            };
        }
        else {
            debug!("S %u", aline.i);
            lines.insert(aline.i, aline.b);
        };
        i += 1_u;
    }
}

fn main(args: ~[~str]) {
    let args = if os::getenv(~"RUST_BENCH").is_some() {
        ~[~"", ~"4000", ~"10"]
    } else {
        args
    };

    let path = if vec::len(args) < 4_u { ~"" }
    else { args[3] };

    let yieldevery = if vec::len(args) < 3_u { 10_u }
    else { uint::from_str(args[2]).get() };

    let size = if vec::len(args) < 2_u { 80_u }
    else { uint::from_str(args[1]).get() };

    let writep = comm::Port();
    let writech = comm::Chan(writep);
    do task::spawn {
        writer(path, writech, size);
    };
    let ch = comm::recv(writep);
    for uint::range(0_u, size) |j| {
        task::spawn(|| chanmb(j, size, ch) );
        if j % yieldevery == 0_u {
            debug!("Y %u", j);
            task::yield();
        };
    };
}
<commit_msg>remove cfg(stage0) version of Eq, not needed for tests<commit_after>//  based on:
//  http://shootout.alioth.debian.org/
//   u64q/program.php?test=mandelbrot&lang=python3&id=2
//
//  takes 3 optional args:
//   square image size, defaults to 80_u
//   yield frequency, defaults to 10_u (yield every 10 spawns)
//   output path, default is "" (no output), "-" means stdout
//
//  in the shootout, they use 16000 as image size
//  yield frequency doesn't seem to have much effect
//
//  writes pbm image to output path

#[legacy_modes];

extern mod std;
use io::WriterUtil;
use std::map::HashMap;

struct cmplx {
    re: f64,
    im: f64
}

impl cmplx : ops::Mul<cmplx,cmplx> {
    pure fn mul(x: &cmplx) -> cmplx {
        cmplx {
            re: self.re*(*x).re - self.im*(*x).im,
            im: self.re*(*x).im + self.im*(*x).re
        }
    }
}

impl cmplx : ops::Add<cmplx,cmplx> {
    pure fn add(x: &cmplx) -> cmplx {
        cmplx {
            re: self.re + (*x).re,
            im: self.im + (*x).im
        }
    }
}

type line = {i: uint, b: ~[u8]};

pure fn cabs(x: cmplx) -> f64
{
    x.re*x.re + x.im*x.im
}

fn mb(x: cmplx) -> bool
{
    let mut z = cmplx {re: 0f64, im: 0f64};
    let mut i = 0;
    let mut in = true;
    while i < 50 {
        z = z*z + x;
        if cabs(z) >= 4f64 {
            in = false;
            break;
        }
        i += 1;
    }
    in
}

fn fillbyte(x: cmplx, incr: f64) -> u8 {
    let mut rv = 0_u8;
    let mut i = 0_u8;
    while i < 8_u8 {
        let z = cmplx {re: x.re + (i as f64)*incr, im: x.im};
        if mb(z) {
            rv += 1_u8 << (7_u8 - i);
        }
        i += 1_u8;
    }
    rv
}

fn chanmb(i: uint, size: uint, ch: comm::Chan<line>) -> ()
{
    let mut crv = ~[];
    let incr = 2f64/(size as f64);
    let y = incr*(i as f64) - 1f64;
    let xincr = 8f64*incr;
    for uint::range(0_u, size/8_u) |j| {
        let x = cmplx {re: xincr*(j as f64) - 1.5f64, im: y};
        vec::push(crv, fillbyte(x, incr));
    };
    comm::send(ch, {i:i, b:crv});
}

type devnull = {dn: int};

impl devnull: io::Writer {
    fn write(_b: &[const u8]) {}
    fn seek(_i: int, _s: io::SeekStyle) {}
    fn tell() -> uint {0_u}
    fn flush() -> int {0}
    fn get_type() -> io::WriterType { io::File }
}

fn writer(path: ~str, writech: comm::Chan<comm::Chan<line>>, size: uint)
{
    let p: comm::Port<line> = comm::Port();
    let ch = comm::Chan(p);
    comm::send(writech, ch);
    let cout: io::Writer = match path {
        ~"" => {
            {dn: 0} as io::Writer
        }
        ~"-" => {
            io::stdout()
        }
        _ => {
            result::get(
                io::file_writer(&Path(path),
                ~[io::Create, io::Truncate]))
        }
    };
    cout.write_line(~"P4");
    cout.write_line(fmt!("%u %u", size, size));
    let lines: HashMap<uint, ~[u8]> = HashMap();
    let mut done = 0_u;
    let mut i = 0_u;
    while i < size {
        let aline = comm::recv(p);
        if aline.i == done {
            debug!("W %u", aline.i);
            cout.write(aline.b);
            done += 1_u;
            let mut prev = done;
            while prev <= i {
                if lines.contains_key(prev) {
                    debug!("WS %u", prev);
                    cout.write(lines.get(prev));
                    done += 1_u;
                    lines.remove(prev);
                    prev += 1_u;
                }
                else {
                    break
                }
            };
        }
        else {
            debug!("S %u", aline.i);
            lines.insert(aline.i, aline.b);
        };
        i += 1_u;
    }
}

fn main(args: ~[~str]) {
    let args = if os::getenv(~"RUST_BENCH").is_some() {
        ~[~"", ~"4000", ~"10"]
    } else {
        args
    };

    let path = if vec::len(args) < 4_u { ~"" }
    else { args[3] };

    let yieldevery = if vec::len(args) < 3_u { 10_u }
    else { uint::from_str(args[2]).get() };

    let size = if vec::len(args) < 2_u { 80_u }
    else { uint::from_str(args[1]).get() };

    let writep = comm::Port();
    let writech = comm::Chan(writep);
    do task::spawn {
        writer(path, writech, size);
    };
    let ch = comm::recv(writep);
    for uint::range(0_u, size) |j| {
        task::spawn(|| chanmb(j, size, ch) );
        if j % yieldevery == 0_u {
            debug!("Y %u", j);
            task::yield();
        };
    };
}
<|endoftext|>