<commit_before>use yaml_rust::Yaml;
use command::{Command, Session, SendKeys, Split, Layout, Window, Window2, Attach, Panes};

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

pub fn main(yaml_string: &Vec<Yaml>, project_name: String) -> Vec<Command> {
    let mut commands: Vec<Command> = vec!();

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

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

        match &first_window[0] {
             &Yaml::Hash(ref h)  => {
                 for (k, v) in h {
                     if v.as_hash().is_some() {
                         commands.push(Command::Session(Session{name: project_name.clone(), window_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::Session(Session{name: project_name.clone(), window_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::Session(Session{name: project_name.clone(), window_name: s.clone(), root: root.clone()}))
             },
             &Yaml::Integer(ref s) => {
                 commands.push(Command::Session(Session{name: project_name.clone(), window_name: s.to_string(), root: root.clone()}))
             },
             _ => panic!("Muxed config file formatting isn't recognized.")
        };

        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{value: k.as_str().unwrap().to_string(), root: root.clone(), exec: "".to_string()}));
                            commands.append(&mut pane_matcher(project_name.clone(), v, root.clone(), k.as_str().unwrap().to_string()));
                        } else {
                            commands.push(Command::Window2(Window2{session_name: project_name.clone(), 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::Window2(Window2{session_name: project_name.clone(), name: s.clone(), root: root.clone()}))
                },
                &Yaml::Integer(ref s) => {
                    commands.push(Command::Window2(Window2{session_name: project_name.clone(), name: s.to_string(), root: root.clone()}))
                },
                _ => panic!("Muxed config file formatting isn't recognized.")
            };
        };
    };
    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().unwrap().to_string();
        commands.push(Command::Layout(Layout{target: format!("{}:{}", session, window).to_string(), layout: panes["layout"].as_str().expect("Bad layout").to_string()}));
    };

    commands
}

#[test]
pub fn windows_as_array() {
    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(), 3)
}

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

#[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(), 3)
}

#[test]
pub fn root_command() {
    let s = "---
root: '~/.muxed'
windows:
  - cargo: ''
  - vim: ''
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let commands = main(&yaml, "muxed".to_string());

    let first_window: Option<Window> = match commands[0].clone() {
        Command::Window(w) => Some(w),
        _                  => None
    };

    assert_eq!(first_window.unwrap().root.unwrap(), "~/.muxed".to_string())
}

#[test]
pub fn panes_array() {
    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(), 2)
}

#[test]
pub fn panes_command_exists() {
    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());

    let pane_command: Option<Panes> = match commands[1].clone() {
        Command::Panes(w) => Some(w),
        _                 => None
    };

    assert!(pane_command.is_some())
}

#[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 commands = main(&yaml, "muxed".to_string());

    let pane_command: Option<Panes> = match commands[1].clone() {
        Command::Panes(w) => Some(w),
        _                 => None
    };

    assert_eq!(pane_command.clone().unwrap().exec[0], "vim");
    assert_eq!(pane_command.clone().unwrap().exec[1], "guard")
}
<commit_msg>Better testing<commit_after>use yaml_rust::Yaml;
use command::{Command, Session, SendKeys, Split, Layout, Window, Window2, Attach, Panes};

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

pub fn main(yaml_string: &Vec<Yaml>, project_name: String) -> Vec<Command> {
    let mut commands: Vec<Command> = vec!();

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

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

        match &first_window[0] {
             &Yaml::Hash(ref h)  => {
                 for (k, v) in h {
                     if v.as_hash().is_some() {
                         commands.push(Command::Session(Session{name: project_name.clone(), window_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::Session(Session{name: project_name.clone(), window_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::Session(Session{name: project_name.clone(), window_name: s.clone(), root: root.clone()}))
             },
             &Yaml::Integer(ref s) => {
                 commands.push(Command::Session(Session{name: project_name.clone(), window_name: s.to_string(), root: root.clone()}))
             },
             _ => panic!("Muxed config file formatting isn't recognized.")
        };

        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{value: k.as_str().unwrap().to_string(), root: root.clone(), exec: "".to_string()}));
                            commands.append(&mut pane_matcher(project_name.clone(), v, root.clone(), k.as_str().unwrap().to_string()));
                        } else {
                            commands.push(Command::Window2(Window2{session_name: project_name.clone(), 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::Window2(Window2{session_name: project_name.clone(), name: s.clone(), root: root.clone()}))
                },
                &Yaml::Integer(ref s) => {
                    commands.push(Command::Window2(Window2{session_name: project_name.clone(), name: s.to_string(), root: root.clone()}))
                },
                _ => panic!("Muxed config file formatting isn't recognized.")
            };
        };
    };
    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().unwrap().to_string();
        commands.push(Command::Layout(Layout{target: format!("{}:{}", session, window).to_string(), layout: panes["layout"].as_str().expect("Bad layout").to_string()}));
    };

    commands
}

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

#[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_2_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::Window2(_) => true,
      _ => false
    }).collect();

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

#[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(), 4)
}

#[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(), 7)
}

#[test]
pub fn root_command() {
    let s = "---
root: '~/.muxed'
windows:
  - cargo: ''
  - vim: ''
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let commands = main(&yaml, "muxed".to_string());

    let first_window: Option<Session> = match commands[0].clone() {
        Command::Session(w) => Some(w),
        _                   => None
    };

    assert_eq!(first_window.unwrap().root.unwrap(), "~/.muxed".to_string())
}

#[test]
pub fn panes_array_has_7_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());
    println!("{:?}", commands);
    assert_eq!(commands.len(), 6)
}

#[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_no_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::Window2(_) => true,
      _ => false
    }).collect();

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

#[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>extern crate pancurses;

use pancurses::*;

fn main() {
    let window = initscr();

    start_color();
    use_default_colors();
    set_blink(true);

    cbreak();
    noecho();

    window.clear();
    window.refresh();

    window.keypad(true);

    init_pair(1, 15, COLOR_BLACK);
    init_pair(2, COLOR_BLACK, COLOR_YELLOW);

    set_title("NewTest: tests various pancurses features");

    mousemask(ALL_MOUSE_EVENTS, std::ptr::null_mut());

    window.attrset(COLOR_PAIR(1));

    let mut quit = false;
    let mut redraw = true;

    const COL1: i32 = 2;
    const COL2: i32 = COL1 + 30;
    const COL3: i32 = 72;

    let mut unicode_offset = 0x80;

    while !quit {
        let (y_max, x_max) = window.get_max_yx();
        let color_block_start = 54;
        let mut color_block_cols = (x_max - color_block_start) / 2;
        let color_block_lines = 19;

        if color_block_cols < 0 {
            color_block_cols = 0;
        }

        if redraw {
            window.mvaddstr(1, COL1, "'Normal' white-on-black");

            window.attron(A_DIM);
            window.mvaddstr(2, COL1, "Dimmed text");
            window.attroff(A_DIM);

            window.attron(A_BLINK);
            window.mvaddstr(6, 40, "Blinking");
            window.attron(A_BOLD);
            window.mvaddstr(8, 40, "BlinkBold");
            window.attron(A_ITALIC);
            window.mvaddstr(0, COL2, "BlinkBoldItalic");
            window.attrset(COLOR_PAIR(3));
            window.attron(A_UNDERLINE);

            window.mvaddstr(1, COL2, "Underlined");

            window.attrset(COLOR_PAIR(1));
            window.attron(A_UNDERLINE | A_ITALIC);
            window.mvaddstr(2, COL2, "UnderlinedItalic");
            window.attrset(COLOR_PAIR(2));
            window.attron(A_BLINK);
            window.mvaddstr(4, COL1, "Black-on-yellow blinking");

            window.attrset(COLOR_PAIR(1));
            window.mv(4, COL2);
            text_in_a_box("Text in a box", &window);

            window.attrset(COLOR_PAIR(6));
            window.attron(A_STRIKEOUT);
            window.mvaddstr(10, 40, "Strikeout");
            window.attrset(COLOR_PAIR(1));

            window.mv(11, 40);
            text_in_a_box("Next Ucode pg", &window);
            if unicode_offset != 0 {
                window.mv(12, 40);
                text_in_a_box("Prev Ucode pg", &window);
            }
            window.mvprintw(13, 40, &format!("U+{:04X} ", unicode_offset));

            for i in 0..128 { // Show extended characters
                window.mvaddstr(5 + i % 16, (i / 16) * 5, &format!("{:02X} ", i + unicode_offset));
                if i + unicode_offset > ' ' as i32 {
                   window.addch((i + unicode_offset) as chtype);
                } else {
                   window.addch(' ');
                }
                window.addch(' ');
            }
        }

        // These two lines are just here to allow running this until its finished
        window.getch();
        break;
    }

    endwin();
}

fn text_in_a_box(text: &str, window: &Window) {
    let len = text.len();

    window.attron(A_OVERLINE | A_UNDERLINE | A_LEFTLINE);
    if len == 1 {
        window.attron(A_RIGHTLINE);
    }

    window.addnstr(text, 1);
    if len > 1 {
        window.attroff(A_LEFTLINE);
        if len > 2 {
            window.addnstr(&text[1..], len - 2);
        }
        window.attron(A_RIGHTLINE);
        window.addnstr(&text[len - 1..], 1);
    }

    window.attroff(A_OVERLINE | A_UNDERLINE | A_LEFTLINE | A_RIGHTLINE);
}
<commit_msg>Display text in various alphabets<commit_after>extern crate pancurses;

use pancurses::*;

