<commit_before>//! The YAML parser. Here is where we convert the yaml in to commands to be
/// processed later.

use yaml_rust::Yaml;
use command::*;

#[cfg(test)] use yaml_rust::{YamlLoader};

/// Here was pass in the parsed yaml and project name. The purpose of this main
/// loop is to build the stack of commands that are run to setup a users tmux
/// session.
///
/// yaml_string: The parsed yaml from the config file.
/// project_name: The name of the project.
pub fn main(yaml_string: &Vec<Yaml>, project_name: &String, daemonize: bool) -> Result<Vec<Command>, String> {
    let mut commands: Vec<Command> = vec!();

    // There should only be one doc but it's a vec so loop it.
    for doc in yaml_string {
        let root = match doc["root"].as_str() {
            Some(x) => Some(x.to_string()),
            None    => None
        };

        let pre = match doc["pre"].as_str() {
            Some(x) => Some(x.to_string()),
            None    => None
        };

        // A clojure used to capture the current local root and pre Options.
        // This way we can call the clojure to create common SendKeys command
        // like changing the directory or executing a system command from the
        // `pre` option.
        let common_commands = |target: String| -> Vec<Command> {
            let mut commands2 = vec!();

            // SendKeys to change to the `root` directory
            if root.is_some() {
                let r = root.clone().unwrap();
                commands2.push(Command::SendKeys(SendKeys{
                    target: target.clone(),
                    exec: format!("cd \"{}\"", r)
                }));
            };

            // SendKeys for the Pre option
            if pre.is_some() {
                let p = pre.clone().unwrap();
                commands2.push(Command::SendKeys(SendKeys{
                    target: target.clone(),
                    exec: p
                }));
            };

            commands2
        };

        let windows = doc["windows"].as_vec().expect("No Windows have been defined.");

        for window in windows.iter() {
            match window {
                &Yaml::Hash(ref h)  => {
                    for (k, v) in h {
                        if v.as_hash().is_some() {
                            commands.push(Command::Window(Window{
                                session_name: project_name.clone(),
                                name: k.as_str().unwrap().to_string()
                            }));

                            let target = format!("{}:{}", project_name, k.as_str().unwrap());
                            commands.append(&mut try!(pane_matcher(v, &target, &common_commands)));
                        } else {
                            commands.push(Command::Window(Window{
                                session_name: project_name.clone(),
                                name: try!(k.as_str().ok_or_else(|| "Windows require being named in your config.").map(|x| x.to_string()))
                            }));

                            let t = format!("{}:{}", project_name, k.as_str().unwrap()).to_string();
                            commands.append(&mut common_commands(t.to_string()));

                            // SendKeys for the exec command
                            if v.as_str().is_some() {
                                let ex = v.as_str().unwrap();
                                if !ex.is_empty() {
                                    commands.push(Command::SendKeys(SendKeys{
                                        target: format!("{}:{}", project_name, k.as_str().unwrap()).to_string(),
                                        exec: v.as_str().unwrap().to_string()
                                    }));
                                };
                            }
                        }
                    }
                },
                &Yaml::String(ref s) => {
                    commands.push(Command::Window(Window{
                        session_name: project_name.clone(),
                        name: s.clone()
                    }));

                    let t = format!("{}:{}", &project_name, &s);
                    commands.append(&mut common_commands(t.to_string()));
                },
                &Yaml::Integer(ref s) => {
                    commands.push(Command::Window(Window{
                        session_name: project_name.clone(),
                        name: s.to_string()
                    }));

                    let t = format!("{}:{}", &project_name, &s);
                    commands.append(&mut common_commands(t.to_string()));
                },
                _ => panic!("Muxed config file formatting isn't recognized.")
            };
        };
    };

    if !daemonize { commands.push(Command::Attach(Attach{name: project_name.clone()})) };

    let (first, commands) = commands.split_first().unwrap();
    let mut remains = commands.to_vec();

    match first {
        &Command::Window(ref w) => {
            remains.insert(0, Command::Session(Session{
                name: project_name.clone(),
                window_name: w.name.clone()
            }));
        },
        _ => {}
    };

    Ok(remains)
}

/// Pane matcher is for breaking apart the panes. Splitting windows when needed
/// and executing commands as needed.
fn pane_matcher<T>(window: &Yaml, target: &str, common_commands: T) -> Result<Vec<Command>, String>
    where T : Fn(String) -> Vec<Command> {

    let mut commands = vec!();
    let panes = window["panes"].as_vec().expect("Something is wrong with panes.");

    for (i, pane) in panes.iter().enumerate() {
        let t = format!("{}.{}", target, i);
        // For every pane, we need one less split.
        // ex. An existing window to become 2 panes, needs 1 split.
        if i < (panes.len()-1) {
            commands.push(Command::Split(Split{
                target: t.to_string()
            }));
        };

        // Call the common_commands clojure to execute `cd` and `pre` options in
        // pane splits.
        commands.append(&mut common_commands(t.to_string()));

        // Execute given commands in each new pane after all splits are
        // complete.
        if pane.as_str().is_some() {
            let p = pane.as_str().unwrap();
            if !p.is_empty() {
                commands.push(Command::SendKeys(SendKeys{
                    target: t.to_string(),
                    exec: p.to_string()
                }));
            };
        };
    };

    // After all panes are split select the layout for the window
    if window["layout"].as_str().is_some() {
        let err = format!("A problem with the specified layout for the window: {}", target);
        let layout = window["layout"].as_str().expect(err.as_str()).to_string();
        commands.push(Command::Layout(Layout{
            target: target.to_string(),
            layout: layout
        }));
    };

    Ok(commands)
}

#[test]
pub fn expect_1_session() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::Session(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn expect_2_windows_from_array() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::Window(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 2)
}

#[test]
pub fn expect_1_attach() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::Attach(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn expect_2_windows_with_mixed_type_names() {
    let s = "---
windows: [1, 'vim', 3]
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::Window(_) => true,
        _ => false
    }).collect();
    assert_eq!(remains.len(), 2)
}

#[test]
pub fn expect_2_windows_from_list() {
    let s = "---
windows:
  - cargo: ''
  - vim: ''
  - git: ''
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::Window(_) => true,
        _ => false
    }).collect();
    assert_eq!(remains.len(), 2)
}

#[test]
pub fn expect_ok_with_empty_syscommands() {
    let s = "---
windows:
  - editor:
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let result = main(&yaml, &"muxed".to_string(), false);
    assert!(result.is_ok())
}

#[test]
pub fn expect_no_send_keys_commands() {
    let s = "---
windows:
  - editor:
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::SendKeys(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 0)
}

#[test]
pub fn expect_err_with_nameless_window() {
    let s = "---
windows:
  - : ls
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let result = main(&yaml, &"muxed".to_string(), false);
    assert!(result.is_err())
}

#[test]
pub fn expect_ok_with_empty_panes_syscommands() {
    let s = "---
windows:
  - cargo:
      layout: 'main-vertical'
      panes:
        -
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let result = main(&yaml, &"muxed".to_string(), false);
    assert!(result.is_ok())
}

#[test]
pub fn expect_no_send_keys_with_empty_panes_syscommands() {
    let s = "---
windows:
  - editor:
      panes:
        -
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::SendKeys(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 0)
}