fn main() {
    let window = initscr();

    start_color();
    use_default_colors();
    set_blink(true);

    cbreak();
    noecho();

    window.clear();
    window.refresh();

    window.keypad(true);

    init_pair(1, 15, COLOR_BLACK);
    init_pair(2, COLOR_BLACK, COLOR_YELLOW);

    set_title("NewTest: tests various pancurses features");

    mousemask(ALL_MOUSE_EVENTS, std::ptr::null_mut());

    window.attrset(COLOR_PAIR(1));

    let mut quit = false;
    let mut redraw = true;

    const COL1: i32 = 2;
    const COL2: i32 = COL1 + 30;
    const COL3: i32 = 72;

    let mut unicode_offset = 0x80;

    while !quit {
        let (y_max, x_max) = window.get_max_yx();
        let color_block_start = 54;
        let mut color_block_cols = (x_max - color_block_start) / 2;
        let color_block_lines = 19;

        if color_block_cols < 0 {
            color_block_cols = 0;
        }

        if redraw {
            window.mvaddstr(1, COL1, "'Normal' white-on-black");

            window.attron(A_DIM);
            window.mvaddstr(2, COL1, "Dimmed text");
            window.attroff(A_DIM);

            window.attron(A_BLINK);
            window.mvaddstr(6, 40, "Blinking");
            window.attron(A_BOLD);
            window.mvaddstr(8, 40, "BlinkBold");
            window.attron(A_ITALIC);
            window.mvaddstr(0, COL2, "BlinkBoldItalic");
            window.attrset(COLOR_PAIR(3));
            window.attron(A_UNDERLINE);

            window.mvaddstr(1, COL2, "Underlined");

            window.attrset(COLOR_PAIR(1));
            window.attron(A_UNDERLINE | A_ITALIC);
            window.mvaddstr(2, COL2, "UnderlinedItalic");
            window.attrset(COLOR_PAIR(2));
            window.attron(A_BLINK);
            window.mvaddstr(4, COL1, "Black-on-yellow blinking");

            window.attrset(COLOR_PAIR(1));
            window.mv(4, COL2);
            text_in_a_box("Text in a box", &window);

            window.attrset(COLOR_PAIR(6));
            window.attron(A_STRIKEOUT);
            window.mvaddstr(10, 40, "Strikeout");
            window.attrset(COLOR_PAIR(1));

            window.mv(11, 40);
            text_in_a_box("Next Ucode pg", &window);
            if unicode_offset != 0 {
                window.mv(12, 40);
                text_in_a_box("Prev Ucode pg", &window);
            }
            window.mvprintw(13, 40, &format!("U+{:04X} ", unicode_offset));

            for i in 0..128 { // Show extended characters
                window.mvaddstr(5 + i % 16, (i / 16) * 5, &format!("{:02X} ", i + unicode_offset));
                if i + unicode_offset > ' ' as i32 {
                   window.addch((i + unicode_offset) as chtype);
                } else {
                   window.addch(' ');
                }
                window.addch(' ');
            }

            // Skipped the full RGB example, I don't think there's a suitable feature in ncurses

            redraw = false;
            window.attrset(COLOR_PAIR(1));

            for i in 0..6 {
                let line = 24 + i / 3;
                let col = 5 + 25 * (i % 3);

                let spanish = "Español";
                let russian = "Русский язык";
                let greek = "Ελληνικά";
                let georgian = "ქართული ენა";
                let fullwidth = " Ｆｕｌｌｗｉｄｔｈ";
                let combining_marks =  "Cmin 	r";

                let texts = [spanish, russian, greek, georgian, fullwidth, combining_marks];

                if line < y_max && col < x_max {
                    window.mvaddnstr(line, 5 + 25 * (i % 3), texts[i as usize], x_max - col);
                }
            }

            window.mvaddstr(  1, COL3, "Click on cursor descriptions to");
            window.mvaddstr(  2, COL3, "cycle through possible cursors");
            window.mvaddstr(  3, COL3, "Click on colors at left to change");
            window.mvaddstr(  4, COL3, "colors used for under/over/outlining");
            window.mvaddstr(  5, COL3, "Click 'Blink' at bottom to toggle");
            window.mvaddstr(  6, COL3, "'real' blinking vs. 'highlit' blink");

        }

        // These two lines are just here to allow running this until its finished
        window.getch();
        break;
    }

    endwin();
}

fn text_in_a_box(text: &str, window: &Window) {
    let len = text.len();

    window.attron(A_OVERLINE | A_UNDERLINE | A_LEFTLINE);
    if len == 1 {
        window.attron(A_RIGHTLINE);
    }

    window.addnstr(text, 1);
    if len > 1 {
        window.attroff(A_LEFTLINE);
        if len > 2 {
            window.addnstr(&text[1..], len - 2);
        }
        window.attron(A_RIGHTLINE);
        window.addnstr(&text[len - 1..], 1);
    }

    window.attroff(A_OVERLINE | A_UNDERLINE | A_LEFTLINE | A_RIGHTLINE);
}
<|endoftext|>


<commit_before>use convert::*;
use errors::*;

#[derive(Debug, PartialEq, Default, FromBytes, BlockLength)]
struct MessageHeader {
    block_length: u16,
    template_id: u16,
    schema_id: u16,
    version: u16,
}

#[derive(Debug, PartialEq, Default, FromBytes, BlockLength)]
struct ExecuteCommandRequest {
    partition_id: u16,
    key: u64,
    event_type: EventType,
    topic_name: String,
    command: Vec<u8>,
}

#[derive(Debug, PartialEq, EnumDefault, FromBytes, BlockLength)]
enum EventType {
    Task,
    Raft,
    Subscription,
    Subscriber,
    Deployment,
    Workflow,
    Incident,
    Unknown,
}

#[cfg(test)]
mod tests {

    use super::*;
    use transport::*;

    use std::io::Cursor;

    macro_rules! cursor {
        ($reader:ident, $file:expr) => (
            let data = include_bytes!($file).to_vec();
            let mut $reader = Cursor::new(data);
        )
    }

    #[test]
    fn test_block_length() {
        assert_eq!(MessageHeader::block_length(), 8);
        assert_eq!(ExecuteCommandRequest::block_length(), 11);
        assert_eq!(EventType::block_length(), 1);
    }

    #[test]
    fn test_decode_create_task_request() {
        cursor!(reader, "../../dumps/create-task-request");

        FrameHeader::from_bytes(&mut reader).unwrap();
        TransportHeader::from_bytes(&mut reader).unwrap();
        RequestResponseHeader::from_bytes(&mut reader).unwrap();

        let header = MessageHeader::from_bytes(&mut reader).unwrap();
        assert_eq!(header.block_length, 11);
        assert_eq!(header.template_id, 20);
        assert_eq!(header.schema_id, 0);
        assert_eq!(header.version, 1);

        let request = ExecuteCommandRequest::from_bytes(&mut reader).unwrap();
        assert_eq!(request.partition_id, 0);
        assert_eq!(request.key, ::std::u64::MAX);
        assert_eq!(request.event_type, EventType::Task);
        assert_eq!(request.topic_name, "default-topic");
        assert_eq!(request.command.len(), 75);
    }

}
<commit_msg>chore(protocol): add ExecuteCommandResponse, ControlMessage types<commit_after>use convert::*;
use errors::*;

#[derive(Debug, PartialEq, Default, FromBytes, BlockLength)]
struct MessageHeader {
    block_length: u16,
    template_id: u16,
    schema_id: u16,
    version: u16,
}

#[derive(Debug, PartialEq, Default, FromBytes, BlockLength)]
struct ExecuteCommandRequest {
    partition_id: u16,
    key: u64,
    event_type: EventType,
    topic_name: String,
    command: Vec<u8>,
}

#[derive(Debug, PartialEq, Default, FromBytes, BlockLength)]
struct ExecuteCommandResponse {
    partition_id: u32,
    key: u64,
    topic_name: String,
    event: Vec<u8>,
}

#[derive(Debug, PartialEq, Default, FromBytes, BlockLength)]
struct ControlMessageRequest {
    message_type: ControlMessageType,
    data: Vec<u8>,
}

#[derive(Debug, PartialEq, Default, FromBytes, BlockLength)]
struct ControlMessageResponse {
    data: Vec<u8>,
}

#[derive(Debug, PartialEq, EnumDefault, FromBytes, BlockLength)]
enum EventType {
    Task,
    Raft,
    Subscription,
    Subscriber,
    Deployment,
    Workflow,
    Incident,
    Unknown,
}

#[derive(Debug, PartialEq, EnumDefault, FromBytes, BlockLength)]
enum ControlMessageType {
    AddTaskSubscription,
    RemoveTaskSubscription,
    IncreaseTaskSubscriptionCredits,
    RemoveTopicSubscription,
    Unknown,
}

#[cfg(test)]
mod tests {

    use super::*;
    use transport::*;

    use std::io::Cursor;

    macro_rules! cursor {
        ($reader:ident, $file:expr) => (
            let data = include_bytes!($file).to_vec();
            let mut $reader = Cursor::new(data);

            FrameHeader::skip_block(&mut $reader).unwrap();
            TransportHeader::skip_block(&mut $reader).unwrap();
            RequestResponseHeader::skip_block(&mut $reader).unwrap();
        )
    }

    #[test]
    fn test_block_length() {
        assert_eq!(MessageHeader::block_length(), 8);
        assert_eq!(ExecuteCommandRequest::block_length(), 11);
        assert_eq!(EventType::block_length(), 1);
    }