#[test]
pub fn expect_1_split_window() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::Split(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn expect_1_layout() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::Layout(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn expect_1_session_with_panes_array() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::Session(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn expect_no_layout() {
    let s = "---
windows:
  - editor:
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::Layout(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 0)
}

#[test]
pub fn expect_three_send_keys_commands_from_pre() {
    let s = "---
pre: 'ls'
windows:
  - editor:
      panes:
        -
        -
  - logs:
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::SendKeys(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 3)
}

#[test]
pub fn expect_no_send_keys_with_blank_panes() {
    let s = "---
windows:
  - editor:
      panes: ['','','']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::SendKeys(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 0)
}

#[test]
pub fn expect_no_send_keys_with_blank_window() {
    let s = "---
windows:
  - editor: ''
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::SendKeys(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 0)
}

#[test]
pub fn expect_full_directory_name() {
    let s = "---
root: ~/JustPlainSimple Technologies Inc./financials/ledgers
windows:
  - dir: ''
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Command = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().find(|x| match x {
        &Command::SendKeys(_) => true,
        _ => false
    }).unwrap();

    let root = match remains {
        Command::SendKeys(ref k) => k,
        _ => panic!("nope")
    };

    assert_eq!(root.exec, "cd \"~/JustPlainSimple Technologies Inc./financials/ledgers\"")
}
<commit_msg>issue-9 Select window and pane<commit_after>//! The YAML parser. Here is where we convert the yaml in to commands to be
/// processed later.

use yaml_rust::Yaml;
use command::*;

#[cfg(test)] use yaml_rust::{YamlLoader};

/// Here was pass in the parsed yaml and project name. The purpose of this main
/// loop is to build the stack of commands that are run to setup a users tmux
/// session.
///
/// yaml_string: The parsed yaml from the config file.
/// project_name: The name of the project.
pub fn main(yaml_string: &Vec<Yaml>, project_name: &String, daemonize: bool) -> Result<Vec<Command>, String> {
    let mut commands: Vec<Command> = vec!();

    // There should only be one doc but it's a vec so loop it.
    for doc in yaml_string {
        let root = match doc["root"].as_str() {
            Some(x) => Some(x.to_string()),
            None    => None
        };

        let pre = match doc["pre"].as_str() {
            Some(x) => Some(x.to_string()),
            None    => None
        };

        // A clojure used to capture the current local root and pre Options.
        // This way we can call the clojure to create common SendKeys command
        // like changing the directory or executing a system command from the
        // `pre` option.
        let common_commands = |target: String| -> Vec<Command> {
            let mut commands2 = vec!();

            // SendKeys to change to the `root` directory
            if root.is_some() {
                let r = root.clone().unwrap();
                commands2.push(Command::SendKeys(SendKeys{
                    target: target.clone(),
                    exec: format!("cd \"{}\"", r)
                }));
            };

            // SendKeys for the Pre option
            if pre.is_some() {
                let p = pre.clone().unwrap();
                commands2.push(Command::SendKeys(SendKeys{
                    target: target.clone(),
                    exec: p
                }));
            };

            commands2
        };

        let windows = doc["windows"].as_vec().expect("No Windows have been defined.");

        for window in windows.iter() {
            match window {
                &Yaml::Hash(ref h)  => {
                    for (k, v) in h {
                        if v.as_hash().is_some() {
                            commands.push(Command::Window(Window{
                                session_name: project_name.clone(),
                                name: k.as_str().unwrap().to_string()
                            }));

                            let target = format!("{}:{}", project_name, k.as_str().unwrap());
                            commands.append(&mut try!(pane_matcher(v, &target, &common_commands)));
                        } else {
                            commands.push(Command::Window(Window{
                                session_name: project_name.clone(),
                                name: try!(k.as_str().ok_or_else(|| "Windows require being named in your config.").map(|x| x.to_string()))
                            }));

                            let t = format!("{}:{}", project_name, k.as_str().unwrap()).to_string();
                            commands.append(&mut common_commands(t.to_string()));

                            // SendKeys for the exec command
                            if v.as_str().is_some() {
                                let ex = v.as_str().unwrap();
                                if !ex.is_empty() {
                                    commands.push(Command::SendKeys(SendKeys{
                                        target: format!("{}:{}", project_name, k.as_str().unwrap()).to_string(),
                                        exec: v.as_str().unwrap().to_string()
                                    }));
                                };
                            }
                        }
                    }
                },
                &Yaml::String(ref s) => {
                    commands.push(Command::Window(Window{
                        session_name: project_name.clone(),
                        name: s.clone()
                    }));

                    let t = format!("{}:{}", &project_name, &s);
                    commands.append(&mut common_commands(t.to_string()));
                },
                &Yaml::Integer(ref s) => {
                    commands.push(Command::Window(Window{
                        session_name: project_name.clone(),
                        name: s.to_string()
                    }));

                    let t = format!("{}:{}", &project_name, &s);
                    commands.append(&mut common_commands(t.to_string()));
                },
                _ => panic!("Muxed config file formatting isn't recognized.")
            };
        };
    };

    let (first, commands) = commands.split_first().unwrap();
    let mut remains = commands.to_vec();

    match first {
        &Command::Window(ref w) => {
            remains.insert(0, Command::Session(Session{
                name: project_name.clone(),
                window_name: w.name.clone()
            }));

            remains.push(Command::SelectWindow(SelectWindow{target: format!("{}:{}", &project_name, &w.name)}));
            remains.push(Command::SelectPane(SelectPane{target: format!("{}:{}.top", &project_name, &w.name)}));
        },
        _ => {}
    };

    if !daemonize { remains.push(Command::Attach(Attach{name: project_name.clone()})) };

    Ok(remains)
}

/// Pane matcher is for breaking apart the panes. Splitting windows when needed
/// and executing commands as needed.
fn pane_matcher<T>(window: &Yaml, target: &str, common_commands: T) -> Result<Vec<Command>, String>
    where T : Fn(String) -> Vec<Command> {

    let mut commands = vec!();
    let panes = window["panes"].as_vec().expect("Something is wrong with panes.");

    for (i, pane) in panes.iter().enumerate() {
        let t = format!("{}.{}", target, i);
        // For every pane, we need one less split.
        // ex. An existing window to become 2 panes, needs 1 split.
        if i < (panes.len()-1) {
            commands.push(Command::Split(Split{
                target: t.to_string()
            }));
        };

        // Call the common_commands clojure to execute `cd` and `pre` options in
        // pane splits.
        commands.append(&mut common_commands(t.to_string()));

        // Execute given commands in each new pane after all splits are
        // complete.
        if pane.as_str().is_some() {
            let p = pane.as_str().unwrap();
            if !p.is_empty() {
                commands.push(Command::SendKeys(SendKeys{
                    target: t.to_string(),
                    exec: p.to_string()
                }));
            };
        };
    };

    // After all panes are split select the layout for the window
    if window["layout"].as_str().is_some() {
        let err = format!("A problem with the specified layout for the window: {}", target);
        let layout = window["layout"].as_str().expect(err.as_str()).to_string();
        commands.push(Command::Layout(Layout{
            target: target.to_string(),
            layout: layout
        }));
    };

    Ok(commands)
}

#[test]
pub fn expect_1_session() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::Session(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn expect_2_windows_from_array() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::Window(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 2)
}

#[test]
pub fn expect_1_attach() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::Attach(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn expect_2_windows_with_mixed_type_names() {
    let s = "---
windows: [1, 'vim', 3]
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::Window(_) => true,
        _ => false
    }).collect();
    assert_eq!(remains.len(), 2)
}

#[test]
pub fn expect_2_windows_from_list() {
    let s = "---
windows:
  - cargo: ''
  - vim: ''
  - git: ''
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::Window(_) => true,
        _ => false
    }).collect();
    assert_eq!(remains.len(), 2)
}

#[test]
pub fn expect_ok_with_empty_syscommands() {
    let s = "---
windows:
  - editor:
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let result = main(&yaml, &"muxed".to_string(), false);
    assert!(result.is_ok())
}

#[test]
pub fn expect_no_send_keys_commands() {
    let s = "---
windows:
  - editor:
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::SendKeys(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 0)
}

#[test]
pub fn expect_err_with_nameless_window() {
    let s = "---
windows:
  - : ls
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let result = main(&yaml, &"muxed".to_string(), false);
    assert!(result.is_err())
}

#[test]
pub fn expect_ok_with_empty_panes_syscommands() {
    let s = "---
windows:
  - cargo:
      layout: 'main-vertical'
      panes:
        -
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let result = main(&yaml, &"muxed".to_string(), false);
    assert!(result.is_ok())
}

#[test]
pub fn expect_no_send_keys_with_empty_panes_syscommands() {
    let s = "---
windows:
  - editor:
      panes:
        -
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::SendKeys(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 0)
}