    #[test]
    fn test_decode_create_task_request() {
        cursor!(reader, "../../dumps/create-task-request");

        let header = MessageHeader::from_bytes(&mut reader).unwrap();
        assert_eq!(header.block_length, 11);
        assert_eq!(header.template_id, 20);
        assert_eq!(header.schema_id, 0);
        assert_eq!(header.version, 1);

        let request = ExecuteCommandRequest::from_bytes(&mut reader).unwrap();
        assert_eq!(request.partition_id, 0);
        assert_eq!(request.key, ::std::u64::MAX);
        assert_eq!(request.event_type, EventType::Task);
        assert_eq!(request.topic_name, "default-topic");
        assert_eq!(request.command.len(), 75);
    }

    #[test]
    fn test_decode_create_task_response() {
        cursor!(reader, "../../dumps/create-task-response");

        let header = MessageHeader::from_bytes(&mut reader).unwrap();
        assert_eq!(header.block_length, 12);
        assert_eq!(header.template_id, 21);
        assert_eq!(header.schema_id, 0);
        assert_eq!(header.version, 1);

        let request = ExecuteCommandResponse::from_bytes(&mut reader).unwrap();
        assert_eq!(request.partition_id, 0);
        assert_eq!(request.key, 4294967544);
        assert_eq!(request.topic_name, "default-topic");
        assert_eq!(request.event.len(), 216);
    }

    #[test]
    fn test_decode_close_subscription_request() {
        cursor!(reader, "../../dumps/close-subscription-request");

        let header = MessageHeader::from_bytes(&mut reader).unwrap();
        assert_eq!(header.block_length, 1);
        assert_eq!(header.template_id, 10);
        assert_eq!(header.schema_id, 0);
        assert_eq!(header.version, 1);

        let request = ControlMessageRequest::from_bytes(&mut reader).unwrap();
        assert_eq!(request.message_type,
                   ControlMessageType::RemoveTopicSubscription);
        assert_eq!(request.data.len(), 61);
    }

    #[test]
    fn test_decode_close_subscription_response() {
        cursor!(reader, "../../dumps/close-subscription-response");

        let header = MessageHeader::from_bytes(&mut reader).unwrap();
        assert_eq!(header.block_length, 0);
        assert_eq!(header.template_id, 11);
        assert_eq!(header.schema_id, 0);
        assert_eq!(header.version, 1);

        let request = ControlMessageResponse::from_bytes(&mut reader).unwrap();
        assert_eq!(request.data.len(), 61);
    }

}
<|endoftext|>


<commit_before>//! Streams
//!
//! This module contains a number of functions for working with `Streams`s
//! that return `Result`s, allowing for short-circuiting computations.

use core::marker::Unpin;
use futures_core::future::TryFuture;
use futures_core::stream::TryStream;

mod err_into;
pub use self::err_into::ErrInto;

mod into_stream;
pub use self::into_stream::IntoStream;

mod map_ok;
pub use self::map_ok::MapOk;

mod map_err;
pub use self::map_err::MapErr;

mod try_next;
pub use self::try_next::TryNext;

mod try_for_each;
pub use self::try_for_each::TryForEach;

mod try_filter_map;
pub use self::try_filter_map::TryFilterMap;

mod try_buffer_unordered;
pub use self::try_buffer_unordered::TryBufferUnordered;

if_std! {
    mod try_collect;
    pub use self::try_collect::TryCollect;
}

impl<S: TryStream> TryStreamExt for S {}

/// Adapters specific to `Result`-returning streams
pub trait TryStreamExt: TryStream {
    /// Wraps the current stream in a new stream which converts the error type
    /// into the one provided.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(async_await, await_macro)]
    /// # futures::executor::block_on(async {
    /// use futures::stream::{self, TryStreamExt};
    ///
    /// let mut stream =
    ///     stream::iter(vec![Ok(()), Err(5i32)])
    ///         .err_into::<i64>();
    ///
    /// assert_eq!(await!(stream.try_next()), Ok(Some(())));
    /// assert_eq!(await!(stream.try_next()), Err(5i64));
    /// # })
    /// ```
    fn err_into<E>(self) -> ErrInto<Self, E>
    where
        Self: Sized,
        Self::Error: Into<E>
    {
        ErrInto::new(self)
    }

    /// Wraps the current stream in a new stream which maps the success value
    /// using the provided closure.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(async_await, await_macro)]
    /// # futures::executor::block_on(async {
    /// use futures::stream::{self, TryStreamExt};
    ///
    /// let mut stream =
    ///     stream::iter(vec![Ok(5), Err(0)])
    ///         .map_ok(|x| x + 2);
    ///
    /// assert_eq!(await!(stream.try_next()), Ok(Some(7)));
    /// assert_eq!(await!(stream.try_next()), Err(0));
    /// # })
    /// ```
    fn map_ok<T, F>(self, f: F) -> MapOk<Self, F>
    where
        Self: Sized,
        F: FnMut(Self::Ok) -> T,
    {
        MapOk::new(self, f)
    }

    /// Wraps the current stream in a new stream which maps the error value
    /// using the provided closure.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(async_await, await_macro)]
    /// # futures::executor::block_on(async {
    /// use futures::stream::{self, TryStreamExt};
    ///
    /// let mut stream =
    ///     stream::iter(vec![Ok(5), Err(0)])
    ///         .map_err(|x| x + 2);
    ///
    /// assert_eq!(await!(stream.try_next()), Ok(Some(5)));
    /// assert_eq!(await!(stream.try_next()), Err(2));
    /// # })
    /// ```
    fn map_err<E, F>(self, f: F) -> MapErr<Self, F>
    where
        Self: Sized,
        F: FnMut(Self::Error) -> E,
    {
        MapErr::new(self, f)
    }

    /// Wraps a [`TryStream`] into a type that implements
    /// [`Stream`](futures_core::Stream)
    ///
    /// [`TryStream`]s currently do not implement the
    /// [`Stream`](futures_core::Stream) trait because of limitations
    /// of the compiler.
    ///
    /// # Examples
    ///
    /// ```
    /// use futures::stream::{Stream, TryStream, TryStreamExt};
    ///
    /// # type T = i32;
    /// # type E = ();
    /// fn make_try_stream() -> impl TryStream<Ok = T, Error = E> { // ... }
    /// # futures::stream::empty()
    /// # }
    /// fn take_stream(stream: impl Stream<Item = Result<T, E>>) { /* ... */ }
    ///
    /// take_stream(make_try_stream().into_stream());
    /// ```
    fn into_stream(self) -> IntoStream<Self>
        where Self: Sized,
    {
        IntoStream::new(self)
    }

    /// Creates a future that attempts to resolve the next item in the stream.
    /// If an error is encountered before the next item, the error is returned
    /// instead.
    ///
    /// This is similar to the `Stream::next` combinator, but returns a
    /// `Result<Option<T>, E>` rather than an `Option<Result<T, E>>`, making
    /// for easy use with the `?` operator.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(async_await, await_macro)]
    /// # futures::executor::block_on(async {
    /// use futures::stream::{self, TryStreamExt};
    ///
    /// let mut stream = stream::iter(vec![Ok(()), Err(())]);
    ///
    /// assert_eq!(await!(stream.try_next()), Ok(Some(())));
    /// assert_eq!(await!(stream.try_next()), Err(()));
    /// # })
    /// ```
    fn try_next(&mut self) -> TryNext<'_, Self>
        where Self: Sized + Unpin,
    {
        TryNext::new(self)
    }

    /// Attempts to run this stream to completion, executing the provided
    /// asynchronous closure for each element on the stream.
    ///
    /// The provided closure will be called for each item this stream produces,
    /// yielding a future. That future will then be executed to completion
    /// before moving on to the next item.
    ///
    /// The returned value is a [`Future`](futures_core::Future) where the
    /// [`Output`](futures_core::Future::Output) type is
    /// `Result<(), Self::Error>`. If any of the intermediate
    /// futures or the stream returns an error, this future will return
    /// immediately with an error.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(async_await, await_macro)]
    /// # futures::executor::block_on(async {
    /// use futures::future;
    /// use futures::stream::{self, TryStreamExt};
    ///
    /// let mut x = 0i32;
    ///
    /// {
    ///     let fut = stream::repeat(Ok(1)).try_for_each(|item| {
    ///         x += item;
    ///         future::ready(if x == 3 { Err(()) } else { Ok(()) })
    ///     });
    ///     assert_eq!(await!(fut), Err(()));
    /// }
    ///
    /// assert_eq!(x, 3);
    /// # })
    /// ```
    fn try_for_each<Fut, F>(self, f: F) -> TryForEach<Self, Fut, F>
        where F: FnMut(Self::Ok) -> Fut,
              Fut: TryFuture<Ok = (), Error=Self::Error>,
              Self: Sized
    {
        TryForEach::new(self, f)
    }

    /// Attempt to Collect all of the values of this stream into a vector,
    /// returning a future representing the result of that computation.
    ///
    /// This combinator will collect all successful results of this stream and
    /// collect them into a `Vec<Self::Item>`. If an error happens then all
    /// collected elements will be dropped and the error will be returned.
    ///
    /// The returned future will be resolved when the stream terminates.
    ///
    /// This method is only available when the `std` feature of this
    /// library is activated, and it is activated by default.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(async_await, await_macro)]
    /// # futures::executor::block_on(async {
    /// use futures::channel::mpsc;
    /// use futures::executor::block_on;
    /// use futures::stream::TryStreamExt;
    /// use std::thread;
    ///
    /// let (mut tx, rx) = mpsc::unbounded();
    ///
    /// thread::spawn(move || {
    ///     for i in (1..=5) {
    ///         tx.unbounded_send(Ok(i)).unwrap();
    ///     }
    ///     tx.unbounded_send(Err(6)).unwrap();
    /// });
    ///
    /// let output: Result<Vec<i32>, i32> = await!(rx.try_collect());
    /// assert_eq!(output, Err(6));
    /// # })
    /// ```
    #[cfg(feature = "std")]
    fn try_collect<C: Default + Extend<Self::Ok>>(self) -> TryCollect<Self, C>
        where Self: Sized
    {
        TryCollect::new(self)
    }

    /// Attempt to filter the values produced by this stream while
    /// simultaneously mapping them to a different type according to the
    /// provided asynchronous closure.
    ///
    /// As values of this stream are made available, the provided function will
    /// be run on them. If the future returned by the predicate `f` resolves to
    /// [`Some(item)`](Some) then the stream will yield the value `item`, but if
    /// it resolves to [`None`] then the next value will be produced.
    ///
    /// All errors are passed through without filtering in this combinator.
    ///
    /// Note that this function consumes the stream passed into it and returns a
    /// wrapped version of it, similar to the existing `filter_map` methods in
    /// the standard library.
    ///
    /// # Examples
    /// ```
    /// #![feature(async_await, await_macro)]
    /// # futures::executor::block_on(async {
    /// use futures::executor::block_on;
    /// use futures::prelude::*;
    ///
    /// let stream = stream::iter(vec![Ok(1i32), Ok(6i32), Err("error")]);
    /// let mut halves = stream.try_filter_map(|x| {
    ///     let ret = if x % 2 == 0 { Some(x / 2) } else { None };
    ///     future::ready(Ok(ret))
    /// });
    ///
    /// assert_eq!(await!(halves.next()), Some(Ok(3)));
    /// assert_eq!(await!(halves.next()), Some(Err("error")));
    /// # })
    /// ```
    fn try_filter_map<Fut, F, T>(self, f: F) -> TryFilterMap<Self, Fut, F>
        where Fut: TryFuture<Ok = Option<T>, Error = Self::Error>,
              F: FnMut(Self::Ok) -> Fut,
              Self: Sized
    {
        TryFilterMap::new(self, f)
    }

    /// Attempt to execute several futures from a stream concurrently.
    ///
    /// This stream's `Ok` type must be a [`TryFuture`] with an `Error` type
    /// that matches the stream's `Error` type.
    ///
    /// This adaptor will buffer up to `n` futures and then return their
    /// outputs in the order in which they complete. If the underlying stream
    /// returns an error, it will be immediately propagated.
    ///
    /// The returned stream will be a stream of results, each containing either
    /// an error or a future's output. An error can be produced either by the
    /// underlying stream itself or by one of the futures it yielded.
    ///
    /// # Examples
    ///
    /// Results are returned in the order of completion:
    /// ```
    /// #![feature(async_await, await_macro)]
    /// # futures::executor::block_on(async {
    /// use futures::channel::oneshot;
    /// use futures::stream::{self, StreamExt, TryStreamExt};
    ///
    /// let (send_one, recv_one) = oneshot::channel();
    /// let (send_two, recv_two) = oneshot::channel();
    ///
    /// let stream_of_futures = stream::iter(vec![Ok(recv_one), Ok(recv_two)]);
    ///
    /// let mut buffered = stream_of_futures.try_buffer_unordered(10);
    ///
    /// send_two.send(2i32);
    /// assert_eq!(await!(buffered.next()), Some(Ok(2i32)));
    ///
    /// send_one.send(1i32);
    /// assert_eq!(await!(buffered.next()), Some(Ok(1i32)));
    ///
    /// assert_eq!(await!(buffered.next()), None);
    /// # })
    /// ```
    ///
    /// Errors from the underlying stream itself are propagated:
    /// ```
    /// #![feature(async_await, await_macro)]
    /// # futures::executor::block_on(async {
    /// use futures::channel::mpsc;
    /// use futures::future;
    /// use futures::stream::{StreamExt, TryStreamExt};
    ///
    /// let (sink, stream_of_futures) = mpsc::unbounded();
    /// let mut buffered = stream_of_futures.try_buffer_unordered(10);
    ///
    /// sink.unbounded_send(Ok(future::ready(Ok(7i32))));
    /// assert_eq!(await!(buffered.next()), Some(Ok(7i32)));
    ///
    /// sink.unbounded_send(Err("error in the stream"));
    /// assert_eq!(await!(buffered.next()), Some(Err("error in the stream")));
    /// # })
    /// ```
    fn try_buffer_unordered(self, n: usize) -> TryBufferUnordered<Self>
        where Self::Ok: TryFuture<Error = Self::Error>,
              Self: Sized
    {
        TryBufferUnordered::new(self, n)
    }
}
<commit_msg>Enable try_buffer_unordered only if std is available<commit_after>//! Streams
//!
//! This module contains a number of functions for working with `Streams`s
//! that return `Result`s, allowing for short-circuiting computations.

use core::marker::Unpin;
use futures_core::future::TryFuture;
use futures_core::stream::TryStream;

mod err_into;
pub use self::err_into::ErrInto;

mod into_stream;
pub use self::into_stream::IntoStream;

mod map_ok;
pub use self::map_ok::MapOk;

mod map_err;
pub use self::map_err::MapErr;

mod try_next;
pub use self::try_next::TryNext;

mod try_for_each;
pub use self::try_for_each::TryForEach;

mod try_filter_map;
pub use self::try_filter_map::TryFilterMap;

if_std! {
    mod try_buffer_unordered;
    pub use self::try_buffer_unordered::TryBufferUnordered;

    mod try_collect;
    pub use self::try_collect::TryCollect;
}

impl<S: TryStream> TryStreamExt for S {}

/// Adapters specific to `Result`-returning streams
pub trait TryStreamExt: TryStream {
    /// Wraps the current stream in a new stream which converts the error type
    /// into the one provided.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(async_await, await_macro)]
    /// # futures::executor::block_on(async {
    /// use futures::stream::{self, TryStreamExt};
    ///
    /// let mut stream =
    ///     stream::iter(vec![Ok(()), Err(5i32)])
    ///         .err_into::<i64>();
    ///
    /// assert_eq!(await!(stream.try_next()), Ok(Some(())));
    /// assert_eq!(await!(stream.try_next()), Err(5i64));
    /// # })
    /// ```
    fn err_into<E>(self) -> ErrInto<Self, E>
    where
        Self: Sized,
        Self::Error: Into<E>
    {
        ErrInto::new(self)
    }

    /// Wraps the current stream in a new stream which maps the success value
    /// using the provided closure.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(async_await, await_macro)]
    /// # futures::executor::block_on(async {
    /// use futures::stream::{self, TryStreamExt};
    ///
    /// let mut stream =
    ///     stream::iter(vec![Ok(5), Err(0)])
    ///         .map_ok(|x| x + 2);
    ///
    /// assert_eq!(await!(stream.try_next()), Ok(Some(7)));
    /// assert_eq!(await!(stream.try_next()), Err(0));
    /// # })
    /// ```
    fn map_ok<T, F>(self, f: F) -> MapOk<Self, F>
    where
        Self: Sized,
        F: FnMut(Self::Ok) -> T,
    {
        MapOk::new(self, f)
    }

    /// Wraps the current stream in a new stream which maps the error value
    /// using the provided closure.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(async_await, await_macro)]
    /// # futures::executor::block_on(async {
    /// use futures::stream::{self, TryStreamExt};
    ///
    /// let mut stream =
    ///     stream::iter(vec![Ok(5), Err(0)])
    ///         .map_err(|x| x + 2);
    ///
    /// assert_eq!(await!(stream.try_next()), Ok(Some(5)));
    /// assert_eq!(await!(stream.try_next()), Err(2));
    /// # })
    /// ```
    fn map_err<E, F>(self, f: F) -> MapErr<Self, F>
    where
        Self: Sized,
        F: FnMut(Self::Error) -> E,
    {
        MapErr::new(self, f)
    }

    /// Wraps a [`TryStream`] into a type that implements
    /// [`Stream`](futures_core::Stream)
    ///
    /// [`TryStream`]s currently do not implement the
    /// [`Stream`](futures_core::Stream) trait because of limitations
    /// of the compiler.
    ///
    /// # Examples
    ///
    /// ```
    /// use futures::stream::{Stream, TryStream, TryStreamExt};
    ///
    /// # type T = i32;
    /// # type E = ();
    /// fn make_try_stream() -> impl TryStream<Ok = T, Error = E> { // ... }
    /// # futures::stream::empty()
    /// # }
    /// fn take_stream(stream: impl Stream<Item = Result<T, E>>) { /* ... */ }
    ///
    /// take_stream(make_try_stream().into_stream());
    /// ```
    fn into_stream(self) -> IntoStream<Self>
        where Self: Sized,
    {
        IntoStream::new(self)
    }

    /// Creates a future that attempts to resolve the next item in the stream.
    /// If an error is encountered before the next item, the error is returned
    /// instead.
    ///
    /// This is similar to the `Stream::next` combinator, but returns a
    /// `Result<Option<T>, E>` rather than an `Option<Result<T, E>>`, making
    /// for easy use with the `?` operator.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(async_await, await_macro)]
    /// # futures::executor::block_on(async {
    /// use futures::stream::{self, TryStreamExt};
    ///
    /// let mut stream = stream::iter(vec![Ok(()), Err(())]);
    ///
    /// assert_eq!(await!(stream.try_next()), Ok(Some(())));
    /// assert_eq!(await!(stream.try_next()), Err(()));
    /// # })
    /// ```
    fn try_next(&mut self) -> TryNext<'_, Self>
        where Self: Sized + Unpin,
    {
        TryNext::new(self)
    }