#[test]
pub fn expect_1_split_window() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::Split(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn expect_1_layout() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::Layout(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn expect_1_session_with_panes_array() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::Session(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn expect_no_layout() {
    let s = "---
windows:
  - editor:
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::Layout(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 0)
}

#[test]
pub fn expect_three_send_keys_commands_from_pre() {
    let s = "---
pre: 'ls'
windows:
  - editor:
      panes:
        -
        -
  - logs:
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::SendKeys(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 3)
}

#[test]
pub fn expect_no_send_keys_with_blank_panes() {
    let s = "---
windows:
  - editor:
      panes: ['','','']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::SendKeys(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 0)
}

#[test]
pub fn expect_no_send_keys_with_blank_window() {
    let s = "---
windows:
  - editor: ''
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::SendKeys(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 0)
}

#[test]
pub fn expect_full_directory_name() {
    let s = "---
root: ~/JustPlainSimple Technologies Inc./financials/ledgers
windows:
  - dir: ''
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Command = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().find(|x| match x {
        &Command::SendKeys(_) => true,
        _ => false
    }).unwrap();

    let root = match remains {
        Command::SendKeys(ref k) => k,
        _ => panic!("nope")
    };

    assert_eq!(root.exec, "cd \"~/JustPlainSimple Technologies Inc./financials/ledgers\"")
}

#[test]
pub fn expect_1_select_window() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::SelectWindow(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn expect_1_select_pane() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string(), false).unwrap().into_iter().filter(|x| match x {
        &Command::SelectPane(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}
<|endoftext|>


<commit_before>use yaml_rust::Yaml;
use command::{Command, Session, SendKeys, Split, Layout, Window, Attach, KillWindow};
use rand::random;

#[cfg(test)] use yaml_rust::{YamlLoader};

pub fn main(yaml_string: &Vec<Yaml>, project_name: &String) -> Vec<Command> {
    let mut commands: Vec<Command> = vec!();
    let tmp_window_name = format!("muxed_first_window_{}", random::<u16>().to_string());

    commands.push(Command::Session(Session{name: project_name.clone(), tmp_window_name: tmp_window_name.clone()}));

    for doc in yaml_string {
        let root = match doc["root"].as_str() {
            Some(x) => Some(x.to_string()),
            None    => None
        };

        let windows = doc["windows"].as_vec().expect("No Windows have been defined.");
        for (i, window) in windows.iter().enumerate() {
            match window {
                &Yaml::Hash(ref h)  => {
                    for (k, v) in h {
                        if v.as_hash().is_some() {
                            commands.push(Command::Window(Window{session_name: format!("{}:{}", project_name.clone(), i+1), name: k.as_str().unwrap().to_string(), root: root.clone()}));
                            commands.append(&mut pane_matcher(project_name.clone(), v, root.clone(), k.as_str().unwrap().to_string()));
                        } else {
                            commands.push(Command::Window(Window{session_name: format!("{}:{}", project_name.clone(), i+1), name: k.as_str().unwrap().to_string(), root: root.clone()}));
                            commands.push(Command::SendKeys(SendKeys{target: format!("{}:{}", project_name, k.as_str().unwrap().to_string()).to_string(), exec: v.as_str().expect("Bad exec command").to_string()}));
                        }
                    }
                },
                &Yaml::String(ref s) => {
                    commands.push(Command::Window(Window{session_name: format!("{}:{}", project_name.clone(), i+1), name: s.clone(), root: root.clone()}))
                },
                &Yaml::Integer(ref s) => {
                    commands.push(Command::Window(Window{session_name: format!("{}:{}", project_name.clone(), i+1), name: s.to_string(), root: root.clone()}))
                },
                _ => panic!("Muxed config file formatting isn't recognized.")
            };
        };
    };

    commands.push(Command::KillWindow(KillWindow{name: tmp_window_name.clone()}));
    commands.push(Command::Attach(Attach{name: project_name.clone()}));
    commands
}

fn pane_matcher(session: String, panes: &Yaml, root: Option<String>, window: String) -> Vec<Command> {
    let mut commands = vec!();
    let panes2 = panes["panes"].as_vec().expect("Something is wrong with panes.");

    for (i, pane) in panes2.iter().enumerate() {
        if i < (panes2.len()-1) {
            commands.push(Command::Split(Split{target: format!("{}:{}.{}", session, window, i.to_string()).to_string(), root: root.clone()}));
        };
        commands.push(Command::SendKeys(SendKeys{target: format!("{}:{}.{}", session, window, i).to_string(), exec: pane.as_str().expect("Bad exec command").to_string()}));
    };

    if panes["layout"].as_str().is_some() {
        let layout = panes["layout"].as_str().expect("Bad layout").to_string();
        commands.push(Command::Layout(Layout{target: format!("{}:{}", session, window).to_string(), layout: layout}));
    };

    commands
}

#[test]
pub fn windows_defined_as_array_has_6_commands() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    assert_eq!(main(&yaml, &"muxed".to_string()).len(), 6)
}

#[test]
pub fn windows_defined_as_array_has_1_session() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string()).into_iter().filter(|x| match x {
        &Command::Session(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn windows_defined_as_array_has_3_windows() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string()).into_iter().filter(|x| match x {
        &Command::Window(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 3)
}

#[test]
pub fn windows_defined_as_array_has_1_attach() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string()).into_iter().filter(|x| match x {
        &Command::Attach(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn windows_with_integer_names() {
    let s = "---
windows: [1, 'vim', 3]
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    assert_eq!(main(&yaml, &"muxed".to_string()).len(), 6)
}

#[test]
pub fn windows_as_list() {
    let s = "---
windows:
  - cargo: ''
  - vim: ''
  - git: ''
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let commands = main(&yaml, &"muxed".to_string());
    assert_eq!(commands.len(), 9)
}

#[test]
pub fn panes_array_has_8_commands() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let commands = main(&yaml, &"muxed".to_string());
    assert_eq!(commands.len(), 8)
}

#[test]
pub fn panes_array_has_1_split() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string()).into_iter().filter(|x| match x {
        &Command::Split(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn panes_array_has_1_layout() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string()).into_iter().filter(|x| match x {
        &Command::Layout(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn panes_array_has_1_window() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string()).into_iter().filter(|x| match x {
        &Command::Window(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn panes_array_has_1_session() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string()).into_iter().filter(|x| match x {
        &Command::Session(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

//#[test]
//pub fn panes_command_execs_array() {
//    let s = "---
//windows:
//  - editor:
//      layout: 'main-vertical'
//      panes: ['vim', 'guard']
//";
//    let yaml = YamlLoader::load_from_str(s).unwrap();
//    let remains: Vec<Command> = main(&yaml, &"muxed".to_string()).into_iter().filter(|x| match x {
//      &Command::SendKeys(_) => true,
//      _ => false
//    }).collect();
//
//    assert_eq!(remains.len(), 2);
//    assert_eq!(remains[0].exec, "vim");
//    assert_eq!(remains[1].exec, "guard")
//}
<commit_msg>Add module and function docs to the parser<commit_after>//! The YAML parser. Here is where we convert the yaml in to commands to be
/// processed later.

use yaml_rust::Yaml;
use command::{Command, Session, SendKeys, Split, Layout, Window, Attach, KillWindow};
use rand::random;

#[cfg(test)] use yaml_rust::{YamlLoader};

/// Here was pass in the parsed yaml and project name. The purpose of this main
/// loop is to build the stack of commands that are run to setup a users tmux
/// session.
///
/// yaml_string: The parsed yaml from the config file.
/// project_name: The name of the project.
pub fn main(yaml_string: &Vec<Yaml>, project_name: &String) -> Vec<Command> {
    let mut commands: Vec<Command> = vec!();
    let tmp_window_name = format!("muxed_first_window_{}", random::<u16>().to_string());

    // The initial session command. Contains the tmp_window_name to be closed
    // before attaching.
    commands.push(Command::Session(Session{name: project_name.clone(), tmp_window_name: tmp_window_name.clone()}));

    // There should only be one doc but it's a vec so loop it.
    for doc in yaml_string {
        let root = match doc["root"].as_str() {
            Some(x) => Some(x.to_string()),
            None    => None
        };

        let windows = doc["windows"].as_vec().expect("No Windows have been defined.");
        for (i, window) in windows.iter().enumerate() {
            match window {
                &Yaml::Hash(ref h)  => {
                    for (k, v) in h {
                        if v.as_hash().is_some() {
                            commands.push(Command::Window(Window{session_name: format!("{}:{}", project_name.clone(), i+1), name: k.as_str().unwrap().to_string(), root: root.clone()}));
                            commands.append(&mut pane_matcher(project_name.clone(), v, root.clone(), k.as_str().unwrap().to_string()));
                        } else {
                            commands.push(Command::Window(Window{session_name: format!("{}:{}", project_name.clone(), i+1), name: k.as_str().unwrap().to_string(), root: root.clone()}));
                            commands.push(Command::SendKeys(SendKeys{target: format!("{}:{}", project_name, k.as_str().unwrap().to_string()).to_string(), exec: v.as_str().expect("Bad exec command").to_string()}));
                        }
                    }
                },
                &Yaml::String(ref s) => {
                    commands.push(Command::Window(Window{session_name: format!("{}:{}", project_name.clone(), i+1), name: s.clone(), root: root.clone()}))
                },
                &Yaml::Integer(ref s) => {
                    commands.push(Command::Window(Window{session_name: format!("{}:{}", project_name.clone(), i+1), name: s.to_string(), root: root.clone()}))
                },
                _ => panic!("Muxed config file formatting isn't recognized.")
            };
        };
    };

    commands.push(Command::KillWindow(KillWindow{name: tmp_window_name.clone()}));
    commands.push(Command::Attach(Attach{name: project_name.clone()}));
    commands
}

/// Pane matcher is for breaking apart the panes. Splitting windows when needed
/// and executing commands as needed.
fn pane_matcher(session: String, panes: &Yaml, root: Option<String>, window: String) -> Vec<Command> {
    let mut commands = vec!();
    let panes2 = panes["panes"].as_vec().expect("Something is wrong with panes.");

    for (i, pane) in panes2.iter().enumerate() {
        // For every pane, we need one less split.
        // ex. An existing window to become 2 panes, needs 1 split.
        if i < (panes2.len()-1) {
            commands.push(Command::Split(Split{target: format!("{}:{}.{}", session, window, i.to_string()).to_string(), root: root.clone()}));
        };
        // Execute given commands in each new pane after all splits are
        // complete.
        commands.push(Command::SendKeys(SendKeys{target: format!("{}:{}.{}", session, window, i).to_string(), exec: pane.as_str().expect("Bad exec command").to_string()}));
    };

    // After all panes are split select the layout for the window
    if panes["layout"].as_str().is_some() {
        let layout = panes["layout"].as_str().expect("Bad layout").to_string();
        commands.push(Command::Layout(Layout{target: format!("{}:{}", session, window).to_string(), layout: layout}));
    };

    commands
}

#[test]
pub fn windows_defined_as_array_has_6_commands() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    assert_eq!(main(&yaml, &"muxed".to_string()).len(), 6)
}

#[test]
pub fn windows_defined_as_array_has_1_session() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string()).into_iter().filter(|x| match x {
        &Command::Session(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn windows_defined_as_array_has_3_windows() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string()).into_iter().filter(|x| match x {
        &Command::Window(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 3)
}

#[test]
pub fn windows_defined_as_array_has_1_attach() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string()).into_iter().filter(|x| match x {
        &Command::Attach(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn windows_with_integer_names() {
    let s = "---
windows: [1, 'vim', 3]
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    assert_eq!(main(&yaml, &"muxed".to_string()).len(), 6)
}

#[test]
pub fn windows_as_list() {
    let s = "---
windows:
  - cargo: ''
  - vim: ''
  - git: ''
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let commands = main(&yaml, &"muxed".to_string());
    assert_eq!(commands.len(), 9)
}

#[test]
pub fn panes_array_has_8_commands() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let commands = main(&yaml, &"muxed".to_string());
    assert_eq!(commands.len(), 8)
}

#[test]
pub fn panes_array_has_1_split() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string()).into_iter().filter(|x| match x {
        &Command::Split(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn panes_array_has_1_layout() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string()).into_iter().filter(|x| match x {
        &Command::Layout(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn panes_array_has_1_window() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string()).into_iter().filter(|x| match x {
        &Command::Window(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn panes_array_has_1_session() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, &"muxed".to_string()).into_iter().filter(|x| match x {
        &Command::Session(_) => true,
        _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

//#[test]
//pub fn panes_command_execs_array() {
//    let s = "---
//windows:
//  - editor:
//      layout: 'main-vertical'
//      panes: ['vim', 'guard']
//";
//    let yaml = YamlLoader::load_from_str(s).unwrap();
//    let remains: Vec<Command> = main(&yaml, &"muxed".to_string()).into_iter().filter(|x| match x {
//      &Command::SendKeys(_) => true,
//      _ => false
//    }).collect();
//
//    assert_eq!(remains.len(), 2);
//    assert_eq!(remains[0].exec, "vim");
//    assert_eq!(remains[1].exec, "guard")
//}
<|endoftext|>


<commit_before>use anyhow::Result;

use cnx::text::*;
use cnx::widgets::*;
use cnx::{Cnx, Position};

fn main() -> Result<()> {
    let attr = Attributes {
        font: Font::new("Ubuntu Mono Bold 14"),
        fg_color: Color::white(),
        bg_color: None,
        padding: Padding::new(8.0, 8.0, 0.0, 0.0),
    };
    let mut active_attr = attr.clone();
    active_attr.bg_color = Some(Color::blue());

    let mut cnx = Cnx::new(Position::Bottom);

    let sensors = Sensors::new(attr.clone(), vec!["Core 0", "Core 1"]);
    let battery = Battery::new(attr.clone(), Color::red());
    let cpu = cpu::Cpu::new(attr.clone())?;
    let mut volume_attr = attr.clone();
    // volume_attr.font = Font::new("FontAwesome-9");
    let volume = volume::Volume::new(volume_attr.clone());

    let default_threshold = Threshold::default();

    let wireless = wireless::Wireless::new(
        attr.clone(),
        "wlp0s20f3".to_owned(),
        Some(default_threshold),
    );
    cnx.add_widget(Pager::new(active_attr, attr.clone()));
    cnx.add_widget(ActiveWindowTitle::new(attr.clone()));
    cnx.add_widget(cpu);
    cnx.add_widget(wireless);
    cnx.add_widget(volume);
    // cnx.add_widget(sensors);
    // cnx.add_widget(battery);
    cnx.add_widget(Clock::new(attr.clone()));
    cnx.run()?;

    Ok(())
}
<commit_msg>Have zero pading<commit_after>use anyhow::Result;

use cnx::text::*;
use cnx::widgets::*;
use cnx::{Cnx, Position};

fn main() -> Result<()> {
    let attr = Attributes {
        font: Font::new("Ubuntu Mono Bold 14"),
        fg_color: Color::white(),
        bg_color: None,
        padding: Padding::new(0.0, 0.0, 0.0, 0.0),
    };
    let mut active_attr = attr.clone();
    active_attr.bg_color = Some(Color::blue());

    let mut cnx = Cnx::new(Position::Bottom);

    let sensors = Sensors::new(attr.clone(), vec!["Core 0", "Core 1"]);
    let battery = Battery::new(attr.clone(), Color::red());
    let cpu = cpu::Cpu::new(attr.clone())?;
    let mut volume_attr = attr.clone();
    // volume_attr.font = Font::new("FontAwesome-9");
    let volume = volume::Volume::new(volume_attr.clone());

    let default_threshold = Threshold::default();

    let wireless = wireless::Wireless::new(
        attr.clone(),
        "wlp0s20f3".to_owned(),
        Some(default_threshold),
    );
    cnx.add_widget(Pager::new(active_attr, attr.clone()));
    cnx.add_widget(ActiveWindowTitle::new(attr.clone()));
    cnx.add_widget(cpu);
    cnx.add_widget(wireless);
    cnx.add_widget(volume);
    // cnx.add_widget(sensors);
    // cnx.add_widget(battery);
    cnx.add_widget(Clock::new(attr.clone()));
    cnx.run()?;

    Ok(())
}
<|endoftext|>


<commit_before>use hb;
use std;

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

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

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

        BufferRaw { hb_buffer: buffer }
    }

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

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

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

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

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

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

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

    fn get_language(&self) -> Option<Language> {
        let raw_lang = unsafe { hb::hb_buffer_get_language(self.hb_buffer) };
        if raw_lang.is_null() {
            None
        } else {
            Some(Language(raw_lang))
        }
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    fn get_string(&self) -> String {
        self.0
            .get_glyph_infos()
            .iter()
            .map(|info| std::char::from_u32(info.codepoint).unwrap())
            .collect()
    }

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

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

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

    pub fn get_script(&self) -> Tag {
        Tag(unsafe { hb::hb_script_to_iso15924_tag(self.0.get_script()) })
    }

    pub fn set_language(mut self, lang: Language) -> UnicodeBuffer {
        self.0.set_language(lang);
        self
    }

    pub fn get_language(&self) -> Option<Language> {
        self.0.get_language()
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

#[derive(Debug, Clone)]
pub enum Buffer {
    UnicodeBuffer(UnicodeBuffer),
    GlyphBuffer(GlyphBuffer),
}
<commit_msg>Improve the interface for hb_buffer_t<commit_after>use hb;
use std;

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

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

#[derive(Debug)]
pub(crate) struct GenericBuffer {
    _raw: hb::hb_buffer_t,
}
impl GenericBuffer {
    pub(crate) fn new() -> HbBox<GenericBuffer> {
        let buffer = unsafe { hb::hb_buffer_create() };
        unsafe { HbBox::from_raw(buffer) }
    }

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

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

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

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

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

    pub(crate) fn set_language(&mut self, lang: Language) {
        unsafe { hb::hb_buffer_set_language(self.as_raw(), lang.0) }
    }

    pub(crate) fn get_language(&self) -> Option<Language> {
        let raw_lang = unsafe { hb::hb_buffer_get_language(self.as_raw()) };
        if raw_lang.is_null() {
            None
        } else {
            Some(Language(raw_lang))
        }
    }

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

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

    pub(crate) fn guess_segment_properties(&mut self) {
        unsafe { hb::hb_buffer_guess_segment_properties(self.as_raw()) };
    }

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

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

    pub(crate) fn clear_contents(&mut self) {
        unsafe { hb::hb_buffer_clear_contents(self.as_raw()) };
    }

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

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

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

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

    pub(crate) fn content_type(&self) -> hb::hb_buffer_content_type_t {
        unsafe { hb::hb_buffer_get_content_type(self.as_raw()) }
    }
}

impl HarfbuzzObject for GenericBuffer {
    type Raw = hb::hb_buffer_t;

    unsafe fn reference(&self) {
        hb::hb_buffer_reference(self.as_raw());
    }

    unsafe fn dereference(&self) {
        hb::hb_buffer_destroy(self.as_raw());
    }
}

/// This type provides an interface to create one of the buffer types from a raw harfbuzz pointer.
#[derive(Debug)]
pub enum TypedBuffer {
    Unicode(UnicodeBuffer),
    Glyphs(GlyphBuffer),
}

impl TypedBuffer {
    pub unsafe fn take_from_raw(raw: *mut hb::hb_buffer_t) -> Option<TypedBuffer> {
        let generic_buf: HbBox<GenericBuffer> = HbBox::from_raw(raw);
        let content_type = generic_buf.content_type();
        match content_type {
            hb::HB_BUFFER_CONTENT_TYPE_UNICODE => Some(TypedBuffer::Unicode(UnicodeBuffer(generic_buf))),
            hb::HB_BUFFER_CONTENT_TYPE_GLYPHS => Some(TypedBuffer::Glyphs(GlyphBuffer(generic_buf))),
            _ => None,
        }
    }
}

/// A `UnicodeBuffer` can be filled with unicode text and corresponding cluster indices.
///
/// The buffer manages an allocation for the unicode codepoints to be shaped. This allocation however
/// is reused for storing the results of the shaping operation in a `GlyphBuffer` object.
///
/// If you want to get a `UnicodeBuffer` from a pointer to a raw harfbuzz object, you need to use the
/// `from_raw` static method on `TypedBuffer`. This ensures that a buffer of correct type is created.
pub struct UnicodeBuffer(HbBox<GenericBuffer>);
impl UnicodeBuffer {
    /// Creates a new empty `Buffer`.
    ///
    /// # Examples
    /// ```
    /// use harfbuzz_rs::UnicodeBuffer;
    ///
    /// let buffer = UnicodeBuffer::new();
    /// assert!(buffer.is_empty());
    /// ```
    pub fn new() -> UnicodeBuffer {
        UnicodeBuffer(GenericBuffer::new())
    }

    /// Converts this buffer to a raw harfbuzz object pointer.
    pub fn into_raw(self) -> *mut hb::hb_buffer_t {
        HbBox::into_raw(self.0)
    }

    /// Returns the length of the data of the buffer.
    ///
    /// This corresponds to the number of unicode codepoints contained in the buffer.
    ///
    /// # Examples
    /// ```
    /// use harfbuzz_rs::UnicodeBuffer;
    ///
    /// let str1 = "Hello ";
    /// let buffer = UnicodeBuffer::new().add_str(str1);
    /// assert_eq!(buffer.len(), str1.len());
    ///
    /// let str2 = "😍🙈";
    /// let buffer = buffer.add_str(str2);
    /// assert_eq!(buffer.len(), str1.len() + 2);;
    /// ```
    pub fn len(&self) -> usize {
        self.0.len()
    }

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

    /// Add the string slice `str_slice` to the `Buffer`'s array of codepoints.
    ///
    /// # Examples
    /// ```
    /// use harfbuzz_rs::UnicodeBuffer;
    ///
    /// let buffer = UnicodeBuffer::new().add_str("Hello");
    /// let buffer = buffer.add_str(" World");
    /// assert_eq!(buffer.string_lossy(), "Hello World");
    /// ```
    pub fn add_str(mut self, str_slice: &str) -> UnicodeBuffer {
        self.0.add_str(str_slice);
        self
    }

    /// Returns an Iterator over the stored unicode codepoints.
    ///
    /// # Examples
    /// ```
    /// use harfbuzz_rs::UnicodeBuffer;
    ///
    /// let buffer = UnicodeBuffer::new().add_str("ab");
    /// let mut iterator = buffer.codepoints();
    ///
    /// assert_eq!('a' as u32, iterator.next().unwrap());
    /// assert_eq!('b' as u32, iterator.next().unwrap());
    /// assert!(iterator.next().is_none());
    /// ```
    pub fn codepoints<'a>(&'a self) -> CodepointsIter<'a> {
        CodepointsIter {
            slice_iter: self.0.get_glyph_infos().iter(),
        }
    }

    /// Get the stored codepoints as a `String`.
    ///
    /// Invalid codepoints get replaced by the U+FFFD replacement character.
    pub fn string_lossy(&self) -> String {
        self.codepoints()
            .map(|cp| std::char::from_u32(cp).unwrap_or('\u{FFFD}'))
            .collect()
    }

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

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

    /// Set the script from an ISO15924 tag.
    pub fn set_script(mut self, script: Tag) -> UnicodeBuffer {
        self.0
            .set_script(unsafe { hb::hb_script_from_iso15924_tag(script.0) });
        self
    }

    /// Get the ISO15924 script tag.
    pub fn get_script(&self) -> Tag {
        Tag(unsafe { hb::hb_script_to_iso15924_tag(self.0.get_script()) })
    }

    /// Set the buffer language.
    pub fn set_language(mut self, lang: Language) -> UnicodeBuffer {
        self.0.set_language(lang);
        self
    }

    /// Get the buffer language.
    pub fn get_language(&self) -> Option<Language> {
        self.0.get_language()
    }

    /// Guess the segment properties (direction, language, script) for the current buffer.
    pub fn guess_segment_properties(mut self) -> UnicodeBuffer {
        self.0.guess_segment_properties();
        self
    }

    /// Get the segment properties (direction, language, script) of the current buffer.
    pub fn get_segment_properties(&self) -> hb::hb_segment_properties_t {
        self.0.get_segment_properties()
    }

    /// Shape the contents of the buffer using the provided font and activating all OpenType features
    /// given in `features`.
    ///
    /// This function consumes the `UnicodeBuffer` and returns a `GlyphBuffer` containing the
    /// resulting glyph indices and the corresponding positioning information.
    pub fn shape(mut self, font: &Font, features: &[Feature]) -> GlyphBuffer {
        self = self.guess_segment_properties();
        self.0.shape(font, features);
        GlyphBuffer(self.0)
    }

    /// Clear the contents of the buffer (i.e. the stored string of unicode characters).
    ///
    /// # Examples
    /// ```
    /// use harfbuzz_rs::UnicodeBuffer;
    ///
    /// let buffer = UnicodeBuffer::new();
    /// let buffer = buffer.add_str("Test!");
    /// assert_eq!(buffer.len(), 5);
    /// let buffer = buffer.clear_contents();
    /// assert!(buffer.is_empty());
    /// ```
    pub fn clear_contents(mut self) -> UnicodeBuffer {
        self.0.clear_contents();
        self
    }
}

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

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

/// An iterator over the codepoints stored in a `UnicodeBuffer`.
///
/// You get an iterator of this type from the `codepoints()` method on `UnicodeBuffer`.
#[derive(Debug)]
pub struct CodepointsIter<'a> {
    slice_iter: std::slice::Iter<'a, GlyphInfo>,
}

impl<'a> Iterator for CodepointsIter<'a> {
    type Item = u32;

    fn next(&mut self) -> Option<u32> {
        self.slice_iter.next().map(|info| info.codepoint)
    }
}

/// A `GlyphBuffer` contains the resulting output information of the shaping process.
///
/// An object of this type is obtained through the `shape` function of a `UnicodeBuffer`.
pub struct GlyphBuffer(HbBox<GenericBuffer>);

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

    /// Converts this buffer to a raw harfbuzz object pointer.
    pub fn into_raw(self) -> *mut hb::hb_buffer_t {
        HbBox::into_raw(self.0)
    }

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

    /// Get the glyph positions.
    pub fn get_glyph_positions(&self) -> &[GlyphPosition] {
        self.0.get_glyph_positions()
    }

    /// Get the glyph infos.
    pub fn get_glyph_infos(&self) -> &[GlyphInfo] {
        self.0.get_glyph_infos()
    }

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

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

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

impl std::fmt::Debug for GlyphBuffer {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        fmt.debug_struct("GlyphBuffer")
            .field("glyph_positions", &self.get_glyph_positions())
            .field("glyph_infos", &self.get_glyph_infos())
            .finish()
    }
}
<|endoftext|>


<commit_before>extern crate extra;

use std::env::args;
use std::io::{self, Write};
use std::process::exit;
use std::thread::sleep;
use std::time::Duration;

use extra::option::OptionalExt;

static MAN_PAGE: &str = /* @MANSTART{rem} */ r#"
NAME
    rem - set a count-down.

SYNOPSIS
    rem [-h | --help] [-m N | --minutes N] [-H N | --hours N] [-s N | --seconds N]
        [-M N | --milliseconds N] [-n | --len] [-b | --blink]

DESCRIPTION
    This utility lets you set a count-down with a progress bar. The options can be given in
    combination, adding together the durations given.

OPTIONS
    --help
        Print this manual page.

    -h
        Print short help page.

    -m N
    --minutes N
        Wait N minutes.

    -H N
    --hours N
        Wait N hours.

    -s N
    --seconds N
        Wait N seconds.

    -M N
    --milliseconds N
        Wait N milliseconds.

    -n N
    --len N
        Set the length of the progress bar to N.

    -b
    --blink
        Blink with a red banner when done.

AUTHOR
    This program was written by Ticki for Redox OS. Bugs, issues, or feature requests should be
    reported in the Gitlab repository, 'redox-os/extrautils'.

COPYRIGHT
    Copyright (c) 2016 Ticki

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software
    and associated documentation files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use, copy, modify, merge, publish,
    distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all copies or
    substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
    BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
    DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"#; /* @MANEND */

static SHORT_HELP: &str = r#"
    rem - set a count-down.

    Options (use --help for extended list):
    -m N          => Wait N minutes.
    -H N          => Wait N hours.
    -s N          => Wait N seconds.
    -M N          => Wait N milliseconds.
    -n N          => N character progress bar.
    -b            => Blink when done.
"#;

fn main() {
    let mut args = args().skip(1);
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut stderr = io::stderr();

    let mut ms = 0u64;
    let mut len = 20;
    let mut blink = false;

    // Loop over the arguments.
    loop {
        let arg = if let Some(x) = args.next() {
            x
        } else {
            break;
        };

        match arg.as_str() {
            "--help" => {
                print!("{}", MAN_PAGE);
                return;
            },
            "-h" => {
                print!("{}", SHORT_HELP);
                return;
            },
            "-n" | "--len" => len = args.next().fail("no number after -n.", &mut stderr).parse().try(&mut stderr),
            "-b" | "--blink" => blink = true,
            t => {
                // Find number input.
                let num: u64 = args.next().unwrap_or_else(|| {
                    eprintln!("error: incorrectly formatted number.  Please input a positive integer.");
                    exit(1);
                }).parse().try(&mut stderr);
                ms += num * match t {
                    "-m" | "--minutes" => 1000 * 60,
                    "-H" | "--hours" => 1000 * 60 * 60,
                    "-s" | "--seconds" => 1000,
                    "-M" | "--milliseconds" => 1,
                    _ => {
                        // Unknown argument.
                        eprintln!("Error: unknown argument, {}", t);
                        exit(1);
                    },

                };
            },
        }
    }

    // Default to help page.
    if ms == 0 {
        print!("{}", SHORT_HELP);
        return;
    }

    // Hide the cursor.
    print!("\x1b[?25l");
    // Draw the empty progress bar.
    for _ in 0..len + 1 {
        print!(" ");
    }
    print!("]");

    print!("\r[");

    // As time goes, update the progress bar.
    for _ in 0..len {
        print!("#");
        stdout.flush().try(&mut stderr);
        // Sleep.
        sleep(Duration::from_millis(ms / len));
    }

    if blink {
        // This will print a blinking red banner.
        for _ in 0..13 {
            // Set drawing mode to red background.
            // Clear the current line, rendering the background red.
            print!("\x1b[41m\x1b[2K");
            // Flush.
            stdout.flush().try(&mut stderr);
            // Sleep.
            sleep(Duration::from_millis(200));

            // Clear the drawing mode.
            // Clear the background.
            print!("\x1b[0m\x1b[2K");
            // Flush.
            stdout.flush().try(&mut stderr);
            sleep(Duration::from_millis(200));

            // Repeat...
        }
    }

    // Show the cursor again.
    println!("\x1b[?25h");
}
<commit_msg>Clippy cleanup, fix banner blink<commit_after>extern crate extra;

use std::env::args;
use std::io::{self, Write};
use std::process::exit;
use std::thread::sleep;
use std::time::Duration;

use extra::option::OptionalExt;

static MAN_PAGE: &str = /* @MANSTART{rem} */ r#"
NAME
    rem - set a count-down.

SYNOPSIS
    rem [-h | --help] [-m N | --minutes N] [-H N | --hours N] [-s N | --seconds N]
        [-M N | --milliseconds N] [-n | --len] [-b | --blink]

DESCRIPTION
    This utility lets you set a count-down with a progress bar. The options can be given in
    combination, adding together the durations given.

OPTIONS
    --help
        Print this manual page.

    -h
        Print short help page.

    -m N
    --minutes N
        Wait N minutes.

    -H N
    --hours N
        Wait N hours.

    -s N
    --seconds N
        Wait N seconds.

    -M N
    --milliseconds N
        Wait N milliseconds.

    -n N
    --len N
        Set the length of the progress bar to N.

    -b
    --blink
        Blink with a red banner when done.

AUTHOR
    This program was written by Ticki for Redox OS. Bugs, issues, or feature requests should be
    reported in the Gitlab repository, 'redox-os/extrautils'.

COPYRIGHT
    Copyright (c) 2016 Ticki

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software
    and associated documentation files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use, copy, modify, merge, publish,
    distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all copies or
    substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
    BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
    DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"#; /* @MANEND */

static SHORT_HELP: &str = r#"
    rem - set a count-down.

    Options (use --help for extended list):
    -m N          => Wait N minutes.
    -H N          => Wait N hours.
    -s N          => Wait N seconds.
    -M N          => Wait N milliseconds.
    -n N          => N character progress bar.
    -b            => Blink when done.
"#;

fn main() {
    let mut args = args().skip(1);
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut stderr = io::stderr();

    let mut ms = 0u64;
    let mut len = 20;
    let mut blink = false;

    // Loop over the arguments.
    while let Some(arg) = args.next() {

        match arg.as_str() {
            "--help" => {
                print!("{}", MAN_PAGE);
                return;
            },
            "-h" => {
                print!("{}", SHORT_HELP);
                return;
            },
            "-n" | "--len" => len = args.next().fail("no number after -n.", &mut stderr).parse().try(&mut stderr),
            "-b" | "--blink" => blink = true,
            t => {
                // Find number input.
                let num: u64 = args.next().unwrap_or_else(|| {
                    eprintln!("error: incorrectly formatted number.  Please input a positive integer.");
                    exit(1);
                }).parse().try(&mut stderr);
                ms += num * match t {
                    "-m" | "--minutes" => 1000 * 60,
                    "-H" | "--hours" => 1000 * 60 * 60,
                    "-s" | "--seconds" => 1000,
                    "-M" | "--milliseconds" => 1,
                    _ => {
                        eprintln!("Error: unknown argument, {}", t);
                        exit(1);
                    },

                };
            },
        }
    }

    // Default to help page.
    if ms == 0 {
        print!("{}", SHORT_HELP);
        return;
    }

    // Hide the cursor.
    print!("\x1b[?25l");
    // Draw the empty progress bar.
    for _ in 0..len + 1 {
        print!(" ");
    }
    print!("]\r[");

    // As time goes, update the progress bar.
    for _ in 0..len {
        print!("#");
        stdout.flush().try(&mut stderr);
        // Sleep.
        sleep(Duration::from_millis(ms / len));
    }

    if blink {
        // This will print a blinking red banner.
        for _ in 0..13 {
            print!("\x1b[41m\x1b[2K\r"); // Clear the current line, rendering the background red.
            for _ in 0..len + 2 {
                print!(" ");
            }
            stdout.flush().try(&mut stderr);
            sleep(Duration::from_millis(200));

            print!("\x1b[0m\x1b[2K\r"); // Clear the drawing mode and background.
            for _ in 0..len + 2 {
                print!(" ");
            }

            stdout.flush().try(&mut stderr);
            sleep(Duration::from_millis(200));
        }
    }

    // Show the cursor again.
    println!("\x1b[?25h");
}
<|endoftext|>


<commit_before>#![cfg_attr(test, allow(dead_code, unused_imports))]

extern crate arguments;
extern crate log;
extern crate mcpat;
extern crate term;

#[macro_use]
extern crate recorder;

mod dynamic;
mod logger;
mod statik;

use recorder::{Error, Result};

const USAGE: &'static str = "
Usage: recorder <command> [options]

Commands:
    dynamic        Record the power consumed by a system on a chip.
    static         Record the area occupied by a system on a chip.
";

fn main() {
    logger::setup();
    start().unwrap_or_else(|error| fail(error));
}

fn start() -> Result<()> {
    let arguments = ok!(arguments::parse(std::env::args()));
    if arguments.orphans.len() != 1 {
        help(USAGE);
    }
    match &arguments.orphans[0][..] {
        "dynamic" => dynamic::execute(&arguments),
        "static" => statik::execute(&arguments),
        _ => raise!("the command is unknown"),
    }
}

fn help(message: &str) -> ! {
    println!("{}", message.trim());
    std::process::exit(0);
}

#[allow(unused_must_use)]
fn fail(error: Error) -> ! {
    use std::io::{stderr, Write};
    if let Some(mut output) = term::stderr() {
        output.fg(term::color::RED);
        output.write_all(format!("Error: {}.\n", error).as_bytes());
    }
    std::process::exit(1);
}
<commit_msg>Update the usage message<commit_after>#![cfg_attr(test, allow(dead_code, unused_imports))]

extern crate arguments;
extern crate log;
extern crate mcpat;
extern crate term;

#[macro_use]
extern crate recorder;

mod dynamic;
mod logger;
mod statik;

use recorder::{Error, Result};

const USAGE: &'static str = "
Usage: recorder <command> [options]

Commands:
    dynamic        Record dynamic characteristics.
    static         Record static characteristics.
";

fn main() {
    logger::setup();
    start().unwrap_or_else(|error| fail(error));
}

fn start() -> Result<()> {
    let arguments = ok!(arguments::parse(std::env::args()));
    if arguments.orphans.len() != 1 {
        help(USAGE);
    }
    match &arguments.orphans[0][..] {
        "dynamic" => dynamic::execute(&arguments),
        "static" => statik::execute(&arguments),
        _ => raise!("the command is unknown"),
    }
}

fn help(message: &str) -> ! {
    println!("{}", message.trim());
    std::process::exit(0);
}

#[allow(unused_must_use)]
fn fail(error: Error) -> ! {
    use std::io::{stderr, Write};
    if let Some(mut output) = term::stderr() {
        output.fg(term::color::RED);
        output.write_all(format!("Error: {}.\n", error).as_bytes());
    }
    std::process::exit(1);
}
<|endoftext|>


<commit_before>use super::cipher;
use super::byte_matrix;
use std::ops::BitAnd;
use std::collections::BinaryHeap;
use std::cmp::Ordering;
use std::f32;

fn try_size(cipher: &[u8], size: i32) -> f32 {

    let b1 = &cipher[0..size as usize];
    let b2 = &cipher[size as usize..2 * size as usize];
    let b3 = &cipher[2 * size as usize..3 * size as usize];
    let b4 = &cipher[3 * size as usize..4 * size as usize];

    let d1 = hamming_dist(b1, b2) as f32;
    let d2 = hamming_dist(b1, b3) as f32;
    let d3 = hamming_dist(b1, b4) as f32;
    let d4 = hamming_dist(b2, b3) as f32;
    let d5 = hamming_dist(b2, b4) as f32;
    let d6 = hamming_dist(b3, b4) as f32;

    ((d1 + d2 + d3 + d4 + d5 + d6) / 6.0) / size as f32

}

#[derive(Copy, Clone, PartialEq, Debug)]
struct KeyScore {
    size: u32,
    score: f32,
}

impl Eq for KeyScore {}

impl Ord for KeyScore {
    fn cmp(&self, other: &KeyScore) -> Ordering {
        other.score.partial_cmp(&self.score).unwrap_or(Ordering::Equal)
    }
}

impl PartialOrd for KeyScore {
    fn partial_cmp(&self, other: &KeyScore) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

pub fn break_file(input_path: &str, output_path: &str) -> Result<(), cipher::Error> {
    let cipher = try!(cipher::CipherText::from_file(input_path));
    let plain = try!(decode_text(&cipher));
    try!(plain.to_file(output_path));
    Ok(())
}

pub fn decode_text(cipher: &cipher::CipherText) -> Result<cipher::PlainText, cipher::Error> {

    let mut best_score = 0;
    let mut candidate = None;
    let key_size_guesses = guess_key_size(cipher);
    for key_size in key_size_guesses.iter() {
        let plain = break_cipher(cipher, *key_size);
        let score = score(&plain.as_bytes());
        if score > best_score {
            best_score = score;
            candidate = Some(plain);
        }

    }
    match candidate {
        None => Err(cipher::Error::Failure("Couldn't decode text".to_string())),
        Some(plain) => Ok(plain),
    }
}

pub fn guess_key_size(cipher: &cipher::CipherText) -> Vec<u32> {
    let mut heap = BinaryHeap::new();
    let mut best = Vec::new();
    for i in 1..20 {
        let score = try_size(&cipher.as_bytes(), i);
        heap.push(KeyScore {
            size: i as u32,
            score: score,
        });
    }

    let mut count = 0;
    while let Some(v) = heap.pop() {
        if count > 10 {
            break;
        } else {
            best.push(v.size);
            count += 1;
        }
    }
    best
}

pub fn hamming_dist(bytes1: &[u8], bytes2: &[u8]) -> i32 {
    let mut count = 0;
    for (i, val1) in bytes1.iter().enumerate() {
        let diff_bytes = val1 ^ bytes2[i];
        count += count_set_bits(diff_bytes);
    }
    count
}

pub fn count_set_bits(mut byte: u8) -> i32 {
    let mut count = 0;
    while byte != 0 {
        byte = byte.bitand(byte - 1);
        count += 1;
    }
    count
}

pub fn break_cipher(cipher: &cipher::CipherText, key_size: u32) -> cipher::PlainText {
    let matrix = byte_matrix::ByteMatrix::transpose(&cipher.as_bytes(), key_size as usize);
    let matrix = matrix.transform(|vec: &Vec<u8>| {
        let cipher = cipher::CipherText::new(vec);
        let plain = decode_single_key(&cipher);
        plain.as_bytes()
    });
    let decoded = matrix.reassemble();
    let plain = cipher::PlainText::new(&decoded);
    plain
}

pub fn decode_single_key(cipher: &cipher::CipherText) -> cipher::PlainText {
    let mut plain = None;
    let mut best_score = 0;
    for key in 0...255_u8 {
        let candidate = cipher::decrypt_single_key(cipher, key).unwrap();
        let score = score(&candidate.as_bytes());
        if score > best_score {
            best_score = score;
            plain = Some(candidate);
        }
    }
    plain.unwrap()
}

// Measures the number of lowercase characters
fn score(input: &[u8]) -> u32 {
    input.iter().fold(0, |acc, b| {
        if *b >= 97 && *b <= 122 {
            acc + 1
        } else if *b >= 33 && *b <= 64 && acc > 0 {
            acc - 1
        } else {
            acc
        }
    })
}

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

    #[test]
    fn test_count_bits() {
        assert_eq!(3, count_set_bits(0b0101010));
        assert_eq!(0, count_set_bits(0b0000000));
        assert_eq!(7, count_set_bits(0b1111111));
        assert_eq!(1, count_set_bits(0b0010000));
    }

    #[test]
    fn test_break() {
        check_break_single("This should be a simple test", "b");
        check_break_single("This should be a simple test", "x");
        check_break_single("This should be a simple test", "1");
        check_break_single("En un lugar de la mancha", "9");

        check_break("This should be a simple test, since we know the size. Something wasn't long \
                     enough, so we should investigate a little bit more",
                    "pipi");

    }

    #[test]
    fn test_guess_key() {
        check_guess_key("En un lugar de la Mancha, de cuyo nombre no quiero acordarme, no ha \
                         mucho tiempo que vivía un hidalgo de los de lanza en astillero, adarga \
                         antigua, rocín flaco y galgo corredor. Una olla de algo más vaca que \
                         carnero, salpicón las más noches, duelos y quebrantos los sábados, \
                         lantejas los viernes, algún palomino de añadidura los domingos, \
                         consumían las tres partes de su hacienda. El resto della concluían sayo \
                         de velarte, calzas de velludo para las fiestas, con sus pantuflos de lo \
                         mesmo, y los días de entresemana se honraba con su vellorí de lo más \
                         fino. Tenía en su casa una ama que pasaba de los cuarenta, y una \
                         sobrina que no llegaba a los veinte, y un mozo de campo y plaza, que \
                         así ensillaba el rocín como tomaba la podadera. Frisaba la edad de \
                         nuestro hidalgo con los cincuenta años; era de complexión recia, seco \
                         de carnes, enjuto de rostro, gran madrugador y amigo de la caza. \
                         Quieren decir que tenía el sobrenombre de Quijada, o Quesada, que en \
                         esto hay alguna diferencia en los autores que deste caso escriben; \
                         aunque, por conjeturas verosímiles, se deja entender que se llamaba \
                         Quejana. Pero esto importa poco a nuestro cuento; basta que en la \
                         narración dél no se salga un punto de la verdad.",
                        "pipi");

    }

    fn check_guess_key(text: &str, key: &str) {
        let plain = cipher::PlainText::from_string(text);
        let cipher = cipher::encrypt(&plain, key);
        assert!(guess_key_size(&cipher).contains(&(key.len() as u32)));
    }

    fn check_break(text: &str, key: &str) {
        let plain = cipher::PlainText::from_string(text);
        let cipher = cipher::encrypt(&plain, key);
        let decoded = break_cipher(&cipher, key.len() as u32);
        assert_eq!(text, decoded.to_utf8().unwrap());
    }

    fn check_break_single(text: &str, key: &str) {
        let plain = cipher::PlainText::from_string(text);
        let cipher = cipher::encrypt(&plain, key);
        let decoded = decode_single_key(&cipher);
        assert_eq!(text, decoded.to_utf8().unwrap());
    }

    #[test]
    fn test_hamming_dist() {
        assert_eq!(37,
                   hamming_dist("this is a test".as_bytes(), "wokka wokka!!!".as_bytes()));
    }
}
<commit_msg>Small changes, naming and tests<commit_after>use super::cipher;
use super::byte_matrix;
use std::ops::BitAnd;
use std::collections::BinaryHeap;
use std::cmp::Ordering;
use std::f32;

fn calc_size_score(cipher: &[u8], size: i32) -> f32 {

    let b1 = &cipher[0..size as usize];
    let b2 = &cipher[size as usize..2 * size as usize];
    let b3 = &cipher[2 * size as usize..3 * size as usize];
    let b4 = &cipher[3 * size as usize..4 * size as usize];

    let d1 = hamming_dist(b1, b2) as f32;
    let d2 = hamming_dist(b1, b3) as f32;
    let d3 = hamming_dist(b1, b4) as f32;
    let d4 = hamming_dist(b2, b3) as f32;
    let d5 = hamming_dist(b2, b4) as f32;
    let d6 = hamming_dist(b3, b4) as f32;

    ((d1 + d2 + d3 + d4 + d5 + d6) / 6.0) / size as f32

}

#[derive(Copy, Clone, PartialEq, Debug)]
struct KeyScore {
    size: u32,
    score: f32,
}

impl Eq for KeyScore {}

impl Ord for KeyScore {
    fn cmp(&self, other: &KeyScore) -> Ordering {
        other.score.partial_cmp(&self.score).unwrap_or(Ordering::Equal)
    }
}

impl PartialOrd for KeyScore {
    fn partial_cmp(&self, other: &KeyScore) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

pub fn break_file(input_path: &str, output_path: &str) -> Result<(), cipher::Error> {
    let cipher = try!(cipher::CipherText::from_file(input_path));
    let plain = try!(decode_text(&cipher));
    try!(plain.to_file(output_path));
    Ok(())
}

pub fn decode_text(cipher: &cipher::CipherText) -> Result<cipher::PlainText, cipher::Error> {

    let mut best_score = 0;
    let mut candidate = None;
    let key_size_guesses = guess_key_size(cipher);
    for key_size in key_size_guesses.iter() {
        let plain = break_cipher(cipher, *key_size);
        let score = score(&plain.as_bytes());
        if score > best_score {
            best_score = score;
            candidate = Some(plain);
        }

    }
    match candidate {
        None => Err(cipher::Error::Failure("Couldn't decode text".to_string())),
        Some(plain) => Ok(plain),
    }
}

pub fn guess_key_size(cipher: &cipher::CipherText) -> Vec<u32> {
    let mut heap = BinaryHeap::new();
    let mut best = Vec::new();
    for i in 1..40 {
        let score = calc_size_score(&cipher.as_bytes(), i);
        heap.push(KeyScore {
            size: i as u32,
            score: score,
        });
    }

    let mut count = 0;
    while let Some(v) = heap.pop() {
        if count > 3 {
            break;
        } else {
            best.push(v.size);
            count += 1;
        }
    }
    best
}

pub fn hamming_dist(bytes1: &[u8], bytes2: &[u8]) -> i32 {
    let mut count = 0;
    for (i, val1) in bytes1.iter().enumerate() {
        let diff_bytes = val1 ^ bytes2[i];
        count += count_set_bits(diff_bytes);
    }
    count
}

pub fn count_set_bits(mut byte: u8) -> i32 {
    let mut count = 0;
    while byte != 0 {
        byte = byte.bitand(byte - 1);
        count += 1;
    }
    count
}

pub fn break_cipher(cipher: &cipher::CipherText, key_size: u32) -> cipher::PlainText {
    let matrix = byte_matrix::ByteMatrix::transpose(&cipher.as_bytes(), key_size as usize);
    let matrix = matrix.transform(|vec: &Vec<u8>| {
        let cipher = cipher::CipherText::new(vec);
        let plain = decode_single_key(&cipher);
        plain.as_bytes()
    });
    let decoded = matrix.reassemble();
    let plain = cipher::PlainText::new(&decoded);
    plain
}

pub fn decode_single_key(cipher: &cipher::CipherText) -> cipher::PlainText {
    let mut plain = None;
    let mut best_score = 0;
    for key in 0...255_u8 {
        let candidate = cipher::decrypt_single_key(cipher, key).unwrap();
        let score = score(&candidate.as_bytes());
        if score > best_score {
            best_score = score;
            plain = Some(candidate);
        }
    }
    plain.unwrap()
}

// Measures the number of lowercase characters
fn score(input: &[u8]) -> u32 {
    input.iter().fold(0, |acc, b| {
        if *b >= 97 && *b <= 122 {
            acc + 1
        } else if *b >= 33 && *b <= 64 && acc > 0 {
            acc - 1
        } else {
            acc
        }
    })
}

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

    #[test]
    fn test_count_bits() {
        assert_eq!(3, count_set_bits(0b0101010));
        assert_eq!(0, count_set_bits(0b0000000));
        assert_eq!(7, count_set_bits(0b1111111));
        assert_eq!(1, count_set_bits(0b0010000));
    }

    #[test]
    fn test_break() {
        check_break_single("This should be a simple test", "b");
        check_break_single("This should be a simple test", "x");
        check_break_single("This should be a simple test", "1");
        check_break_single("En un lugar de la mancha", "9");

        check_break("This should be a simple test with a not too long text",
                    "ace");

    }

    #[test]
    fn test_guess_key() {
        check_guess_key("En un lugar de la Mancha, de cuyo nombre no quiero acordarme, no ha \
                         mucho tiempo que vivía un hidalgo de los de lanza en astillero, adarga \
                         antigua, rocín flaco y galgo corredor. Una olla de algo más vaca que \
                         carnero, salpicón las más noches, duelos y quebrantos los sábados, \
                         lantejas los viernes, algún palomino de añadidura los domingos, \
                         consumían las tres partes de su hacienda. El resto della concluían sayo \
                         de velarte, calzas de velludo para las fiestas, con sus pantuflos de lo \
                         mesmo, y los días de entresemana se honraba con su vellorí de lo más \
                         fino. Tenía en su casa una ama que pasaba de los cuarenta, y una \
                         sobrina que no llegaba a los veinte, y un mozo de campo y plaza, que \
                         así ensillaba el rocín como tomaba la podadera. Frisaba la edad de \
                         nuestro hidalgo con los cincuenta años; era de complexión recia, seco \
                         de carnes, enjuto de rostro, gran madrugador y amigo de la caza. \
                         Quieren decir que tenía el sobrenombre de Quijada, o Quesada, que en \
                         esto hay alguna diferencia en los autores que deste caso escriben; \
                         aunque, por conjeturas verosímiles, se deja entender que se llamaba \
                         Quejana. Pero esto importa poco a nuestro cuento; basta que en la \
                         narración dél no se salga un punto de la verdad.",
                        "caracol");

    }

    fn check_guess_key(text: &str, key: &str) {
        let plain = cipher::PlainText::from_string(text);
        let cipher = cipher::encrypt(&plain, key);
        assert!(guess_key_size(&cipher).contains(&(key.len() as u32)));
    }

    fn check_break(text: &str, key: &str) {
        let plain = cipher::PlainText::from_string(text);
        let cipher = cipher::encrypt(&plain, key);
        let decoded = break_cipher(&cipher, key.len() as u32);
        assert_eq!(text, decoded.to_utf8().unwrap());
    }

    fn check_break_single(text: &str, key: &str) {
        let plain = cipher::PlainText::from_string(text);
        let cipher = cipher::encrypt(&plain, key);
        let decoded = decode_single_key(&cipher);
        assert_eq!(text, decoded.to_utf8().unwrap());
    }

    #[test]
    fn test_hamming_dist() {
        assert_eq!(37,
                   hamming_dist("this is a test".as_bytes(), "wokka wokka!!!".as_bytes()));
    }
}
<|endoftext|>