    /// Attempts to run this stream to completion, executing the provided
    /// asynchronous closure for each element on the stream.
    ///
    /// The provided closure will be called for each item this stream produces,
    /// yielding a future. That future will then be executed to completion
    /// before moving on to the next item.
    ///
    /// The returned value is a [`Future`](futures_core::Future) where the
    /// [`Output`](futures_core::Future::Output) type is
    /// `Result<(), Self::Error>`. If any of the intermediate
    /// futures or the stream returns an error, this future will return
    /// immediately with an error.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(async_await, await_macro)]
    /// # futures::executor::block_on(async {
    /// use futures::future;
    /// use futures::stream::{self, TryStreamExt};
    ///
    /// let mut x = 0i32;
    ///
    /// {
    ///     let fut = stream::repeat(Ok(1)).try_for_each(|item| {
    ///         x += item;
    ///         future::ready(if x == 3 { Err(()) } else { Ok(()) })
    ///     });
    ///     assert_eq!(await!(fut), Err(()));
    /// }
    ///
    /// assert_eq!(x, 3);
    /// # })
    /// ```
    fn try_for_each<Fut, F>(self, f: F) -> TryForEach<Self, Fut, F>
        where F: FnMut(Self::Ok) -> Fut,
              Fut: TryFuture<Ok = (), Error=Self::Error>,
              Self: Sized
    {
        TryForEach::new(self, f)
    }

    /// Attempt to Collect all of the values of this stream into a vector,
    /// returning a future representing the result of that computation.
    ///
    /// This combinator will collect all successful results of this stream and
    /// collect them into a `Vec<Self::Item>`. If an error happens then all
    /// collected elements will be dropped and the error will be returned.
    ///
    /// The returned future will be resolved when the stream terminates.
    ///
    /// This method is only available when the `std` feature of this
    /// library is activated, and it is activated by default.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(async_await, await_macro)]
    /// # futures::executor::block_on(async {
    /// use futures::channel::mpsc;
    /// use futures::executor::block_on;
    /// use futures::stream::TryStreamExt;
    /// use std::thread;
    ///
    /// let (mut tx, rx) = mpsc::unbounded();
    ///
    /// thread::spawn(move || {
    ///     for i in (1..=5) {
    ///         tx.unbounded_send(Ok(i)).unwrap();
    ///     }
    ///     tx.unbounded_send(Err(6)).unwrap();
    /// });
    ///
    /// let output: Result<Vec<i32>, i32> = await!(rx.try_collect());
    /// assert_eq!(output, Err(6));
    /// # })
    /// ```
    #[cfg(feature = "std")]
    fn try_collect<C: Default + Extend<Self::Ok>>(self) -> TryCollect<Self, C>
        where Self: Sized
    {
        TryCollect::new(self)
    }

    /// Attempt to filter the values produced by this stream while
    /// simultaneously mapping them to a different type according to the
    /// provided asynchronous closure.
    ///
    /// As values of this stream are made available, the provided function will
    /// be run on them. If the future returned by the predicate `f` resolves to
    /// [`Some(item)`](Some) then the stream will yield the value `item`, but if
    /// it resolves to [`None`] then the next value will be produced.
    ///
    /// All errors are passed through without filtering in this combinator.
    ///
    /// Note that this function consumes the stream passed into it and returns a
    /// wrapped version of it, similar to the existing `filter_map` methods in
    /// the standard library.
    ///
    /// # Examples
    /// ```
    /// #![feature(async_await, await_macro)]
    /// # futures::executor::block_on(async {
    /// use futures::executor::block_on;
    /// use futures::prelude::*;
    ///
    /// let stream = stream::iter(vec![Ok(1i32), Ok(6i32), Err("error")]);
    /// let mut halves = stream.try_filter_map(|x| {
    ///     let ret = if x % 2 == 0 { Some(x / 2) } else { None };
    ///     future::ready(Ok(ret))
    /// });
    ///
    /// assert_eq!(await!(halves.next()), Some(Ok(3)));
    /// assert_eq!(await!(halves.next()), Some(Err("error")));
    /// # })
    /// ```
    fn try_filter_map<Fut, F, T>(self, f: F) -> TryFilterMap<Self, Fut, F>
        where Fut: TryFuture<Ok = Option<T>, Error = Self::Error>,
              F: FnMut(Self::Ok) -> Fut,
              Self: Sized
    {
        TryFilterMap::new(self, f)
    }

    /// Attempt to execute several futures from a stream concurrently.
    ///
    /// This stream's `Ok` type must be a [`TryFuture`] with an `Error` type
    /// that matches the stream's `Error` type.
    ///
    /// This adaptor will buffer up to `n` futures and then return their
    /// outputs in the order in which they complete. If the underlying stream
    /// returns an error, it will be immediately propagated.
    ///
    /// The returned stream will be a stream of results, each containing either
    /// an error or a future's output. An error can be produced either by the
    /// underlying stream itself or by one of the futures it yielded.
    ///
    /// This method is only available when the `std` feature of this
    /// library is activated, and it is activated by default.
    ///
    /// # Examples
    ///
    /// Results are returned in the order of completion:
    /// ```
    /// #![feature(async_await, await_macro)]
    /// # futures::executor::block_on(async {
    /// use futures::channel::oneshot;
    /// use futures::stream::{self, StreamExt, TryStreamExt};
    ///
    /// let (send_one, recv_one) = oneshot::channel();
    /// let (send_two, recv_two) = oneshot::channel();
    ///
    /// let stream_of_futures = stream::iter(vec![Ok(recv_one), Ok(recv_two)]);
    ///
    /// let mut buffered = stream_of_futures.try_buffer_unordered(10);
    ///
    /// send_two.send(2i32);
    /// assert_eq!(await!(buffered.next()), Some(Ok(2i32)));
    ///
    /// send_one.send(1i32);
    /// assert_eq!(await!(buffered.next()), Some(Ok(1i32)));
    ///
    /// assert_eq!(await!(buffered.next()), None);
    /// # })
    /// ```
    ///
    /// Errors from the underlying stream itself are propagated:
    /// ```
    /// #![feature(async_await, await_macro)]
    /// # futures::executor::block_on(async {
    /// use futures::channel::mpsc;
    /// use futures::future;
    /// use futures::stream::{StreamExt, TryStreamExt};
    ///
    /// let (sink, stream_of_futures) = mpsc::unbounded();
    /// let mut buffered = stream_of_futures.try_buffer_unordered(10);
    ///
    /// sink.unbounded_send(Ok(future::ready(Ok(7i32))));
    /// assert_eq!(await!(buffered.next()), Some(Ok(7i32)));
    ///
    /// sink.unbounded_send(Err("error in the stream"));
    /// assert_eq!(await!(buffered.next()), Some(Err("error in the stream")));
    /// # })
    /// ```
    #[cfg(feature = "std")]
    fn try_buffer_unordered(self, n: usize) -> TryBufferUnordered<Self>
        where Self::Ok: TryFuture<Error = Self::Error>,
              Self: Sized
    {
        TryBufferUnordered::new(self, n)
    }
}
<|endoftext|>


<commit_before>use super::*;
use redox::*;

impl Editor {
    /// Redraw the window
    pub fn redraw(&mut self) {
        // TODO: Only draw when relevant for the window
        let x = self.x();
        let y = self.y();
        // Redraw window
        self.window.set(Color::BLACK);

        self.window.rect(8 * (x - self.scroll_y) as isize,
                         16 * (y - self.scroll_x) as isize,
                         8,
                         16,
                         Color::WHITE);

        let mut string = false;

        for (y, row) in self.text.iter().enumerate() {
            for (x, &c) in row.iter().enumerate() {
                // TODO: Move outta here
                let color = match c {
                    '\'' | '"' => {
                        string = !string;
                        (226, 225, 167) //(167, 222, 156)
                    },
                    _ if string => (226, 225, 167), //(167, 222, 156)
                    '!' | '@' | '#' | '$' | '%' | '^' | '&' | '|' | '*' | '+' | '-' | '/' | ':' | '=' | '<' | '>' => (198, 83, 83), //(228, 190, 175), //(194, 106, 71),
                    '.' | ',' => (241, 213, 226),
                    '(' | ')' | '[' | ']' | '{' | '}' => (164, 212, 125), //(195, 139, 75),
                    '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => (209, 209, 177),
                    _ => (255, 255, 255),
                };

                if self.x() == x && self.y() == y {
                    self.window.char(8 * (x - self.scroll_y) as isize,
                                     16 * (y - self.scroll_x) as isize,
                                     c,
                                     Color::rgb(color.0 / 3, color.1 / 3, color.2 / 3));
                } else {
                    self.window.char(8 * (x - self.scroll_y) as isize,
                                     16 * (y - self.scroll_x) as isize,
                                     c,
                                     Color::rgb(color.0, color.1, color.2));
                }
            }
        }
        let h = self.window.height();
        let w = self.window.width();
        self.window.rect(0, h as isize - 18, w, 18, Color::rgba(74, 74, 74, 255));

        for (n, c) in (if self.status_bar.mode.len() > w / (8 * 4) {
            self.status_bar.mode.chars().take(w / (8 * 4) - 5).chain(vec!['.', '.', '.']).collect::<Vec<_>>()
        } else {
            self.status_bar.mode.chars().collect()
        }).into_iter().enumerate() {

            self.window.char(n as isize * 8, h as isize - 16 - 1, if c == '\t' { ' ' } else { c }, Color::WHITE);
        }

        self.window.sync();
    }
}

/// The statubar (showing various info about the current state of the editor)
pub struct StatusBar {
    /// The current mode
    pub mode: String,
    /// The cureent char
    pub file: String,
    /// The current command
    pub cmd: String,
    /// A message (such as an error or other info to the user)
    pub msg: String,
}

impl StatusBar {
    /// Create new status bar
    pub fn new() -> Self {
        StatusBar {
            mode: "Normal".to_string(),
            file: String::new(),
            cmd: String::new(),
            msg: String::new(),
        }
    }
}
<commit_msg>Change bg color<commit_after>use super::*;
use redox::*;

impl Editor {
    /// Redraw the window
    pub fn redraw(&mut self) {
        // TODO: Only draw when relevant for the window
        let x = self.x();
        let y = self.y();
        // Redraw window
        self.window.set(Color::rgb(25, 25, 25));

        self.window.rect(8 * (x - self.scroll_y) as isize,
                         16 * (y - self.scroll_x) as isize,
                         8,
                         16,
                         Color::WHITE);

        let mut string = false;

        for (y, row) in self.text.iter().enumerate() {
            for (x, &c) in row.iter().enumerate() {
                // TODO: Move outta here
                let color = match c {
                    '\'' | '"' => {
                        string = !string;
                        (226, 225, 167) //(167, 222, 156)
                    },
                    _ if string => (226, 225, 167), //(167, 222, 156)
                    '!' | '@' | '#' | '$' | '%' | '^' | '&' | '|' | '*' | '+' | '-' | '/' | ':' | '=' | '<' | '>' => (198, 83, 83), //(228, 190, 175), //(194, 106, 71),
                    '.' | ',' => (241, 213, 226),
                    '(' | ')' | '[' | ']' | '{' | '}' => (164, 212, 125), //(195, 139, 75),
                    '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => (209, 209, 177),
                    _ => (255, 255, 255),
                };

                if self.x() == x && self.y() == y {
                    self.window.char(8 * (x - self.scroll_y) as isize,
                                     16 * (y - self.scroll_x) as isize,
                                     c,
                                     Color::rgb(color.0 / 3, color.1 / 3, color.2 / 3));
                } else {
                    self.window.char(8 * (x - self.scroll_y) as isize,
                                     16 * (y - self.scroll_x) as isize,
                                     c,
                                     Color::rgb(color.0, color.1, color.2));
                }
            }
        }
        let h = self.window.height();
        let w = self.window.width();
        self.window.rect(0, h as isize - 18, w, 18, Color::rgba(74, 74, 74, 255));

        for (n, c) in (if self.status_bar.mode.len() > w / (8 * 4) {
            self.status_bar.mode.chars().take(w / (8 * 4) - 5).chain(vec!['.', '.', '.']).collect::<Vec<_>>()
        } else {
            self.status_bar.mode.chars().collect()
        }).into_iter().enumerate() {

            self.window.char(n as isize * 8, h as isize - 16 - 1, if c == '\t' { ' ' } else { c }, Color::WHITE);
        }

        self.window.sync();
    }
}

/// The statubar (showing various info about the current state of the editor)
pub struct StatusBar {
    /// The current mode
    pub mode: String,
    /// The cureent char
    pub file: String,
    /// The current command
    pub cmd: String,
    /// A message (such as an error or other info to the user)
    pub msg: String,
}

impl StatusBar {
    /// Create new status bar
    pub fn new() -> Self {
        StatusBar {
            mode: "Normal".to_string(),
            file: String::new(),
            cmd: String::new(),
            msg: String::new(),
        }
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Code to handle a collection of block devices.

use std::io;
use std::collections::{HashMap, HashSet};
use std::fs::{File, OpenOptions};
use std::path::{Path, PathBuf};
use std::str::FromStr;

use chrono::{DateTime, Duration, Utc};
use rand::{thread_rng, sample};
use uuid::Uuid;

use devicemapper::{Bytes, Device, Sectors, Segment};

use super::super::consts::IEC;
use super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::types::{DevUuid, PoolUuid};

use super::cleanup::wipe_blockdevs;
use super::blockdev::BlockDev;
use super::device::blkdev_size;
use super::engine::DevOwnership;
use super::metadata::{BDA, MIN_MDA_SECTORS, StaticHeader, validate_mda_size};
use super::range_alloc::RangeAllocator;
use super::serde_structs::{BlockDevSave, Recordable};

const MIN_DEV_SIZE: Bytes = Bytes(IEC::Gi);
const MAX_NUM_TO_WRITE: usize = 10;

/// Resolve a list of Paths of some sort to a set of unique Devices.
/// Return an IOError if there was a problem resolving any particular device.
pub fn resolve_devices(paths: &[&Path]) -> io::Result<HashSet<Device>> {
    paths
        .iter()
        .map(|p| Device::from_str(&p.to_string_lossy()))
        .collect()
}

#[derive(Debug)]
pub struct BlockDevMgr {
    block_devs: Vec<BlockDev>,
    last_update_time: Option<DateTime<Utc>>,
}

impl BlockDevMgr {
    pub fn new(block_devs: Vec<BlockDev>) -> BlockDevMgr {
        BlockDevMgr {
            block_devs: block_devs,
            last_update_time: None,
        }
    }

    /// Initialize a new BlockDevMgr with specified pool and devices.
    pub fn initialize(pool_uuid: &PoolUuid,
                      paths: &[&Path],
                      mda_size: Sectors,
                      force: bool)
                      -> EngineResult<BlockDevMgr> {
        let devices = resolve_devices(paths)?;
        Ok(BlockDevMgr::new(initialize(pool_uuid, devices, mda_size, force)?))
    }

    /// Obtain a BlockDev by its Device.
    pub fn get_by_device(&self, device: Device) -> Option<&BlockDev> {
        self.block_devs.iter().find(|d| d.device() == &device)
    }

    // Obtain a BlockDev by its UUID.
    pub fn get_by_uuid(&self, uuid: &DevUuid) -> Option<&BlockDev> {
        self.block_devs.iter().find(|d| d.uuid() == uuid)
    }

    pub fn add(&mut self,
               pool_uuid: &PoolUuid,
               paths: &[&Path],
               force: bool)
               -> EngineResult<Vec<PathBuf>> {
        let devices = resolve_devices(paths)?;
        let bds = initialize(pool_uuid, devices, MIN_MDA_SECTORS, force)?;
        let bdev_paths = bds.iter().map(|p| p.devnode.clone()).collect();
        for bd in bds {
            self.block_devs.push(bd);
        }
        Ok(bdev_paths)
    }

    pub fn destroy_all(self) -> EngineResult<()> {
        wipe_blockdevs(self.block_devs)
    }

    /// If available space is less than size, return None, else return
    /// the segments allocated.
    pub fn alloc_space(&mut self, size: Sectors) -> Option<Vec<Segment>> {
        let mut needed: Sectors = size;
        let mut segs = Vec::new();

        if self.avail_space() < size {
            return None;
        }

        for bd in &mut self.block_devs {
            if needed == Sectors(0) {
                break;
            }

            let (gotten, r_segs) = bd.request_space(needed);
            segs.extend(r_segs);
            needed -= gotten;
        }

        assert_eq!(needed, Sectors(0));

        Some(segs)
    }

    pub fn devnodes(&self) -> Vec<PathBuf> {
        self.block_devs
            .iter()
            .map(|d| d.devnode.clone())
            .collect()
    }

    /// Write the given data to all blockdevs marking with current time.
    /// Return an error if data was not written to any blockdev.
    /// Omit blockdevs which do not have sufficient space in BDA to accommodate
    /// metadata. If current time is not more recent than previously written
    /// time, use a time that is one nanosecond greater than that previously
    /// written. Randomly select no more than MAX_NUM_TO_WRITE blockdevs to
    /// write to.
    pub fn save_state(&mut self, metadata: &[u8]) -> EngineResult<()> {
        let current_time = Utc::now();
        let stamp_time = if Some(current_time) <= self.last_update_time {
            self.last_update_time
                .expect("self.last_update_time >= Some(current_time")
                .checked_add_signed(Duration::nanoseconds(1))
                .expect("self.last_update_time << maximum representable DateTime")
        } else {
            current_time
        };

        let data_size = Bytes(metadata.len() as u64).sectors();
        let candidates = self.block_devs
            .iter_mut()
            .filter(|b| b.max_metadata_size() >= data_size);

        // TODO: consider making selection not entirely random, i.e, ensuring
        // distribution of metadata over different paths.
        let saved = sample(&mut thread_rng(), candidates, MAX_NUM_TO_WRITE)
            .iter_mut()
            .fold(false,
                  |acc, b| acc | b.save_state(&stamp_time, metadata).is_ok());

        if saved {
            self.last_update_time = Some(stamp_time);
            Ok(())
        } else {
            let err_msg = "Failed to save metadata to even one device in pool";
            Err(EngineError::Engine(ErrorEnum::Error, err_msg.into()))
        }
    }

    // SIZE methods

    /// The number of sectors not allocated for any purpose.
    pub fn avail_space(&self) -> Sectors {
        self.block_devs.iter().map(|bd| bd.available()).sum()
    }

    /// The current capacity of all the blockdevs.
    /// self.current_capacity() > self.avail_space() because some sectors
    /// are certainly allocated for Stratis metadata
    pub fn current_capacity(&self) -> Sectors {
        self.block_devs
            .iter()
            .map(|b| b.current_capacity())
            .sum()
    }

    /// The number of sectors given over to Stratis metadata
    /// self.current_capacity() - self.metadata_size() >= self.avail_space()
    pub fn metadata_size(&self) -> Sectors {
        self.block_devs
            .iter()
            .map(|bd| bd.metadata_size())
            .sum()
    }
}

impl Recordable<HashMap<Uuid, BlockDevSave>> for BlockDevMgr {
    fn record(&self) -> EngineResult<HashMap<Uuid, BlockDevSave>> {
        self.block_devs
            .iter()
            .map(|bd| bd.record().and_then(|bdsave| Ok((*bd.uuid(), bdsave))))
            .collect()
    }
}

/// Initialize multiple blockdevs at once. This allows all of them
/// to be checked for usability before writing to any of them.
pub fn initialize(pool_uuid: &PoolUuid,
                  devices: HashSet<Device>,
                  mda_size: Sectors,
                  force: bool)
                  -> EngineResult<Vec<BlockDev>> {

    /// Get device information, returns an error if problem with obtaining
    /// that information.
    /// Returns a tuple with the device's path, its size in bytes,
    /// its ownership as determined by calling determine_ownership(),
    /// and an open File handle, all of which are needed later.
    pub fn dev_info(dev: &Device) -> EngineResult<(PathBuf, Bytes, DevOwnership, File)> {
        let devnode = dev.devnode()
            .ok_or_else(|| {
                            EngineError::Engine(ErrorEnum::NotFound,
                                                format!("could not get device node from dev {}",
                                                        dev.dstr()))
                        })?;

        let mut f = OpenOptions::new()
            .read(true)
            .write(true)
            .open(&devnode)?;
        let dev_size = blkdev_size(&f)?;
        let ownership = StaticHeader::determine_ownership(&mut f)?;

        Ok((devnode, dev_size, ownership, f))
    }

    /// Filter devices for admission to pool based on dev_infos.
    /// If there is an error finding out the info, return that error.
    /// Also, return an error if a device is not appropriate for this pool.
    #[allow(type_complexity)]
    fn filter_devs<I>(dev_infos: I,
                      pool_uuid: &PoolUuid,
                      force: bool)
                      -> EngineResult<Vec<(Device, (PathBuf, Bytes, File))>>
        where I: Iterator<Item = (Device, EngineResult<(PathBuf, Bytes, DevOwnership, File)>)>
    {
        let mut add_devs = Vec::new();
        for (dev, dev_result) in dev_infos {
            let (devnode, dev_size, ownership, f) = dev_result?;
            if dev_size < MIN_DEV_SIZE {
                let error_message = format!("{} too small, minimum {} bytes",
                                            devnode.display(),
                                            MIN_DEV_SIZE);
                return Err(EngineError::Engine(ErrorEnum::Invalid, error_message));
            };
            match ownership {
                DevOwnership::Unowned => add_devs.push((dev, (devnode, dev_size, f))),
                DevOwnership::Theirs => {
                    if !force {
                        let err_str = format!("Device {} appears to belong to another application",
                                              devnode.display());
                        return Err(EngineError::Engine(ErrorEnum::Invalid, err_str));
                    } else {
                        add_devs.push((dev, (devnode, dev_size, f)))
                    }
                }
                DevOwnership::Ours(uuid) => {
                    if *pool_uuid != uuid {
                        let error_str = format!("Device {} already belongs to Stratis pool {}",
                                                devnode.display(),
                                                uuid);
                        return Err(EngineError::Engine(ErrorEnum::Invalid, error_str));
                    } else {
                        // Already in this pool (according to its header)
                        // TODO: Check we already know about it
                        // if yes, ignore. If no, add it w/o initializing?
                    }
                }
            }
        }
        Ok(add_devs)
    }

    validate_mda_size(mda_size)?;

    let dev_infos = devices.into_iter().map(|d: Device| (d, dev_info(&d)));

    let add_devs = filter_devs(dev_infos, pool_uuid, force)?;

    let mut bds: Vec<BlockDev> = Vec::new();
    for (dev, (devnode, dev_size, mut f)) in add_devs {

        let bda = BDA::initialize(&mut f,
                                  pool_uuid,
                                  &Uuid::new_v4(),
                                  mda_size,
                                  dev_size.sectors());
        if let Ok(bda) = bda {
            let allocator = RangeAllocator::new(bda.dev_size(), &[(Sectors(0), bda.size())])
                .expect("bda.size() < bda.dev_size() and single range");

            bds.push(BlockDev::new(dev, devnode, bda, allocator));
        } else {
            // TODO: check the return values and update state machine on failure
            let _ = BDA::wipe(&mut f);
            let _ = wipe_blockdevs(bds);

            return Err(bda.unwrap_err());
        }
    }
    Ok(bds)
}
<commit_msg>Use extend() instead of a for loop<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Code to handle a collection of block devices.

use std::io;
use std::collections::{HashMap, HashSet};
use std::fs::{File, OpenOptions};
use std::path::{Path, PathBuf};
use std::str::FromStr;

use chrono::{DateTime, Duration, Utc};
use rand::{thread_rng, sample};
use uuid::Uuid;

use devicemapper::{Bytes, Device, Sectors, Segment};

use super::super::consts::IEC;
use super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::types::{DevUuid, PoolUuid};

use super::cleanup::wipe_blockdevs;
use super::blockdev::BlockDev;
use super::device::blkdev_size;
use super::engine::DevOwnership;
use super::metadata::{BDA, MIN_MDA_SECTORS, StaticHeader, validate_mda_size};
use super::range_alloc::RangeAllocator;
use super::serde_structs::{BlockDevSave, Recordable};

const MIN_DEV_SIZE: Bytes = Bytes(IEC::Gi);
const MAX_NUM_TO_WRITE: usize = 10;

/// Resolve a list of Paths of some sort to a set of unique Devices.
/// Return an IOError if there was a problem resolving any particular device.
pub fn resolve_devices(paths: &[&Path]) -> io::Result<HashSet<Device>> {
    paths
        .iter()
        .map(|p| Device::from_str(&p.to_string_lossy()))
        .collect()
}

#[derive(Debug)]
pub struct BlockDevMgr {
    block_devs: Vec<BlockDev>,
    last_update_time: Option<DateTime<Utc>>,
}

impl BlockDevMgr {
    pub fn new(block_devs: Vec<BlockDev>) -> BlockDevMgr {
        BlockDevMgr {
            block_devs: block_devs,
            last_update_time: None,
        }
    }

    /// Initialize a new BlockDevMgr with specified pool and devices.
    pub fn initialize(pool_uuid: &PoolUuid,
                      paths: &[&Path],
                      mda_size: Sectors,
                      force: bool)
                      -> EngineResult<BlockDevMgr> {
        let devices = resolve_devices(paths)?;
        Ok(BlockDevMgr::new(initialize(pool_uuid, devices, mda_size, force)?))
    }

    /// Obtain a BlockDev by its Device.
    pub fn get_by_device(&self, device: Device) -> Option<&BlockDev> {
        self.block_devs.iter().find(|d| d.device() == &device)
    }

    // Obtain a BlockDev by its UUID.
    pub fn get_by_uuid(&self, uuid: &DevUuid) -> Option<&BlockDev> {
        self.block_devs.iter().find(|d| d.uuid() == uuid)
    }

    pub fn add(&mut self,
               pool_uuid: &PoolUuid,
               paths: &[&Path],
               force: bool)
               -> EngineResult<Vec<PathBuf>> {
        let devices = resolve_devices(paths)?;
        let bds = initialize(pool_uuid, devices, MIN_MDA_SECTORS, force)?;
        let bdev_paths = bds.iter().map(|p| p.devnode.clone()).collect();
        self.block_devs.extend(bds);
        Ok(bdev_paths)
    }

    pub fn destroy_all(self) -> EngineResult<()> {
        wipe_blockdevs(self.block_devs)
    }

    /// If available space is less than size, return None, else return
    /// the segments allocated.
    pub fn alloc_space(&mut self, size: Sectors) -> Option<Vec<Segment>> {
        let mut needed: Sectors = size;
        let mut segs = Vec::new();

        if self.avail_space() < size {
            return None;
        }

        for bd in &mut self.block_devs {
            if needed == Sectors(0) {
                break;
            }

            let (gotten, r_segs) = bd.request_space(needed);
            segs.extend(r_segs);
            needed -= gotten;
        }

        assert_eq!(needed, Sectors(0));

        Some(segs)
    }

    pub fn devnodes(&self) -> Vec<PathBuf> {
        self.block_devs
            .iter()
            .map(|d| d.devnode.clone())
            .collect()
    }

    /// Write the given data to all blockdevs marking with current time.
    /// Return an error if data was not written to any blockdev.
    /// Omit blockdevs which do not have sufficient space in BDA to accommodate
    /// metadata. If current time is not more recent than previously written
    /// time, use a time that is one nanosecond greater than that previously
    /// written. Randomly select no more than MAX_NUM_TO_WRITE blockdevs to
    /// write to.
    pub fn save_state(&mut self, metadata: &[u8]) -> EngineResult<()> {
        let current_time = Utc::now();
        let stamp_time = if Some(current_time) <= self.last_update_time {
            self.last_update_time
                .expect("self.last_update_time >= Some(current_time")
                .checked_add_signed(Duration::nanoseconds(1))
                .expect("self.last_update_time << maximum representable DateTime")
        } else {
            current_time
        };

        let data_size = Bytes(metadata.len() as u64).sectors();
        let candidates = self.block_devs
            .iter_mut()
            .filter(|b| b.max_metadata_size() >= data_size);

        // TODO: consider making selection not entirely random, i.e, ensuring
        // distribution of metadata over different paths.
        let saved = sample(&mut thread_rng(), candidates, MAX_NUM_TO_WRITE)
            .iter_mut()
            .fold(false,
                  |acc, b| acc | b.save_state(&stamp_time, metadata).is_ok());

        if saved {
            self.last_update_time = Some(stamp_time);
            Ok(())
        } else {
            let err_msg = "Failed to save metadata to even one device in pool";
            Err(EngineError::Engine(ErrorEnum::Error, err_msg.into()))
        }
    }

    // SIZE methods

    /// The number of sectors not allocated for any purpose.
    pub fn avail_space(&self) -> Sectors {
        self.block_devs.iter().map(|bd| bd.available()).sum()
    }

    /// The current capacity of all the blockdevs.
    /// self.current_capacity() > self.avail_space() because some sectors
    /// are certainly allocated for Stratis metadata
    pub fn current_capacity(&self) -> Sectors {
        self.block_devs
            .iter()
            .map(|b| b.current_capacity())
            .sum()
    }

    /// The number of sectors given over to Stratis metadata
    /// self.current_capacity() - self.metadata_size() >= self.avail_space()
    pub fn metadata_size(&self) -> Sectors {
        self.block_devs
            .iter()
            .map(|bd| bd.metadata_size())
            .sum()
    }
}

impl Recordable<HashMap<Uuid, BlockDevSave>> for BlockDevMgr {
    fn record(&self) -> EngineResult<HashMap<Uuid, BlockDevSave>> {
        self.block_devs
            .iter()
            .map(|bd| bd.record().and_then(|bdsave| Ok((*bd.uuid(), bdsave))))
            .collect()
    }
}

/// Initialize multiple blockdevs at once. This allows all of them
/// to be checked for usability before writing to any of them.
pub fn initialize(pool_uuid: &PoolUuid,
                  devices: HashSet<Device>,
                  mda_size: Sectors,
                  force: bool)
                  -> EngineResult<Vec<BlockDev>> {

    /// Get device information, returns an error if problem with obtaining
    /// that information.
    /// Returns a tuple with the device's path, its size in bytes,
    /// its ownership as determined by calling determine_ownership(),
    /// and an open File handle, all of which are needed later.
    pub fn dev_info(dev: &Device) -> EngineResult<(PathBuf, Bytes, DevOwnership, File)> {
        let devnode = dev.devnode()
            .ok_or_else(|| {
                            EngineError::Engine(ErrorEnum::NotFound,
                                                format!("could not get device node from dev {}",
                                                        dev.dstr()))
                        })?;

        let mut f = OpenOptions::new()
            .read(true)
            .write(true)
            .open(&devnode)?;
        let dev_size = blkdev_size(&f)?;
        let ownership = StaticHeader::determine_ownership(&mut f)?;

        Ok((devnode, dev_size, ownership, f))
    }

    /// Filter devices for admission to pool based on dev_infos.
    /// If there is an error finding out the info, return that error.
    /// Also, return an error if a device is not appropriate for this pool.
    #[allow(type_complexity)]
    fn filter_devs<I>(dev_infos: I,
                      pool_uuid: &PoolUuid,
                      force: bool)
                      -> EngineResult<Vec<(Device, (PathBuf, Bytes, File))>>
        where I: Iterator<Item = (Device, EngineResult<(PathBuf, Bytes, DevOwnership, File)>)>
    {
        let mut add_devs = Vec::new();
        for (dev, dev_result) in dev_infos {
            let (devnode, dev_size, ownership, f) = dev_result?;
            if dev_size < MIN_DEV_SIZE {
                let error_message = format!("{} too small, minimum {} bytes",
                                            devnode.display(),
                                            MIN_DEV_SIZE);
                return Err(EngineError::Engine(ErrorEnum::Invalid, error_message));
            };
            match ownership {
                DevOwnership::Unowned => add_devs.push((dev, (devnode, dev_size, f))),
                DevOwnership::Theirs => {
                    if !force {
                        let err_str = format!("Device {} appears to belong to another application",
                                              devnode.display());
                        return Err(EngineError::Engine(ErrorEnum::Invalid, err_str));
                    } else {
                        add_devs.push((dev, (devnode, dev_size, f)))
                    }
                }
                DevOwnership::Ours(uuid) => {
                    if *pool_uuid != uuid {
                        let error_str = format!("Device {} already belongs to Stratis pool {}",
                                                devnode.display(),
                                                uuid);
                        return Err(EngineError::Engine(ErrorEnum::Invalid, error_str));
                    } else {
                        // Already in this pool (according to its header)
                        // TODO: Check we already know about it
                        // if yes, ignore. If no, add it w/o initializing?
                    }
                }
            }
        }
        Ok(add_devs)
    }

    validate_mda_size(mda_size)?;

    let dev_infos = devices.into_iter().map(|d: Device| (d, dev_info(&d)));

    let add_devs = filter_devs(dev_infos, pool_uuid, force)?;

    let mut bds: Vec<BlockDev> = Vec::new();
    for (dev, (devnode, dev_size, mut f)) in add_devs {

        let bda = BDA::initialize(&mut f,
                                  pool_uuid,
                                  &Uuid::new_v4(),
                                  mda_size,
                                  dev_size.sectors());
        if let Ok(bda) = bda {
            let allocator = RangeAllocator::new(bda.dev_size(), &[(Sectors(0), bda.size())])
                .expect("bda.size() < bda.dev_size() and single range");

            bds.push(BlockDev::new(dev, devnode, bda, allocator));
        } else {
            // TODO: check the return values and update state machine on failure
            let _ = BDA::wipe(&mut f);
            let _ = wipe_blockdevs(bds);

            return Err(bda.unwrap_err());
        }
    }
    Ok(bds)
}
<|endoftext|>


<commit_before>// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Provides utilities to bind and open a VFIO PCI device.

use std::fs::write;
use std::sync::Arc;

use anyhow::{anyhow, Context, Result};
use sync::Mutex;

use crate::pci::PciAddress;
use crate::vfio::{VfioContainer, VfioDevice};

/// Opens the PCI device as `VfioDevice`.
pub fn open_vfio_device(pci_address: PciAddress) -> Result<VfioDevice> {
    // Convert the PCI address to "DDDD:bb:dd.f" format.
    let addr_str = pci_address.to_string();

    // Unbind virtio-pci from the device.
    write("/sys/bus/pci/drivers/virtio-pci/unbind", &addr_str)
        .context("failed to unbind as virtio-pci device")?;

    // Set "vfio-pci" for the driver so the device can be bound to our vfio driver.
    write(
        format!("/sys/bus/pci/devices/{}/driver_override", &addr_str),
        "vfio-pci\n",
    )
    .context("failed to override driver")?;

    // Bind the device to the driver.
    write("/sys/bus/pci/drivers/vfio-pci/bind", &addr_str).context("failed to bind VFIO device")?;

    // Write the empty string to driver_override to return the device to standard matching rules
    // binding. Note that this operation won't unbind the current driver or load a new driver.
    write(
        format!("/sys/bus/pci/devices/{}/driver_override", &addr_str),
        "",
    )
    .context("failed to clear driver_override")?;

    let vfio_path = format!("/sys/bus/pci/devices/{}", &addr_str);
    let vfio_container = Arc::new(Mutex::new(VfioContainer::new()?));
    let vfio = VfioDevice::new(&vfio_path, vfio_container)
        .map_err(|e| anyhow!("failed to create VFIO device: {}", e))?;
    Ok(vfio)
}
<commit_msg>devices: vvu: poll when opening vvu device<commit_after>// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Provides utilities to bind and open a VFIO PCI device.

use std::fs::write;
use std::sync::Arc;

use anyhow::{anyhow, Context, Result};
use sync::Mutex;

use crate::pci::PciAddress;
use crate::vfio::{VfioContainer, VfioDevice};

/// Opens the PCI device as `VfioDevice`.
pub fn open_vfio_device(pci_address: PciAddress) -> Result<VfioDevice> {
    // Convert the PCI address to "DDDD:bb:dd.f" format.
    let addr_str = pci_address.to_string();

    // Unbind virtio-pci from the device.
    write("/sys/bus/pci/drivers/virtio-pci/unbind", &addr_str)
        .context("failed to unbind as virtio-pci device")?;

    // Set "vfio-pci" for the driver so the device can be bound to our vfio driver.
    write(
        format!("/sys/bus/pci/devices/{}/driver_override", &addr_str),
        "vfio-pci\n",
    )
    .context("failed to override driver")?;

    // Bind the device to the driver.
    write("/sys/bus/pci/drivers/vfio-pci/bind", &addr_str).context("failed to bind VFIO device")?;

    // Write the empty string to driver_override to return the device to standard matching rules
    // binding. Note that this operation won't unbind the current driver or load a new driver.
    write(
        format!("/sys/bus/pci/devices/{}/driver_override", &addr_str),
        "",
    )
    .context("failed to clear driver_override")?;

    let vfio_path = format!("/sys/bus/pci/devices/{}", &addr_str);
    let mut last_err = None;
    // We can't create the container until udev updates the permissions on
    // /dev/vfio/$group_id. There's no easy way to wait for that to happen, so
    // just poll. In practice, this should take <100ms.
    for _ in 1..50 {
        let vfio_container = Arc::new(Mutex::new(VfioContainer::new()?));
        match VfioDevice::new(&vfio_path, vfio_container) {
            Ok(vfio_dev) => return Ok(vfio_dev),
            Err(e) => {
                std::thread::sleep(std::time::Duration::from_millis(100));
                last_err = Some(e);
            }
        }
    }
    Err(anyhow!(
        "failed to create VFIO device: {}",
        last_err.unwrap()
    ))
}
<|endoftext|>