<commit_before>extern crate pancurses;
extern crate rand;

use pancurses::*;

use rand::Rng;

const AUS_MAP: [&'static str; 13] = [
    "                       A ",
    "           AA         AA ",
    "    N.T. AAAAA       AAAA ",
    "     AAAAAAAAAAA  AAAAAAAA ",
    "   AAAAAAAAAAAAAAAAAAAAAAAAA Qld.",
    " AAAAAAAAAAAAAAAAAAAAAAAAAAAA ",
    " AAAAAAAAAAAAAAAAAAAAAAAAAAAAA ",
    " AAAAAAAAAAAAAAAAAAAAAAAAAAAA ",
    "   AAAAAAAAAAAAAAAAAAAAAAAAA N.S.W.",
    "W.A. AAAAAAAAA      AAAAAA Vic.",
    "       AAA   S.A.     AA",
    "                       A  Tas.",
    "",
];

const MESSAGES: [&'static str; 6] = [
    "Hello from the Land Down Under",
    "The Land of crocs, and a big Red Rock",
    "Where the sunflower runs along the highways",
    "The dusty red roads lead one to loneliness",
    "Blue sky in the morning and",
    "Freezing nights and twinkling stars",
];

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

    start_color();

    use_default_colors();

    cbreak();
    noecho();

    curs_set(0);

    // Refresh stdscr so that reading from it will not cause it to  overwrite the other windows
    // that are being created
    main_window.refresh();

    // Create a drawing window
    let width = 48;
    let height = 15;

    let win = newwin(
        height,
        width,
        (main_window.get_max_y() - height) / 2,
        (main_window.get_max_x() - width) / 2,
    );

    loop {
        init_pair(1, COLOR_WHITE, COLOR_BLUE);
        win.bkgd(COLOR_PAIR(1));
        win.erase();

        init_pair(2, COLOR_RED, COLOR_RED);
        win.attrset(ColorPair(2));
        win.draw_box(' ', ' ');
        win.refresh();

        win.attrset(Attribute::Normal);

        // Do random output of a character

        let mut ch = 'a';

        main_window.nodelay(true);

        let mut rng = rand::thread_rng();

        for i in 0..5000 {
            let x = (rng.gen::<i32>() % (width - 2)).abs() + 1;
            let y = (rng.gen::<i32>() % (height - 2)).abs() + 1;

            assert!(x != 0);
            assert!(y != 0);
            win.mvaddch(y, x, ch);
            win.refresh();

            if main_window.getch().is_some() {
                break;
            }

            if i == 2000 {
                ch = 'b';
                init_pair(3, COLOR_CYAN, COLOR_YELLOW);
                win.attrset(ColorPair(3));
            }
        }

        main_window.nodelay(false);

        sub_win_test(&main_window, &win).unwrap();

        // Erase and draw green window

        init_pair(4, COLOR_YELLOW, COLOR_GREEN);
        win.bkgd(COLOR_PAIR(4));
        win.attrset(Attribute::Bold);
        win.erase();
        win.refresh();

        // Draw RED bounding box

        win.attrset(ColorPair(2));
        win.draw_box(' ', ' ');
        win.refresh();

        // Display Australia map

        win.attrset(Attribute::Bold);

        for (i, s) in AUS_MAP.into_iter().enumerate() {
            win.mvaddstr(i as i32 + 1, 8, s);
            win.refresh();
            napms(100);
        }

        init_pair(5, COLOR_BLUE, COLOR_WHITE);
        win.attrset(ColorPair(5) | Attribute::Blink);
        win.mvaddstr(height - 2, 3, " pancurses - Linux, Win32");
        win.refresh();

        // Draw running messages

        init_pair(6, COLOR_BLACK, COLOR_WHITE);
        win.attrset(ColorPair(6));
        let w = width - 2;
        win.nodelay(true);

        // jbuhler's re-hacked scrolling messages

        for message in &MESSAGES {
            let msg_len = message.len() as i32;
            let mut visbuf = String::with_capacity(w as usize);
            let mut stop = false;

            for i in (0..w + msg_len).rev() {
                visbuf.clear();
                for visbuf_i in 0..visbuf.capacity() {
                    let i = i - msg_len as i32;
                    let char_index = visbuf_i as i32 - i;
                    let ch = if char_index >= 0 && char_index < message.len() as i32 {
                        let char_index = char_index as usize;
                        match message[char_index..char_index + 1].chars().next() {
                            Some(c) => c,
                            None => ' ',
                        }
                    } else {
                        ' '
                    };
                    visbuf.push(ch);
                }

                win.mvaddstr(height / 2, 1, &visbuf);
                win.refresh();

                if win.getch().is_some() {
                    flushinp();
                    stop = true;
                    break;
                }

                napms(100);
            }

            if stop {
                break;
            }
        }

        // Draw running 'A's across in RED

        init_pair(7, COLOR_RED, COLOR_GREEN);
        win.attron(ColorPair(7));

        let mut save: [chtype; 80] = [0; 80];

        for (index, pos) in (2..width - 4).enumerate() {
            let ch = win.mvinch(5, pos);
            save[index] = ch;
            let ch = ch & 0x7F;
            win.mvaddch(5, pos, ch);
        }

        win.refresh();

        // Put a message up; wait for a key

        let i = height - 2;
        win.attrset(ColorPair(5));
        win.mvaddstr(i, 3, "   Type a key to continue or ESC to quit  ");
        win.refresh();

        if wait_for_user(&main_window) {
            break;
        }

        // Restore the old line

        win.attrset(Attribute::Normal);

        for (index, pos) in (2..width - 4).enumerate() {
            win.mvaddch(5, pos, save[index]);
        }

        win.refresh();

        bouncing_balls(&main_window, &win, &mut rng);

        // bouncing_balls() leaves a keystroke in the queue

        if wait_for_user(&main_window) {
            break;
        }
    }

    endwin();
}

fn wait_for_user(main_window: &Window) -> bool {
    main_window.nodelay(true);
    half_delay(50);

    let ch = main_window.getch();

    main_window.nodelay(false);
    nocbreak(); // Reset the halfdelay() value
    cbreak();

    match ch {
        Some(Input::Character('\x1B')) => true,
        _ => false,
    }
}

fn sub_win_test(main_window: &Window, win: &Window) -> Result<(), i32> {
    win.attrset(Attribute::Normal);
    let (h, w) = win.get_max_yx();
    let (by, bx) = win.get_beg_yx();

    let sw = w / 3;
    let sh = h / 3;

    let swin1 = try!(win.derwin(sh, sw, 3, 5));
    let swin2 = try!(win.subwin(sh, sw, by + 4, bx + 8));
    let swin3 = try!(win.subwin(sh, sw, by + 5, bx + 11));

    init_pair(8, COLOR_RED, COLOR_BLUE);
    swin1.bkgd(COLOR_PAIR(8));
    swin1.erase();
    swin1.mvaddstr(0, 3, "Sub-window 1");
    swin1.refresh();

    init_pair(9, COLOR_CYAN, COLOR_MAGENTA);
    swin2.bkgd(COLOR_PAIR(9));
    swin2.erase();
    swin2.mvaddstr(0, 3, "Sub-window 2");
    swin2.refresh();

    init_pair(10, COLOR_YELLOW, COLOR_GREEN);
    swin3.bkgd(COLOR_PAIR(10));
    swin3.erase();
    swin3.mvaddstr(0, 3, "Sub-window 3");
    swin3.refresh();

    swin1.delwin();
    swin2.delwin();
    swin3.delwin();

    wait_for_user(main_window);

    Ok(())
}

fn bouncing_balls<T: Rng>(main_window: &Window, win: &Window, rng: &mut T) {
    curs_set(0);

    win.bkgd(COLOR_PAIR(1));
    win.refresh();
    win.attrset(Attribute::Normal);

    init_pair(11, COLOR_RED, COLOR_GREEN);
    init_pair(12, COLOR_BLUE, COLOR_RED);
    init_pair(13, COLOR_YELLOW, COLOR_WHITE);

    let ball1 = 'O' as chtype | COLOR_PAIR(11);
    let ball2 = '*' as chtype | COLOR_PAIR(12);
    let ball3 = '@' as chtype | COLOR_PAIR(13);

    let (h, w) = win.get_max_yx();

    let mut x1 = 2 + (rng.gen::<i32>() % (w - 4)).abs();
    let mut y1 = 2 + (rng.gen::<i32>() % (h - 4)).abs();
    let mut x2 = 2 + (rng.gen::<i32>() % (w - 4)).abs();
    let mut y2 = 2 + (rng.gen::<i32>() % (h - 4)).abs();
    let mut x3 = 2 + (rng.gen::<i32>() % (w - 4)).abs();
    let mut y3 = 2 + (rng.gen::<i32>() % (h - 4)).abs();

    let mut xd1 = 1;
    let mut yd1 = 1;
    let mut xd2 = 1;
    let mut yd2 = -1;
    let mut xd3 = -1;
    let mut yd3 = 1;

    main_window.nodelay(true);

    let mut c = main_window.getch();
    while c.is_none() {
        x1 += xd1;
        if x1 <= 1 || x1 >= w - 2 {
            xd1 *= -1;
        }

        y1 += yd1;
        if y1 <= 1 || y1 >= h - 2 {
            yd1 *= -1;
        }

        x2 += xd2;
        if x2 <= 1 || x2 >= w - 2 {
            xd2 *= -1;
        }

        y2 += yd2;
        if y2 <= 1 || y2 >= h - 2 {
            yd2 *= -1;
        }

        x3 += xd3;
        if x3 <= 1 || x3 >= w - 2 {
            xd3 *= -1;
        }

        y3 += yd3;
        if y3 <= 1 || y3 >= h - 2 {
            yd3 *= -1;
        }

        let c1 = win.mvinch(y1, x1);
        let c2 = win.mvinch(y2, x2);
        let c3 = win.mvinch(y3, x3);

        win.mvaddch(y1, x1, ball1);
        win.mvaddch(y2, x2, ball2);
        win.mvaddch(y3, x3, ball3);

        win.mv(0, 0);
        win.refresh();

        win.mvaddch(y1, x1, c1);
        win.mvaddch(y2, x2, c2);
        win.mvaddch(y3, x3, c3);

        napms(150);
        c = main_window.getch();
    }

    main_window.nodelay(false);
    c.map(|c| main_window.ungetch(&c));
}
<commit_msg>Removed unnecessary 'statics<commit_after>extern crate pancurses;
extern crate rand;

use pancurses::*;

use rand::Rng;

const AUS_MAP: [&str; 13] = [
    "                       A ",
    "           AA         AA ",
    "    N.T. AAAAA       AAAA ",
    "     AAAAAAAAAAA  AAAAAAAA ",
    "   AAAAAAAAAAAAAAAAAAAAAAAAA Qld.",
    " AAAAAAAAAAAAAAAAAAAAAAAAAAAA ",
    " AAAAAAAAAAAAAAAAAAAAAAAAAAAAA ",
    " AAAAAAAAAAAAAAAAAAAAAAAAAAAA ",
    "   AAAAAAAAAAAAAAAAAAAAAAAAA N.S.W.",
    "W.A. AAAAAAAAA      AAAAAA Vic.",
    "       AAA   S.A.     AA",
    "                       A  Tas.",
    "",
];

const MESSAGES: [&str; 6] = [
    "Hello from the Land Down Under",
    "The Land of crocs, and a big Red Rock",
    "Where the sunflower runs along the highways",
    "The dusty red roads lead one to loneliness",
    "Blue sky in the morning and",
    "Freezing nights and twinkling stars",
];

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

    start_color();

    use_default_colors();

    cbreak();
    noecho();

    curs_set(0);

    // Refresh stdscr so that reading from it will not cause it to  overwrite the other windows
    // that are being created
    main_window.refresh();

    // Create a drawing window
    let width = 48;
    let height = 15;

    let win = newwin(
        height,
        width,
        (main_window.get_max_y() - height) / 2,
        (main_window.get_max_x() - width) / 2,
    );

    loop {
        init_pair(1, COLOR_WHITE, COLOR_BLUE);
        win.bkgd(COLOR_PAIR(1));
        win.erase();

        init_pair(2, COLOR_RED, COLOR_RED);
        win.attrset(ColorPair(2));
        win.draw_box(' ', ' ');
        win.refresh();

        win.attrset(Attribute::Normal);

        // Do random output of a character

        let mut ch = 'a';

        main_window.nodelay(true);

        let mut rng = rand::thread_rng();

        for i in 0..5000 {
            let x = (rng.gen::<i32>() % (width - 2)).abs() + 1;
            let y = (rng.gen::<i32>() % (height - 2)).abs() + 1;

            assert!(x != 0);
            assert!(y != 0);
            win.mvaddch(y, x, ch);
            win.refresh();

            if main_window.getch().is_some() {
                break;
            }

            if i == 2000 {
                ch = 'b';
                init_pair(3, COLOR_CYAN, COLOR_YELLOW);
                win.attrset(ColorPair(3));
            }
        }

        main_window.nodelay(false);

        sub_win_test(&main_window, &win).unwrap();

        // Erase and draw green window

        init_pair(4, COLOR_YELLOW, COLOR_GREEN);
        win.bkgd(COLOR_PAIR(4));
        win.attrset(Attribute::Bold);
        win.erase();
        win.refresh();

        // Draw RED bounding box

        win.attrset(ColorPair(2));
        win.draw_box(' ', ' ');
        win.refresh();

        // Display Australia map

        win.attrset(Attribute::Bold);

        for (i, s) in AUS_MAP.into_iter().enumerate() {
            win.mvaddstr(i as i32 + 1, 8, s);
            win.refresh();
            napms(100);
        }

        init_pair(5, COLOR_BLUE, COLOR_WHITE);
        win.attrset(ColorPair(5) | Attribute::Blink);
        win.mvaddstr(height - 2, 3, " pancurses - Linux, Win32");
        win.refresh();

        // Draw running messages

        init_pair(6, COLOR_BLACK, COLOR_WHITE);
        win.attrset(ColorPair(6));
        let w = width - 2;
        win.nodelay(true);

        // jbuhler's re-hacked scrolling messages

        for message in &MESSAGES {
            let msg_len = message.len() as i32;
            let mut visbuf = String::with_capacity(w as usize);
            let mut stop = false;

            for i in (0..w + msg_len).rev() {
                visbuf.clear();
                for visbuf_i in 0..visbuf.capacity() {
                    let i = i - msg_len as i32;
                    let char_index = visbuf_i as i32 - i;
                    let ch = if char_index >= 0 && char_index < message.len() as i32 {
                        let char_index = char_index as usize;
                        match message[char_index..char_index + 1].chars().next() {
                            Some(c) => c,
                            None => ' ',
                        }
                    } else {
                        ' '
                    };
                    visbuf.push(ch);
                }

                win.mvaddstr(height / 2, 1, &visbuf);
                win.refresh();

                if win.getch().is_some() {
                    flushinp();
                    stop = true;
                    break;
                }

                napms(100);
            }

            if stop {
                break;
            }
        }

        // Draw running 'A's across in RED

        init_pair(7, COLOR_RED, COLOR_GREEN);
        win.attron(ColorPair(7));

        let mut save: [chtype; 80] = [0; 80];

        for (index, pos) in (2..width - 4).enumerate() {
            let ch = win.mvinch(5, pos);
            save[index] = ch;
            let ch = ch & 0x7F;
            win.mvaddch(5, pos, ch);
        }

        win.refresh();

        // Put a message up; wait for a key

        let i = height - 2;
        win.attrset(ColorPair(5));
        win.mvaddstr(i, 3, "   Type a key to continue or ESC to quit  ");
        win.refresh();

        if wait_for_user(&main_window) {
            break;
        }

        // Restore the old line

        win.attrset(Attribute::Normal);

        for (index, pos) in (2..width - 4).enumerate() {
            win.mvaddch(5, pos, save[index]);
        }

        win.refresh();

        bouncing_balls(&main_window, &win, &mut rng);

        // bouncing_balls() leaves a keystroke in the queue

        if wait_for_user(&main_window) {
            break;
        }
    }

    endwin();
}

fn wait_for_user(main_window: &Window) -> bool {
    main_window.nodelay(true);
    half_delay(50);

    let ch = main_window.getch();

    main_window.nodelay(false);
    nocbreak(); // Reset the halfdelay() value
    cbreak();

    match ch {
        Some(Input::Character('\x1B')) => true,
        _ => false,
    }
}

fn sub_win_test(main_window: &Window, win: &Window) -> Result<(), i32> {
    win.attrset(Attribute::Normal);
    let (h, w) = win.get_max_yx();
    let (by, bx) = win.get_beg_yx();

    let sw = w / 3;
    let sh = h / 3;

    let swin1 = try!(win.derwin(sh, sw, 3, 5));
    let swin2 = try!(win.subwin(sh, sw, by + 4, bx + 8));
    let swin3 = try!(win.subwin(sh, sw, by + 5, bx + 11));

    init_pair(8, COLOR_RED, COLOR_BLUE);
    swin1.bkgd(COLOR_PAIR(8));
    swin1.erase();
    swin1.mvaddstr(0, 3, "Sub-window 1");
    swin1.refresh();

    init_pair(9, COLOR_CYAN, COLOR_MAGENTA);
    swin2.bkgd(COLOR_PAIR(9));
    swin2.erase();
    swin2.mvaddstr(0, 3, "Sub-window 2");
    swin2.refresh();

    init_pair(10, COLOR_YELLOW, COLOR_GREEN);
    swin3.bkgd(COLOR_PAIR(10));
    swin3.erase();
    swin3.mvaddstr(0, 3, "Sub-window 3");
    swin3.refresh();

    swin1.delwin();
    swin2.delwin();
    swin3.delwin();

    wait_for_user(main_window);

    Ok(())
}

fn bouncing_balls<T: Rng>(main_window: &Window, win: &Window, rng: &mut T) {
    curs_set(0);

    win.bkgd(COLOR_PAIR(1));
    win.refresh();
    win.attrset(Attribute::Normal);

    init_pair(11, COLOR_RED, COLOR_GREEN);
    init_pair(12, COLOR_BLUE, COLOR_RED);
    init_pair(13, COLOR_YELLOW, COLOR_WHITE);

    let ball1 = 'O' as chtype | COLOR_PAIR(11);
    let ball2 = '*' as chtype | COLOR_PAIR(12);
    let ball3 = '@' as chtype | COLOR_PAIR(13);

    let (h, w) = win.get_max_yx();

    let mut x1 = 2 + (rng.gen::<i32>() % (w - 4)).abs();
    let mut y1 = 2 + (rng.gen::<i32>() % (h - 4)).abs();
    let mut x2 = 2 + (rng.gen::<i32>() % (w - 4)).abs();
    let mut y2 = 2 + (rng.gen::<i32>() % (h - 4)).abs();
    let mut x3 = 2 + (rng.gen::<i32>() % (w - 4)).abs();
    let mut y3 = 2 + (rng.gen::<i32>() % (h - 4)).abs();

    let mut xd1 = 1;
    let mut yd1 = 1;
    let mut xd2 = 1;
    let mut yd2 = -1;
    let mut xd3 = -1;
    let mut yd3 = 1;

    main_window.nodelay(true);

    let mut c = main_window.getch();
    while c.is_none() {
        x1 += xd1;
        if x1 <= 1 || x1 >= w - 2 {
            xd1 *= -1;
        }

        y1 += yd1;
        if y1 <= 1 || y1 >= h - 2 {
            yd1 *= -1;
        }

        x2 += xd2;
        if x2 <= 1 || x2 >= w - 2 {
            xd2 *= -1;
        }

        y2 += yd2;
        if y2 <= 1 || y2 >= h - 2 {
            yd2 *= -1;
        }

        x3 += xd3;
        if x3 <= 1 || x3 >= w - 2 {
            xd3 *= -1;
        }

        y3 += yd3;
        if y3 <= 1 || y3 >= h - 2 {
            yd3 *= -1;
        }

        let c1 = win.mvinch(y1, x1);
        let c2 = win.mvinch(y2, x2);
        let c3 = win.mvinch(y3, x3);

        win.mvaddch(y1, x1, ball1);
        win.mvaddch(y2, x2, ball2);
        win.mvaddch(y3, x3, ball3);

        win.mv(0, 0);
        win.refresh();

        win.mvaddch(y1, x1, c1);
        win.mvaddch(y2, x2, c2);
        win.mvaddch(y3, x3, c3);

        napms(150);
        c = main_window.getch();
    }

    main_window.nodelay(false);
    c.map(|c| main_window.ungetch(&c));
}
<|endoftext|>


<commit_before>#[macro_use] extern crate qregister;
pub use qregister::Register;
use std::any::Any;

pub trait Physics: Any {
    fn progress(&mut self, dt: f32);
}

#[derive(Default)]
pub struct NPhysicsPhysics;

impl Physics for NPhysicsPhysics {
    fn progress(&mut self, _: f32) {
        println!("progressing nphysics-physics subsystem...");
    }
}

#[derive(Default)]
pub struct LegacyPhysics;

impl Physics for LegacyPhysics {
    fn progress(&mut self, _: f32) {
        println!("progressing legacy-physics subsystem...");
    }
}

qregister_load_fns!{
    fn load_module(&mut Register) => {

        alternative(Physics) NPhysicsPhysics;

        option Physics;

        alternative(Physics) LegacyPhysics;
    }
}

fn main(){
    let mut reg = Register::new();

    self::load_module(&mut reg);

    reg.wire_alternative("Physics", "NPhysicsPhysics");

    reg.objects.get_mut::<Box<Physics>>().unwrap().progress(1.0);

    reg.wire_alternative("Physics", "LegacyPhysics");

    reg.objects.get_mut::<Box<Physics>>().unwrap().progress(1.0);
}
<commit_msg>Update physics.rs<commit_after>#[macro_use] extern crate qregister;
pub use qregister::Register;
use std::any::Any;

pub trait Physics: Any {
    fn progress(&mut self, dt: f32);
}

#[derive(Default)]
pub struct NPhysicsPhysics;

impl Physics for NPhysicsPhysics {
    fn progress(&mut self, _: f32) {
        println!("progressing nphysics-physics subsystem...");
    }
}

#[derive(Default)]
pub struct LegacyPhysics;

impl Physics for LegacyPhysics {
    fn progress(&mut self, _: f32) {
        println!("progressing legacy-physics subsystem...");
    }
}

qregister_load_fns!{
    fn load_module(&mut Register) => {

        option Physics;

        alternative(Physics) NPhysicsPhysics;

        alternative(Physics) LegacyPhysics;
    }
}

fn main(){
    let mut reg = Register::new();

    self::load_module(&mut reg);

    reg.wire_alternative("Physics", "NPhysicsPhysics");

    reg.objects.get_mut::<Box<Physics>>().unwrap().progress(1.0);

    reg.wire_alternative("Physics", "LegacyPhysics");

    reg.objects.get_mut::<Box<Physics>>().unwrap().progress(1.0);
}
<|endoftext|>


<commit_before>use std::f64::consts::PI;

use turtle::Turtle;

fn main() {
    let mut turtle = Turtle::new();

    turtle.pen_up();
    turtle.backward(280.0);
    turtle.left(90.0);
    turtle.pen_down();

    body(&mut turtle);
    top_hat(&mut turtle);
    face(&mut turtle);

    turtle.hide();
}

fn body(turtle: &mut Turtle) {
    for &radius in &[120.0, 80.0, 60.0] {
        circle(turtle, radius);

        turtle.pen_up();
        sidestep_right(turtle, radius * 2.0);
        turtle.pen_down();
    }
}

fn top_hat(turtle: &mut Turtle) {
    turtle.set_fill_color("black");

    turtle.pen_up();
    sidestep_left(turtle, 10.0);
    turtle.pen_down();

    turtle.begin_fill();
    rectangle_bottom_center(turtle, 100.0, 10.0);
    turtle.end_fill();

    turtle.pen_up();
    sidestep_right(turtle, 10.0);
    turtle.pen_down();

    turtle.begin_fill();
    rectangle_bottom_center(turtle, 60.0, 40.0);
    turtle.end_fill();
}

fn face(turtle: &mut Turtle) {
    nose(turtle);
    eyes(turtle);
    smile(turtle);
}

fn nose(turtle: &mut Turtle) {
    // setup to draw nose
    turtle.pen_up();
    sidestep_left(turtle, 50.0);
    turtle.left(20.0);
    turtle.pen_down();

    // carrot nose
    turtle.set_fill_color("orange");
    turtle.begin_fill();
    turtle.forward(40.0);
    turtle.left(160.0);
    turtle.forward(40.0);
    turtle.end_fill();
}

fn eyes(turtle: &mut Turtle) {
    // setup to draw left eye
    turtle.pen_up();
    turtle.left(40.0);
    turtle.forward(25.0);
    turtle.left(140.0);
    turtle.pen_down();

    // left eye
    turtle.set_fill_color("black");
    turtle.begin_fill();
    circle(turtle, 5.0);
    turtle.end_fill();

    // setup for right eye
    turtle.pen_up();
    turtle.forward(40.0);
    turtle.pen_down();

    // right eye
    turtle.set_fill_color("black");
    turtle.begin_fill();
    circle(turtle, 5.0);
    turtle.end_fill();
}

fn smile(turtle: &mut Turtle) {
    // setup for start of smile
    turtle.pen_up();
    turtle.left(80.0);
    turtle.forward(35.0);
    turtle.left(80.0);
    turtle.pen_down();

    // draw arc for smile
    for _ in 0..25 {
        turtle.forward(2.0);
        turtle.left(2.0);
    }
}

/// Moves the turtle in the direction of its right side, retaining its original direction at the
/// end. Essentially a "sidestep"
fn sidestep_right(turtle: &mut Turtle, length: f64) {
    turtle.right(90.0);
    turtle.forward(length);
    turtle.left(90.0);
}

/// Moves the turtle in the direction of its left side, retaining its original direction at the
/// end. Essentially a "sidestep"
fn sidestep_left(turtle: &mut Turtle, length: f64) {
    turtle.left(90.0);
    turtle.forward(length);
    turtle.right(90.0);
}

/// Draws a rectangle starting at the bottom center point
fn rectangle_bottom_center(turtle: &mut Turtle, width: f64, height: f64) {
    turtle.forward(width/2.0);
    for &length in &[height, width, height] {
        turtle.right(90.0);
        turtle.forward(length);
    }
    turtle.right(90.0);
    turtle.forward(width/2.0);
}

fn circle(turtle: &mut Turtle, radius: f64) {
    arc(turtle, radius, 360.0)
}

fn arc(turtle: &mut Turtle, radius: f64, extent: f64) {
    let steps = 180.0;

    let circumference = 2.0 * PI * radius;
    let step = circumference / steps;
    let rotation = extent / steps;

    for _ in 0..steps as i32 {
        turtle.forward(step);
        turtle.right(rotation);
    }
}
<commit_msg>Adding arms to the snowman example<commit_after>use std::f64::consts::PI;

use turtle::Turtle;

fn main() {
    let mut turtle = Turtle::new();

    turtle.pen_up();
    turtle.backward(280.0);
    turtle.left(90.0);
    turtle.pen_down();

    body(&mut turtle);
    top_hat(&mut turtle);
    face(&mut turtle);

    turtle.hide();
}

fn body(turtle: &mut Turtle) {
    circle(turtle, 120.0);
    turtle.pen_up();
    sidestep_right(turtle, 120.0 * 2.0);
    turtle.pen_down();

    // Second circle is drawn in parts because we want the arms to be there too
    arc(turtle, 80.0, 120.0, 60);
    turtle.left(90.0);
    arm(turtle);
    turtle.right(90.0);
    arc(turtle, 80.0, 120.0, 60);
    turtle.left(90.0);
    arm(turtle);
    turtle.right(90.0);
    arc(turtle, 80.0, 120.0, 60);
    turtle.pen_up();
    sidestep_right(turtle, 80.0 * 2.0);
    turtle.pen_down();

    circle(turtle, 60.0);
    turtle.pen_up();
    sidestep_right(turtle, 60.0 * 2.0);
    turtle.pen_down();
}

fn arm(turtle: &mut Turtle) {
    let pen_color = turtle.pen_color();
    turtle.set_pen_color("brown");

    turtle.forward(150.0);

    turtle.pen_up();
    turtle.backward(40.0);
    turtle.left(25.0);
    turtle.pen_down();

    turtle.forward(60.0);

    turtle.pen_up();
    turtle.backward(60.0);
    turtle.right(25.0);
    turtle.backward(55.0);
    turtle.right(20.0);
    turtle.pen_down();

    turtle.forward(40.0);

    turtle.pen_up();
    turtle.backward(40.0);
    turtle.left(20.0);
    turtle.backward(55.0);
    turtle.pen_down();

    turtle.set_pen_color(pen_color);
}

fn top_hat(turtle: &mut Turtle) {
    turtle.set_fill_color("black");

    turtle.pen_up();
    sidestep_left(turtle, 10.0);
    turtle.pen_down();

    turtle.begin_fill();
    rectangle_bottom_center(turtle, 100.0, 10.0);
    turtle.end_fill();

    turtle.pen_up();
    sidestep_right(turtle, 10.0);
    turtle.pen_down();

    turtle.begin_fill();
    rectangle_bottom_center(turtle, 60.0, 40.0);
    turtle.end_fill();
}

fn face(turtle: &mut Turtle) {
    nose(turtle);
    eyes(turtle);
    smile(turtle);
}

fn nose(turtle: &mut Turtle) {
    // setup to draw nose
    turtle.pen_up();
    sidestep_left(turtle, 50.0);
    turtle.left(20.0);
    turtle.pen_down();

    // carrot nose
    turtle.set_fill_color("orange");
    turtle.begin_fill();
    turtle.forward(40.0);
    turtle.left(160.0);
    turtle.forward(40.0);
    turtle.end_fill();
}

fn eyes(turtle: &mut Turtle) {
    // setup to draw left eye
    turtle.pen_up();
    turtle.left(40.0);
    turtle.forward(25.0);
    turtle.left(140.0);
    turtle.pen_down();

    // left eye
    turtle.set_fill_color("black");
    turtle.begin_fill();
    circle(turtle, 5.0);
    turtle.end_fill();

    // setup for right eye
    turtle.pen_up();
    turtle.forward(40.0);
    turtle.pen_down();

    // right eye
    turtle.set_fill_color("black");
    turtle.begin_fill();
    circle(turtle, 5.0);
    turtle.end_fill();
}

fn smile(turtle: &mut Turtle) {
    // setup for start of smile
    turtle.pen_up();
    turtle.left(80.0);
    turtle.forward(35.0);
    turtle.left(80.0);
    turtle.pen_down();

    // draw arc for smile
    for _ in 0..25 {
        turtle.forward(2.0);
        turtle.left(2.0);
    }
}

/// Moves the turtle in the direction of its right side, retaining its original direction at the
/// end. Essentially a "sidestep"
fn sidestep_right(turtle: &mut Turtle, length: f64) {
    turtle.right(90.0);
    turtle.forward(length);
    turtle.left(90.0);
}

/// Moves the turtle in the direction of its left side, retaining its original direction at the
/// end. Essentially a "sidestep"
fn sidestep_left(turtle: &mut Turtle, length: f64) {
    turtle.left(90.0);
    turtle.forward(length);
    turtle.right(90.0);
}

/// Draws a rectangle starting at the bottom center point
fn rectangle_bottom_center(turtle: &mut Turtle, width: f64, height: f64) {
    turtle.forward(width/2.0);
    for &length in &[height, width, height] {
        turtle.right(90.0);
        turtle.forward(length);
    }
    turtle.right(90.0);
    turtle.forward(width/2.0);
}

fn circle(turtle: &mut Turtle, radius: f64) {
    arc(turtle, radius, 360.0, 180)
}

fn arc(turtle: &mut Turtle, radius: f64, extent: f64, steps: u32) {
    let circumference = 2.0 * PI * radius;
    let distance = circumference * extent / 360.0;
    let step = distance / steps as f64;
    let rotation = extent / steps as f64;

    for _ in 0..steps {
        turtle.forward(step);
        turtle.right(rotation);
    }
}
<|endoftext|>


<commit_before>use clap::Parser;
use dicom_core::{dicom_value, smallvec};
use dicom_core::{DataElement, PrimitiveValue, VR};
use dicom_dictionary_std::tags;
use dicom_dump::DumpOptions;
use dicom_encoding::transfer_syntax;
use dicom_object::{mem::InMemDicomObject, open_file, StandardDataDictionary};
use dicom_transfer_syntax_registry::{entries, TransferSyntaxRegistry};
use dicom_ul::pdu::Pdu;
use dicom_ul::{
    association::ClientAssociationOptions,
    pdu::{PDataValue, PDataValueType},
};
use query::parse_queries;
use smallvec::smallvec;
use snafu::prelude::*;
use std::io::{stderr, Read};
use std::path::PathBuf;
use tracing::{debug, error, info, warn, Level};
use transfer_syntax::TransferSyntaxIndex;

mod query;

/// DICOM C-FIND SCU
#[derive(Debug, Parser)]
struct App {
    /// socket address to FIND SCP (example: "127.0.0.1:1045")
    addr: String,
    /// a DICOM file representing the query object
    file: Option<PathBuf>,
    /// sequence of queries
    #[clap(short('q'))]
    query: Vec<String>,

    /// verbose mode
    #[clap(short = 'v', long = "verbose")]
    verbose: bool,
    /// the calling AE title
    #[clap(long = "calling-ae-title", default_value = "FIND-SCU")]
    calling_ae_title: String,
    /// the called AE title
    #[clap(long = "called-ae-title")]
    called_ae_title: Option<String>,
    /// the maximum PDU length
    #[clap(long = "max-pdu-length", default_value = "16384")]
    max_pdu_length: u32,

    /// use patient root information model
    #[clap(short = 'P', long, conflicts_with = "study")]
    patient: bool,
    /// use study root information model (default)
    #[clap(short = 'S', long, conflicts_with = "patient")]
    study: bool,
}

fn main() {
    run().unwrap_or_else(|err| {
        error!("{}", snafu::Report::from_error(err));
        std::process::exit(-2);
    });
}

#[derive(Debug, Snafu)]
enum Error {
    /// Could not initialize SCU
    InitScu {
        source: dicom_ul::association::client::Error,
    },

    /// Could not construct DICOM command
    CreateCommand { source: dicom_object::Error },

    /// Could not read DICOM command
    ReadCommand { source: dicom_object::Error },

    /// Could not dump DICOM output
    DumpOutput { source: std::io::Error },

    #[snafu(whatever, display("{}", message))]
    Other {
        message: String,
        #[snafu(source(from(Box<dyn std::error::Error + 'static>, Some)))]
        source: Option<Box<dyn std::error::Error + 'static>>,
    },
}

fn build_query(
    file: Option<PathBuf>,
    q: Vec<String>,
    patient: bool,
    study: bool,
    verbose: bool,
) -> Result<InMemDicomObject, Error> {
    match (file, q) {
        (Some(file), q) => {
            if !q.is_empty() {
                whatever!("Conflicted file with query terms");
            }

            if verbose {
                info!("Opening file '{}'...", file.display());
            }

            open_file(file)
                .context(CreateCommandSnafu)
                .map(|file| file.into_inner())
        }
        (None, q) => {
            if q.is_empty() {
                whatever!("Query not specified");
            }

            let mut obj =
                parse_queries(&q).whatever_context("Could not build query object from terms")?;

            // (0008,0052) CS QueryRetrieveLevel
            let level = match (patient, study) {
                (true, false) => "PATIENT",
                (false, true) | (false, false) => "STUDY",
                _ => unreachable!(),
            };
            obj.put(DataElement::new(
                tags::QUERY_RETRIEVE_LEVEL,
                VR::CS,
                PrimitiveValue::from(level),
            ));

            Ok(obj)
        }
    }
}

fn run() -> Result<(), Error> {
    let App {
        addr,
        file,
        verbose,
        calling_ae_title,
        called_ae_title,
        max_pdu_length,
        patient,
        study,
        query,
    } = App::from_args();

    tracing::subscriber::set_global_default(
        tracing_subscriber::FmtSubscriber::builder()
            .with_max_level(if verbose { Level::DEBUG } else { Level::INFO })
            .finish(),
    )
    .unwrap_or_else(|e| {
        error!("{}", snafu::Report::from_error(e));
    });

    let dcm_query = build_query(file, query, patient, study, verbose)?;

    let abstract_syntax = match (patient, study) {
        // Patient Root Query/Retrieve Information Model - FIND
        (true, false) => "1.2.840.10008.5.1.4.1.2.1.1",
        // Study Root Query/Retrieve Information Model – FIND (default)
        (false, false) | (false, true) => "1.2.840.10008.5.1.4.1.2.2.1",
        // Series
        _ => unreachable!("Unexpected flag combination"),
    };

    if verbose {
        info!("Establishing association with '{}'...", &addr);
    }

    let mut scu_opt = ClientAssociationOptions::new()
        .with_abstract_syntax(abstract_syntax)
        .calling_ae_title(calling_ae_title)
        .max_pdu_length(max_pdu_length);

    if let Some(called_ae_title) = called_ae_title {
        scu_opt = scu_opt.called_ae_title(called_ae_title);
    }

    let mut scu = scu_opt.establish_with(&addr).context(InitScuSnafu)?;

    if verbose {
        info!("Association established");
    }

    let pc_selected = if let Some(pc_selected) = scu.presentation_contexts().first() {
        pc_selected
    } else {
        error!("Could not choose a presentation context");
        let _ = scu.abort();
        std::process::exit(-2);
    };
    let pc_selected_id = pc_selected.id;

    let ts = if let Some(ts) = TransferSyntaxRegistry.get(&pc_selected.transfer_syntax) {
        ts
    } else {
        error!("Poorly negotiated transfer syntax");
        let _ = scu.abort();
        std::process::exit(-2);
    };

    if verbose {
        debug!("Transfer Syntax: {}", ts.name());
    }

    let cmd = find_req_command(abstract_syntax, 1);

    let mut cmd_data = Vec::with_capacity(128);
    cmd.write_dataset_with_ts(&mut cmd_data, &entries::IMPLICIT_VR_LITTLE_ENDIAN.erased())
        .whatever_context("Failed to write command")?;

    let implicit_vr_le = entries::IMPLICIT_VR_LITTLE_ENDIAN.erased();

    let mut iod_data = Vec::with_capacity(128);
    dcm_query
        .write_dataset_with_ts(&mut iod_data, &implicit_vr_le)
        .whatever_context("failed to write identifier dataset")?;

    let nbytes = cmd_data.len() + iod_data.len();

    if verbose {
        debug!("Sending query ({} B)...", nbytes);
    }

    let pdu = Pdu::PData {
        data: vec![PDataValue {
            presentation_context_id: pc_selected_id,
            value_type: PDataValueType::Command,
            is_last: true,
            data: cmd_data,
        }],
    };
    scu.send(&pdu).whatever_context("Could not send command")?;

    let pdu = Pdu::PData {
        data: vec![PDataValue {
            presentation_context_id: pc_selected_id,
            value_type: PDataValueType::Data,
            is_last: true,
            data: iod_data,
        }],
    };
    scu.send(&pdu)
        .whatever_context("Could not send C-Find request")?;

    if verbose {
        debug!("Awaiting response...");
    }

    let mut i = 0;
    loop {
        let rsp_pdu = scu
            .receive()
            .whatever_context("Failed to receive response from remote node")?;

        match rsp_pdu {
            Pdu::PData { data } => {
                let data_value = &data[0];

                let cmd_obj = InMemDicomObject::read_dataset_with_ts(
                    &data_value.data[..],
                    &entries::IMPLICIT_VR_LITTLE_ENDIAN.erased(),
                )
                .context(ReadCommandSnafu)?;
                if verbose {
                    eprintln!("Match #{} Response command:", i);
                    DumpOptions::new()
                        .dump_object_to(stderr(), &cmd_obj)
                        .context(DumpOutputSnafu)?;
                }
                let status = cmd_obj
                    .element(tags::STATUS)
                    .whatever_context("status code from response is missing")?
                    .to_int::<u16>()
                    .whatever_context("failed to read status code")?;
                if status == 0 {
                    if verbose {
                        debug!("Matching is complete");
                    }
                    if i == 0 {
                        info!("No results matching query");
                    }
                    break;
                } else if status == 0xFF00 || status == 0xFF01 {
                    if verbose {
                        debug!("Operation pending: {:x}", status);
                    }

                    // fetch DICOM data

                    let dcm = {
                        let mut rsp = scu.receive_pdata();
                        let mut response_data = Vec::new();
                        rsp.read_to_end(&mut response_data)
                            .whatever_context("Failed to read response data")?;

                        InMemDicomObject::read_dataset_with_ts(&response_data[..], &implicit_vr_le)
                            .whatever_context("Could not read response data set")?
                    };

                    println!(
                        "------------------------ Match #{} ------------------------",
                        i
                    );
                    DumpOptions::new()
                        .dump_object(&dcm)
                        .context(DumpOutputSnafu)?;
                    i += 1;
                } else {
                    warn!("Operation failed (status code {})", status);
                    break;
                }
            }

            pdu @ Pdu::Unknown { .. }
            | pdu @ Pdu::AssociationRQ { .. }
            | pdu @ Pdu::AssociationAC { .. }
            | pdu @ Pdu::AssociationRJ { .. }
            | pdu @ Pdu::ReleaseRQ
            | pdu @ Pdu::ReleaseRP
            | pdu @ Pdu::AbortRQ { .. } => {
                error!("Unexpected SCP response: {:?}", pdu);
                let _ = scu.abort();
                std::process::exit(-2);
            }
        }
    }
    let _ = scu.release();

    Ok(())
}

fn find_req_command(
    sop_class_uid: &str,
    message_id: u16,
) -> InMemDicomObject<StandardDataDictionary> {
    let mut obj = InMemDicomObject::new_empty();

    // group length
    obj.put(DataElement::new(
        tags::COMMAND_GROUP_LENGTH,
        VR::UL,
        PrimitiveValue::from(
            8 + even_len(sop_class_uid.len())   // SOP Class UID
            + 8 + 2 // command field
            + 8 + 2 // message ID
            + 8 + 2 // priority
            + 8 + 2, // data set type
        ),
    ));

    // SOP Class UID
    obj.put(DataElement::new(
        tags::AFFECTED_SOP_CLASS_UID,
        VR::UI,
        PrimitiveValue::from(sop_class_uid),
    ));

    // command field
    obj.put(DataElement::new(
        tags::COMMAND_FIELD,
        VR::US,
        // 0020H: C-FIND-RQ message
        dicom_value!(U16, [0x0020]),
    ));

    // message ID
    obj.put(DataElement::new(
        tags::MESSAGE_ID,
        VR::US,
        dicom_value!(U16, [message_id]),
    ));

    //priority
    obj.put(DataElement::new(
        tags::PRIORITY,
        VR::US,
        // medium
        dicom_value!(U16, [0x0000]),
    ));

    // data set type
    obj.put(DataElement::new(
        tags::COMMAND_DATA_SET_TYPE,
        VR::US,
        dicom_value!(U16, [0x0001]),
    ));

    obj
}

fn even_len(l: usize) -> u32 {
    ((l + 1) & !1) as u32
}

#[cfg(test)]
mod tests {
    use super::even_len;
    #[test]
    fn test_even_len() {
        assert_eq!(even_len(0), 0);
        assert_eq!(even_len(1), 2);
        assert_eq!(even_len(2), 2);
        assert_eq!(even_len(3), 4);
        assert_eq!(even_len(4), 4);
        assert_eq!(even_len(5), 6);
    }
}
<commit_msg>-fix use negotiated TS for wirting query dataset<commit_after>use clap::Parser;
use dicom_core::{dicom_value, smallvec};
use dicom_core::{DataElement, PrimitiveValue, VR};
use dicom_dictionary_std::tags;
use dicom_dump::DumpOptions;
use dicom_encoding::transfer_syntax;
use dicom_object::{mem::InMemDicomObject, open_file, StandardDataDictionary};
use dicom_transfer_syntax_registry::{entries, TransferSyntaxRegistry};
use dicom_ul::pdu::Pdu;
use dicom_ul::{
    association::ClientAssociationOptions,
    pdu::{PDataValue, PDataValueType},
};
use query::parse_queries;
use smallvec::smallvec;
use snafu::prelude::*;
use std::io::{stderr, Read};
use std::path::PathBuf;
use tracing::{debug, error, info, warn, Level};
use transfer_syntax::TransferSyntaxIndex;

mod query;

/// DICOM C-FIND SCU
#[derive(Debug, Parser)]
struct App {
    /// socket address to FIND SCP (example: "127.0.0.1:1045")
    addr: String,
    /// a DICOM file representing the query object
    file: Option<PathBuf>,
    /// sequence of queries
    #[clap(short('q'))]
    query: Vec<String>,

    /// verbose mode
    #[clap(short = 'v', long = "verbose")]
    verbose: bool,
    /// the calling AE title
    #[clap(long = "calling-ae-title", default_value = "FIND-SCU")]
    calling_ae_title: String,
    /// the called AE title
    #[clap(long = "called-ae-title")]
    called_ae_title: Option<String>,
    /// the maximum PDU length
    #[clap(long = "max-pdu-length", default_value = "16384")]
    max_pdu_length: u32,

    /// use patient root information model
    #[clap(short = 'P', long, conflicts_with = "study")]
    patient: bool,
    /// use study root information model (default)
    #[clap(short = 'S', long, conflicts_with = "patient")]
    study: bool,
}

fn main() {
    run().unwrap_or_else(|err| {
        error!("{}", snafu::Report::from_error(err));
        std::process::exit(-2);
    });
}

#[derive(Debug, Snafu)]
enum Error {
    /// Could not initialize SCU
    InitScu {
        source: dicom_ul::association::client::Error,
    },

    /// Could not construct DICOM command
    CreateCommand { source: dicom_object::Error },

    /// Could not read DICOM command
    ReadCommand { source: dicom_object::Error },

    /// Could not dump DICOM output
    DumpOutput { source: std::io::Error },

    #[snafu(whatever, display("{}", message))]
    Other {
        message: String,
        #[snafu(source(from(Box<dyn std::error::Error + 'static>, Some)))]
        source: Option<Box<dyn std::error::Error + 'static>>,
    },
}

fn build_query(
    file: Option<PathBuf>,
    q: Vec<String>,
    patient: bool,
    study: bool,
    verbose: bool,
) -> Result<InMemDicomObject, Error> {
    match (file, q) {
        (Some(file), q) => {
            if !q.is_empty() {
                whatever!("Conflicted file with query terms");
            }

            if verbose {
                info!("Opening file '{}'...", file.display());
            }

            open_file(file)
                .context(CreateCommandSnafu)
                .map(|file| file.into_inner())
        }
        (None, q) => {
            if q.is_empty() {
                whatever!("Query not specified");
            }

            let mut obj =
                parse_queries(&q).whatever_context("Could not build query object from terms")?;

            // (0008,0052) CS QueryRetrieveLevel
            let level = match (patient, study) {
                (true, false) => "PATIENT",
                (false, true) | (false, false) => "STUDY",
                _ => unreachable!(),
            };
            obj.put(DataElement::new(
                tags::QUERY_RETRIEVE_LEVEL,
                VR::CS,
                PrimitiveValue::from(level),
            ));

            Ok(obj)
        }
    }
}

fn run() -> Result<(), Error> {
    let App {
        addr,
        file,
        verbose,
        calling_ae_title,
        called_ae_title,
        max_pdu_length,
        patient,
        study,
        query,
    } = App::from_args();

    tracing::subscriber::set_global_default(
        tracing_subscriber::FmtSubscriber::builder()
            .with_max_level(if verbose { Level::DEBUG } else { Level::INFO })
            .finish(),
    )
    .unwrap_or_else(|e| {
        error!("{}", snafu::Report::from_error(e));
    });

    let dcm_query = build_query(file, query, patient, study, verbose)?;

    let abstract_syntax = match (patient, study) {
        // Patient Root Query/Retrieve Information Model - FIND
        (true, false) => "1.2.840.10008.5.1.4.1.2.1.1",
        // Study Root Query/Retrieve Information Model – FIND (default)
        (false, false) | (false, true) => "1.2.840.10008.5.1.4.1.2.2.1",
        // Series
        _ => unreachable!("Unexpected flag combination"),
    };

    if verbose {
        info!("Establishing association with '{}'...", &addr);
    }

    let mut scu_opt = ClientAssociationOptions::new()
        .with_abstract_syntax(abstract_syntax)
        .calling_ae_title(calling_ae_title)
        .max_pdu_length(max_pdu_length);

    if let Some(called_ae_title) = called_ae_title {
        scu_opt = scu_opt.called_ae_title(called_ae_title);
    }

    let mut scu = scu_opt.establish_with(&addr).context(InitScuSnafu)?;

    if verbose {
        info!("Association established");
    }

    let pc_selected = if let Some(pc_selected) = scu.presentation_contexts().first() {
        pc_selected
    } else {
        error!("Could not choose a presentation context");
        let _ = scu.abort();
        std::process::exit(-2);
    };
    let pc_selected_id = pc_selected.id;

    let ts = if let Some(ts) = TransferSyntaxRegistry.get(&pc_selected.transfer_syntax) {
        ts
    } else {
        error!("Poorly negotiated transfer syntax");
        let _ = scu.abort();
        std::process::exit(-2);
    };

    if verbose {
        debug!("Transfer Syntax: {}", ts.name());
    }

    let cmd = find_req_command(abstract_syntax, 1);

    let mut cmd_data = Vec::with_capacity(128);
    cmd.write_dataset_with_ts(&mut cmd_data, &entries::IMPLICIT_VR_LITTLE_ENDIAN.erased())
        .whatever_context("Failed to write command")?;

    let implicit_vr_le = entries::IMPLICIT_VR_LITTLE_ENDIAN.erased();

    let mut iod_data = Vec::with_capacity(128);
    dcm_query
        .write_dataset_with_ts(&mut iod_data, ts)
        .whatever_context("failed to write identifier dataset")?;

    let nbytes = cmd_data.len() + iod_data.len();

    if verbose {
        debug!("Sending query ({} B)...", nbytes);
    }

    let pdu = Pdu::PData {
        data: vec![PDataValue {
            presentation_context_id: pc_selected_id,
            value_type: PDataValueType::Command,
            is_last: true,
            data: cmd_data,
        }],
    };
    scu.send(&pdu).whatever_context("Could not send command")?;

    let pdu = Pdu::PData {
        data: vec![PDataValue {
            presentation_context_id: pc_selected_id,
            value_type: PDataValueType::Data,
            is_last: true,
            data: iod_data,
        }],
    };
    scu.send(&pdu)
        .whatever_context("Could not send C-Find request")?;

    if verbose {
        debug!("Awaiting response...");
    }

    let mut i = 0;
    loop {
        let rsp_pdu = scu
            .receive()
            .whatever_context("Failed to receive response from remote node")?;

        match rsp_pdu {
            Pdu::PData { data } => {
                let data_value = &data[0];

                let cmd_obj = InMemDicomObject::read_dataset_with_ts(
                    &data_value.data[..],
                    &entries::IMPLICIT_VR_LITTLE_ENDIAN.erased(),
                )
                .context(ReadCommandSnafu)?;
                if verbose {
                    eprintln!("Match #{} Response command:", i);
                    DumpOptions::new()
                        .dump_object_to(stderr(), &cmd_obj)
                        .context(DumpOutputSnafu)?;
                }
                let status = cmd_obj
                    .element(tags::STATUS)
                    .whatever_context("status code from response is missing")?
                    .to_int::<u16>()
                    .whatever_context("failed to read status code")?;
                if status == 0 {
                    if verbose {
                        debug!("Matching is complete");
                    }
                    if i == 0 {
                        info!("No results matching query");
                    }
                    break;
                } else if status == 0xFF00 || status == 0xFF01 {
                    if verbose {
                        debug!("Operation pending: {:x}", status);
                    }

                    // fetch DICOM data

                    let dcm = {
                        let mut rsp = scu.receive_pdata();
                        let mut response_data = Vec::new();
                        rsp.read_to_end(&mut response_data)
                            .whatever_context("Failed to read response data")?;

                        InMemDicomObject::read_dataset_with_ts(&response_data[..], &implicit_vr_le)
                            .whatever_context("Could not read response data set")?
                    };

                    println!(
                        "------------------------ Match #{} ------------------------",
                        i
                    );
                    DumpOptions::new()
                        .dump_object(&dcm)
                        .context(DumpOutputSnafu)?;
                    i += 1;
                } else {
                    warn!("Operation failed (status code {})", status);
                    break;
                }
            }

            pdu @ Pdu::Unknown { .. }
            | pdu @ Pdu::AssociationRQ { .. }
            | pdu @ Pdu::AssociationAC { .. }
            | pdu @ Pdu::AssociationRJ { .. }
            | pdu @ Pdu::ReleaseRQ
            | pdu @ Pdu::ReleaseRP
            | pdu @ Pdu::AbortRQ { .. } => {
                error!("Unexpected SCP response: {:?}", pdu);
                let _ = scu.abort();
                std::process::exit(-2);
            }
        }
    }
    let _ = scu.release();

    Ok(())
}

fn find_req_command(
    sop_class_uid: &str,
    message_id: u16,
) -> InMemDicomObject<StandardDataDictionary> {
    let mut obj = InMemDicomObject::new_empty();

    // group length
    obj.put(DataElement::new(
        tags::COMMAND_GROUP_LENGTH,
        VR::UL,
        PrimitiveValue::from(
            8 + even_len(sop_class_uid.len())   // SOP Class UID
            + 8 + 2 // command field
            + 8 + 2 // message ID
            + 8 + 2 // priority
            + 8 + 2, // data set type
        ),
    ));

    // SOP Class UID
    obj.put(DataElement::new(
        tags::AFFECTED_SOP_CLASS_UID,
        VR::UI,
        PrimitiveValue::from(sop_class_uid),
    ));

    // command field
    obj.put(DataElement::new(
        tags::COMMAND_FIELD,
        VR::US,
        // 0020H: C-FIND-RQ message
        dicom_value!(U16, [0x0020]),
    ));

    // message ID
    obj.put(DataElement::new(
        tags::MESSAGE_ID,
        VR::US,
        dicom_value!(U16, [message_id]),
    ));

    //priority
    obj.put(DataElement::new(
        tags::PRIORITY,
        VR::US,
        // medium
        dicom_value!(U16, [0x0000]),
    ));

    // data set type
    obj.put(DataElement::new(
        tags::COMMAND_DATA_SET_TYPE,
        VR::US,
        dicom_value!(U16, [0x0001]),
    ));

    obj
}

fn even_len(l: usize) -> u32 {
    ((l + 1) & !1) as u32
}

#[cfg(test)]
mod tests {
    use super::even_len;
    #[test]
    fn test_even_len() {
        assert_eq!(even_len(0), 0);
        assert_eq!(even_len(1), 2);
        assert_eq!(even_len(2), 2);
        assert_eq!(even_len(3), 4);
        assert_eq!(even_len(4), 4);
        assert_eq!(even_len(5), 6);
    }
}
<|endoftext|>


<commit_before>#![feature(phase)]
#![crate_type = "dylib"]

#[cfg(test)] #[phase(plugin,link)] extern crate shiny;

#[cfg(test)]
mod test {
  describe!(
    before_each {
      let awesome = true;
    }

    it "is awesome" {
      assert!(awesome);
    }

    it "injects before_each into all test cases" {
      let still_awesome = awesome;
      assert!(still_awesome);
    }
  )
}
<commit_msg>Fixed nightly breakage<commit_after>#![feature(phase)]
#![crate_type = "dylib"]

#[cfg(test)] #[phase(plugin,link)] extern crate shiny;

#[cfg(test)]
mod test {
  describe!(
    before_each {
      let awesome = true;
    }

    it "is awesome" {
      assert!(awesome);
    }

    it "injects before_each into all test cases" {
      let still_awesome = awesome;
      assert!(still_awesome);
    }
  );
}
<|endoftext|>


<commit_before>use crate::common::*;
use crate::data::packet::Packet;
use crate::data::value::*;
use std::any::Any;
use std::io::{Cursor, ErrorKind, Seek, SeekFrom, Write};
use std::sync::Arc;

use crate::error::*;

/// A trait for a non-seekable object.
pub trait WriteOwned: Write + ToOwned {}
/// A trait for a seekable object.
pub trait WriteSeek: Write + Seek + ToOwned {}

impl<T: Write + ToOwned> WriteOwned for T {}
impl<T: Write + Seek + ToOwned> WriteSeek for T {}

/// Runtime wrapper around either a [`Write`] or a [`WriteSeek`] trait object which supports querying
/// for seek support.
pub enum Writer<WO = Cursor<Vec<u8>>, WS = Cursor<Vec<u8>>> {
    NonSeekable(WO, u64),
    Seekable(WS),
}

impl<WO: WriteOwned> Writer<WO, Cursor<Vec<u8>>> {
    /// Creates a [`Writer`] from an object that implements the [`Write`] trait.
    pub fn from_nonseekable(inner: WO) -> Self {
        Self::NonSeekable(inner, 0)
    }
}

impl<WS: WriteSeek> Writer<Cursor<Vec<u8>>, WS> {
    /// Creates a [`Writer`] from an object that implements both [`Write`] and [`Seek`] traits.
    pub fn from_seekable(inner: WS) -> Self {
        Self::Seekable(inner)
    }
}

impl<WO: WriteOwned, WS: WriteSeek> Writer<WO, WS> {
    /// Returns whether the [`Writer`] can seek within the source.
    pub fn is_seekable(&self) -> bool {
        matches!(self, Self::Seekable(_))
    }

    /// Returns stream position
    pub fn position(&mut self) -> Result<u64> {
        match self {
            Self::NonSeekable(_, index) => Ok(*index),
            Self::Seekable(ref mut inner) => inner.stream_position().map_err(|e| e.into()),
        }
    }

    /// Returns the non-seekable object whether is present.
    pub fn non_seekable_object(&self) -> Option<(<WO as ToOwned>::Owned, u64)> {
        if let Self::NonSeekable(inner, index) = self {
            Some((inner.to_owned(), *index))
        } else {
            None
        }
    }

    /// Returns the seekable object whether is present.
    pub fn seekable_object(&self) -> Option<<WS as ToOwned>::Owned> {
        if let Self::Seekable(inner) = self {
            Some(inner.to_owned())
        } else {
            None
        }
    }
}

impl<WO: WriteOwned, WS: WriteSeek> Write for Writer<WO, WS> {
    fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
        match self {
            Self::NonSeekable(inner, ref mut index) => {
                let result = inner.write(bytes);

                if let Ok(written) = result {
                    *index += written as u64;
                }

                result
            }
            Self::Seekable(inner) => inner.write(bytes),
        }
    }

    fn flush(&mut self) -> std::io::Result<()> {
        match self {
            Self::NonSeekable(inner, ..) => inner.flush(),
            Self::Seekable(inner) => inner.flush(),
        }
    }
}

impl<WO: WriteOwned, WS: WriteSeek> Seek for Writer<WO, WS> {
    fn seek(&mut self, seek: SeekFrom) -> std::io::Result<u64> {
        match self {
            Self::NonSeekable(_, index) => {
                if let SeekFrom::Current(0) = seek {
                    Ok(*index)
                } else {
                    Err(std::io::Error::new(
                        ErrorKind::Other,
                        "Seeking not supported",
                    ))
                }
            }
            Self::Seekable(inner) => inner.seek(seek),
        }
    }
}

/// Used to implement muxing operations.
pub trait Muxer: Send {
    /// Configures a muxer.
    fn configure(&mut self) -> Result<()>;
    /// Writes a stream header into a data structure implementing
    /// the `Write` trait.
    fn write_header<WO: WriteOwned, WS: WriteSeek>(
        &mut self,
        out: &mut Writer<WO, WS>,
    ) -> Result<()>;
    /// Writes a stream packet into a data structure implementing
    /// the `Write` trait.
    fn write_packet<WO: WriteOwned, WS: WriteSeek>(
        &mut self,
        out: &mut Writer<WO, WS>,
        pkt: Arc<Packet>,
    ) -> Result<()>;
    /// Writes a stream trailer into a data structure implementing
    /// the `Write` trait.
    fn write_trailer<WO: WriteOwned, WS: WriteSeek>(
        &mut self,
        out: &mut Writer<WO, WS>,
    ) -> Result<()>;

    /// Sets global media file information for a muxer.
    fn set_global_info(&mut self, info: GlobalInfo) -> Result<()>;
    /// Sets a muxer option.
    ///
    /// This method should be called as many times as the number of options
    /// present in a muxer.
    fn set_option<'a>(&mut self, key: &str, val: Value<'a>) -> Result<()>;
}

/// Auxiliary structure to encapsulate a muxer object and
/// its additional data.
pub struct Context<M: Muxer + Send, WO: WriteOwned, WS: WriteSeek> {
    muxer: M,
    writer: Writer<WO, WS>,
    /// User private data.
    ///
    /// This data cannot be cloned.
    pub user_private: Option<Box<dyn Any + Send + Sync>>,
}

impl<M: Muxer + Send, WO: WriteOwned, WS: WriteSeek> Context<M, WO, WS> {
    /// Creates a new `Context` instance.
    pub fn new(muxer: M, writer: Writer<WO, WS>) -> Self {
        Context {
            muxer,
            writer,
            user_private: None,
        }
    }

    /// Configures a muxer.
    pub fn configure(&mut self) -> Result<()> {
        self.muxer.configure()
    }

    /// Writes a stream header to an internal buffer and returns how many
    /// bytes were written or an error.
    pub fn write_header(&mut self) -> Result<()> {
        self.muxer.write_header(&mut self.writer)
    }

    /// Writes a stream packet to an internal buffer and returns how many
    /// bytes were written or an error.
    pub fn write_packet(&mut self, pkt: Arc<Packet>) -> Result<()> {
        self.muxer.write_packet(&mut self.writer, pkt)
    }

    /// Writes a stream trailer to an internal buffer and returns how many
    /// bytes were written or an error.
    pub fn write_trailer(&mut self) -> Result<()> {
        self.muxer.write_trailer(&mut self.writer)?;
        self.writer.flush()?;

        Ok(())
    }

    /// Sets global media file information for a muxer.
    pub fn set_global_info(&mut self, info: GlobalInfo) -> Result<()> {
        self.muxer.set_global_info(info)
    }

    /// Sets a muxer option.
    ///
    /// This method should be called as many times as the number of options
    /// present in a muxer.
    pub fn set_option<'a, V>(&mut self, key: &str, val: V) -> Result<()>
    where
        V: Into<Value<'a>>,
    {
        self.muxer.set_option(key, val.into())
    }

    /// Returns the underlying writer.
    pub fn writer(&self) -> &Writer<WO, WS> {
        &self.writer
    }
}

/// Format descriptor.
///
/// Contains information on a format and its own muxer.
#[derive(Clone, Debug, PartialEq)]
pub struct Descr {
    /// Format name.
    pub name: &'static str,
    /// Muxer name.
    pub demuxer: &'static str,
    /// Format description.
    pub description: &'static str,
    /// Format media file extensions.
    pub extensions: &'static [&'static str],
    /// Format MIME.
    pub mime: &'static [&'static str],
}

/// Used to get a format descriptor and create a new muxer.
pub trait Descriptor {
    type OutputMuxer: Muxer + Send;

    /// Creates a new muxer for the requested format.
    fn create(&self) -> Self::OutputMuxer;
    /// Returns the descriptor of a format.
    fn describe(&self) -> &Descr;
}

/// Used to look for a specific format.
pub trait Lookup<T: Descriptor + ?Sized> {
    /// Retrieves a specific format by name.
    fn by_name(&self, name: &str) -> Option<&'static T>;
}

impl<T: Descriptor + ?Sized> Lookup<T> for [&'static T] {
    fn by_name(&self, name: &str) -> Option<&'static T> {
        self.iter().find(|&&d| d.describe().name == name).copied()
    }
}

#[cfg(test)]
mod test {
    use std::io::Cursor;

    use super::*;

    const DUMMY_HEADER_LENGTH: usize = 12;
    const DUMMY_PACKET_LENGTH: usize = 2;
    const DUMMY_PACKETS_NUMBER: usize = 2;
    const DUMMY_TRAILER_LENGTH: usize = 13;

    struct DummyDes {
        d: Descr,
    }

    struct DummyMuxer {}

    impl DummyMuxer {
        pub fn new() -> Self {
            Self {}
        }
    }

    impl Muxer for DummyMuxer {
        fn configure(&mut self) -> Result<()> {
            Ok(())
        }

        fn write_header<WO: WriteOwned, WS: WriteSeek>(
            &mut self,
            out: &mut Writer<WO, WS>,
        ) -> Result<()> {
            let buf = b"Dummy header";
            out.write_all(buf.as_slice()).unwrap();
            Ok(())
        }

        fn write_packet<WO: WriteOwned, WS: WriteSeek>(
            &mut self,
            out: &mut Writer<WO, WS>,
            pkt: Arc<Packet>,
        ) -> Result<()> {
            out.write_all(&pkt.data).unwrap();
            Ok(())
        }

        fn write_trailer<WO: WriteOwned, WS: WriteSeek>(
            &mut self,
            out: &mut Writer<WO, WS>,
        ) -> Result<()> {
            let buf = b"Dummy trailer";
            out.write_all(buf.as_slice()).unwrap();
            Ok(())
        }

        fn set_global_info(&mut self, _info: GlobalInfo) -> Result<()> {
            Ok(())
        }

        fn set_option<'a>(&mut self, _key: &str, _val: Value<'a>) -> Result<()> {
            Ok(())
        }
    }

    impl Descriptor for DummyDes {
        type OutputMuxer = DummyMuxer;

        fn create(&self) -> Self::OutputMuxer {
            DummyMuxer {}
        }
        fn describe(&self) -> &Descr {
            &self.d
        }
    }

    const DUMMY_DES: &dyn Descriptor<OutputMuxer = DummyMuxer> = &DummyDes {
        d: Descr {
            name: "dummy",
            demuxer: "dummy",
            description: "Dummy mux",
            extensions: &["mx", "mux"],
            mime: &["application/dummy"],
        },
    };

    #[test]
    fn lookup() {
        let muxers: &[&dyn Descriptor<OutputMuxer = DummyMuxer>] = &[DUMMY_DES];

        muxers.by_name("dummy").unwrap();
    }

    fn run_muxer<WO: WriteOwned, WS: WriteSeek>(
        writer: Writer<WO, WS>,
    ) -> Context<DummyMuxer, WO, WS> {
        let mux = DummyMuxer::new();

        let mut muxer = Context::new(mux, writer);

        muxer.configure().unwrap();
        muxer.write_header().unwrap();

        // Write zeroed packets of a certain size
        for _ in 0..DUMMY_PACKETS_NUMBER {
            let packet = Packet::zeroed(DUMMY_PACKET_LENGTH);
            muxer.write_packet(Arc::new(packet)).unwrap();
        }

        muxer.write_trailer().unwrap();
        muxer
    }

    fn check_underlying_buffer(buffer: Vec<u8>) {
        assert_eq!(
            buffer.get(..DUMMY_HEADER_LENGTH).unwrap(),
            b"Dummy header".as_slice()
        );

        assert_eq!(
            buffer
                // Get only packets, without header and trailer data
                .get(
                    DUMMY_HEADER_LENGTH
                        ..DUMMY_HEADER_LENGTH + (DUMMY_PACKETS_NUMBER * DUMMY_PACKET_LENGTH)
                )
                .unwrap(),
            &[0, 0, 0, 0]
        );

        assert_eq!(
            buffer
                // Skip header and packets
                .get(DUMMY_HEADER_LENGTH + (DUMMY_PACKETS_NUMBER * DUMMY_PACKET_LENGTH)..)
                .unwrap(),
            b"Dummy trailer".as_slice()
        );
    }

    #[test]
    fn non_seekable_muxer() {
        let muxer = run_muxer(Writer::from_nonseekable(Vec::new()));
        let (buffer, index) = muxer.writer().non_seekable_object().unwrap();
        debug_assert!(!muxer.writer().is_seekable());
        check_underlying_buffer(buffer);
        assert_eq!(
            index as usize,
            DUMMY_HEADER_LENGTH
                + (DUMMY_PACKETS_NUMBER * DUMMY_PACKET_LENGTH)
                + DUMMY_TRAILER_LENGTH
        );
    }

    #[test]
    fn seekable_muxer() {
        let muxer = run_muxer(Writer::from_seekable(Cursor::new(Vec::new())));
        let buffer = muxer.writer().seekable_object().unwrap().into_inner();
        debug_assert!(muxer.writer().is_seekable());
        check_underlying_buffer(buffer);
    }
}
<commit_msg>format: Fix comments<commit_after>use crate::common::*;
use crate::data::packet::Packet;
use crate::data::value::*;
use std::any::Any;
use std::io::{Cursor, ErrorKind, Seek, SeekFrom, Write};
use std::sync::Arc;

use crate::error::*;

/// A trait for a non-seekable object.
pub trait WriteOwned: Write + ToOwned {}
/// A trait for a seekable object.
pub trait WriteSeek: Write + Seek + ToOwned {}

impl<T: Write + ToOwned> WriteOwned for T {}
impl<T: Write + Seek + ToOwned> WriteSeek for T {}

/// Runtime wrapper around either a [`Write`] or a [`WriteSeek`] trait object
/// which supports querying for seek support.
pub enum Writer<WO = Cursor<Vec<u8>>, WS = Cursor<Vec<u8>>> {
    NonSeekable(WO, u64),
    Seekable(WS),
}

impl<WO: WriteOwned> Writer<WO, Cursor<Vec<u8>>> {
    /// Creates a [`Writer`] from an object that implements the [`Write`] trait.
    pub fn from_nonseekable(inner: WO) -> Self {
        Self::NonSeekable(inner, 0)
    }
}

impl<WS: WriteSeek> Writer<Cursor<Vec<u8>>, WS> {
    /// Creates a [`Writer`] from an object that implements both
    /// [`Write`] and [`Seek`] traits.
    pub fn from_seekable(inner: WS) -> Self {
        Self::Seekable(inner)
    }
}

impl<WO: WriteOwned, WS: WriteSeek> Writer<WO, WS> {
    /// Returns whether the [`Writer`] can seek within the source.
    pub fn is_seekable(&self) -> bool {
        matches!(self, Self::Seekable(_))
    }

    /// Returns stream position.
    pub fn position(&mut self) -> Result<u64> {
        match self {
            Self::NonSeekable(_, index) => Ok(*index),
            Self::Seekable(ref mut inner) => inner.stream_position().map_err(|e| e.into()),
        }
    }

    /// Returns the non-seekable object whether is present.
    pub fn non_seekable_object(&self) -> Option<(<WO as ToOwned>::Owned, u64)> {
        if let Self::NonSeekable(inner, index) = self {
            Some((inner.to_owned(), *index))
        } else {
            None
        }
    }

    /// Returns the seekable object whether is present.
    pub fn seekable_object(&self) -> Option<<WS as ToOwned>::Owned> {
        if let Self::Seekable(inner) = self {
            Some(inner.to_owned())
        } else {
            None
        }
    }
}

impl<WO: WriteOwned, WS: WriteSeek> Write for Writer<WO, WS> {
    fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
        match self {
            Self::NonSeekable(inner, ref mut index) => {
                let result = inner.write(bytes);

                if let Ok(written) = result {
                    *index += written as u64;
                }

                result
            }
            Self::Seekable(inner) => inner.write(bytes),
        }
    }

    fn flush(&mut self) -> std::io::Result<()> {
        match self {
            Self::NonSeekable(inner, ..) => inner.flush(),
            Self::Seekable(inner) => inner.flush(),
        }
    }
}

impl<WO: WriteOwned, WS: WriteSeek> Seek for Writer<WO, WS> {
    fn seek(&mut self, seek: SeekFrom) -> std::io::Result<u64> {
        match self {
            Self::NonSeekable(_, index) => {
                if let SeekFrom::Current(0) = seek {
                    Ok(*index)
                } else {
                    Err(std::io::Error::new(
                        ErrorKind::Other,
                        "Seeking not supported",
                    ))
                }
            }
            Self::Seekable(inner) => inner.seek(seek),
        }
    }
}

/// Used to implement muxing operations.
pub trait Muxer: Send {
    /// Configures a muxer.
    fn configure(&mut self) -> Result<()>;
    /// Writes a stream header into a data structure implementing
    /// the `Write` trait.
    fn write_header<WO: WriteOwned, WS: WriteSeek>(
        &mut self,
        out: &mut Writer<WO, WS>,
    ) -> Result<()>;
    /// Writes a stream packet into a data structure implementing
    /// the `Write` trait.
    fn write_packet<WO: WriteOwned, WS: WriteSeek>(
        &mut self,
        out: &mut Writer<WO, WS>,
        pkt: Arc<Packet>,
    ) -> Result<()>;
    /// Writes a stream trailer into a data structure implementing
    /// the `Write` trait.
    fn write_trailer<WO: WriteOwned, WS: WriteSeek>(
        &mut self,
        out: &mut Writer<WO, WS>,
    ) -> Result<()>;

    /// Sets global media file information for a muxer.
    fn set_global_info(&mut self, info: GlobalInfo) -> Result<()>;
    /// Sets a muxer option.
    ///
    /// This method should be called as many times as the number of options
    /// present in a muxer.
    fn set_option<'a>(&mut self, key: &str, val: Value<'a>) -> Result<()>;
}

/// Auxiliary structure to encapsulate a muxer object and
/// its additional data.
pub struct Context<M: Muxer + Send, WO: WriteOwned, WS: WriteSeek> {
    muxer: M,
    writer: Writer<WO, WS>,
    /// User private data.
    ///
    /// This data cannot be cloned.
    pub user_private: Option<Box<dyn Any + Send + Sync>>,
}

impl<M: Muxer + Send, WO: WriteOwned, WS: WriteSeek> Context<M, WO, WS> {
    /// Creates a new `Context` instance.
    pub fn new(muxer: M, writer: Writer<WO, WS>) -> Self {
        Context {
            muxer,
            writer,
            user_private: None,
        }
    }

    /// Configures a muxer.
    pub fn configure(&mut self) -> Result<()> {
        self.muxer.configure()
    }

    /// Writes a stream header to an internal buffer and returns how many
    /// bytes were written or an error.
    pub fn write_header(&mut self) -> Result<()> {
        self.muxer.write_header(&mut self.writer)
    }

    /// Writes a stream packet to an internal buffer and returns how many
    /// bytes were written or an error.
    pub fn write_packet(&mut self, pkt: Arc<Packet>) -> Result<()> {
        self.muxer.write_packet(&mut self.writer, pkt)
    }

    /// Writes a stream trailer to an internal buffer and returns how many
    /// bytes were written or an error.
    pub fn write_trailer(&mut self) -> Result<()> {
        self.muxer.write_trailer(&mut self.writer)?;
        self.writer.flush()?;

        Ok(())
    }

    /// Sets global media file information for a muxer.
    pub fn set_global_info(&mut self, info: GlobalInfo) -> Result<()> {
        self.muxer.set_global_info(info)
    }

    /// Sets a muxer option.
    ///
    /// This method should be called as many times as the number of options
    /// present in a muxer.
    pub fn set_option<'a, V>(&mut self, key: &str, val: V) -> Result<()>
    where
        V: Into<Value<'a>>,
    {
        self.muxer.set_option(key, val.into())
    }

    /// Returns the underlying writer.
    pub fn writer(&self) -> &Writer<WO, WS> {
        &self.writer
    }
}

/// Format descriptor.
///
/// Contains information on a format and its own muxer.
#[derive(Clone, Debug, PartialEq)]
pub struct Descr {
    /// Format name.
    pub name: &'static str,
    /// Muxer name.
    pub demuxer: &'static str,
    /// Format description.
    pub description: &'static str,
    /// Format media file extensions.
    pub extensions: &'static [&'static str],
    /// Format MIME.
    pub mime: &'static [&'static str],
}

/// Used to get a format descriptor and create a new muxer.
pub trait Descriptor {
    type OutputMuxer: Muxer + Send;

    /// Creates a new muxer for the requested format.
    fn create(&self) -> Self::OutputMuxer;
    /// Returns the descriptor of a format.
    fn describe(&self) -> &Descr;
}

/// Used to look for a specific format.
pub trait Lookup<T: Descriptor + ?Sized> {
    /// Retrieves a specific format by name.
    fn by_name(&self, name: &str) -> Option<&'static T>;
}

impl<T: Descriptor + ?Sized> Lookup<T> for [&'static T] {
    fn by_name(&self, name: &str) -> Option<&'static T> {
        self.iter().find(|&&d| d.describe().name == name).copied()
    }
}

#[cfg(test)]
mod test {
    use std::io::Cursor;

    use super::*;

    const DUMMY_HEADER_LENGTH: usize = 12;
    const DUMMY_PACKET_LENGTH: usize = 2;
    const DUMMY_PACKETS_NUMBER: usize = 2;
    const DUMMY_TRAILER_LENGTH: usize = 13;

    struct DummyDes {
        d: Descr,
    }

    struct DummyMuxer {}

    impl DummyMuxer {
        pub fn new() -> Self {
            Self {}
        }
    }

    impl Muxer for DummyMuxer {
        fn configure(&mut self) -> Result<()> {
            Ok(())
        }

        fn write_header<WO: WriteOwned, WS: WriteSeek>(
            &mut self,
            out: &mut Writer<WO, WS>,
        ) -> Result<()> {
            let buf = b"Dummy header";
            out.write_all(buf.as_slice()).unwrap();
            Ok(())
        }

        fn write_packet<WO: WriteOwned, WS: WriteSeek>(
            &mut self,
            out: &mut Writer<WO, WS>,
            pkt: Arc<Packet>,
        ) -> Result<()> {
            out.write_all(&pkt.data).unwrap();
            Ok(())
        }

        fn write_trailer<WO: WriteOwned, WS: WriteSeek>(
            &mut self,
            out: &mut Writer<WO, WS>,
        ) -> Result<()> {
            let buf = b"Dummy trailer";
            out.write_all(buf.as_slice()).unwrap();
            Ok(())
        }

        fn set_global_info(&mut self, _info: GlobalInfo) -> Result<()> {
            Ok(())
        }

        fn set_option<'a>(&mut self, _key: &str, _val: Value<'a>) -> Result<()> {
            Ok(())
        }
    }

    impl Descriptor for DummyDes {
        type OutputMuxer = DummyMuxer;

        fn create(&self) -> Self::OutputMuxer {
            DummyMuxer {}
        }
        fn describe(&self) -> &Descr {
            &self.d
        }
    }

    const DUMMY_DES: &dyn Descriptor<OutputMuxer = DummyMuxer> = &DummyDes {
        d: Descr {
            name: "dummy",
            demuxer: "dummy",
            description: "Dummy mux",
            extensions: &["mx", "mux"],
            mime: &["application/dummy"],
        },
    };

    #[test]
    fn lookup() {
        let muxers: &[&dyn Descriptor<OutputMuxer = DummyMuxer>] = &[DUMMY_DES];

        muxers.by_name("dummy").unwrap();
    }

    fn run_muxer<WO: WriteOwned, WS: WriteSeek>(
        writer: Writer<WO, WS>,
    ) -> Context<DummyMuxer, WO, WS> {
        let mux = DummyMuxer::new();

        let mut muxer = Context::new(mux, writer);

        muxer.configure().unwrap();
        muxer.write_header().unwrap();

        // Write zeroed packets of a certain size
        for _ in 0..DUMMY_PACKETS_NUMBER {
            let packet = Packet::zeroed(DUMMY_PACKET_LENGTH);
            muxer.write_packet(Arc::new(packet)).unwrap();
        }

        muxer.write_trailer().unwrap();
        muxer
    }

    fn check_underlying_buffer(buffer: Vec<u8>) {
        assert_eq!(
            buffer.get(..DUMMY_HEADER_LENGTH).unwrap(),
            b"Dummy header".as_slice()
        );

        assert_eq!(
            buffer
                // Get only packets, without header and trailer data
                .get(
                    DUMMY_HEADER_LENGTH
                        ..DUMMY_HEADER_LENGTH + (DUMMY_PACKETS_NUMBER * DUMMY_PACKET_LENGTH)
                )
                .unwrap(),
            &[0, 0, 0, 0]
        );

        assert_eq!(
            buffer
                // Skip header and packets
                .get(DUMMY_HEADER_LENGTH + (DUMMY_PACKETS_NUMBER * DUMMY_PACKET_LENGTH)..)
                .unwrap(),
            b"Dummy trailer".as_slice()
        );
    }

    #[test]
    fn non_seekable_muxer() {
        let muxer = run_muxer(Writer::from_nonseekable(Vec::new()));
        let (buffer, index) = muxer.writer().non_seekable_object().unwrap();
        debug_assert!(!muxer.writer().is_seekable());
        check_underlying_buffer(buffer);
        assert_eq!(
            index as usize,
            DUMMY_HEADER_LENGTH
                + (DUMMY_PACKETS_NUMBER * DUMMY_PACKET_LENGTH)
                + DUMMY_TRAILER_LENGTH
        );
    }

    #[test]
    fn seekable_muxer() {
        let muxer = run_muxer(Writer::from_seekable(Cursor::new(Vec::new())));
        let buffer = muxer.writer().seekable_object().unwrap().into_inner();
        debug_assert!(muxer.writer().is_seekable());
        check_underlying_buffer(buffer);
    }
}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::io::stdin;
use std::fs::OpenOptions;
use std::result::Result as RResult;
use std::io::Read;
use std::ops::DerefMut;

use clap::ArgMatches;

use libimagrt::runtime::Runtime;
use libimagstore::store::Entry;
use libimagstore::store::EntryHeader;

use error::StoreError;
use error::StoreErrorKind;
use util::build_entry_path;
use util::build_toml_header;

type Result<T> = RResult<T, StoreError>;

pub fn create(rt: &Runtime) {
    rt.cli()
        .subcommand_matches("create")
        .map(|scmd| {
            debug!("Found 'create' subcommand...");

            // unwrap is safe as value is required
            let path = build_entry_path(rt, scmd.value_of("path").unwrap());
            debug!("path = {:?}", path);

            scmd.subcommand_matches("entry")
                .map(|entry| create_from_cli_spec(rt, scmd, &path))
                .ok_or(()) // hackythehackhack
                .map_err(|_| {
                    create_from_source(rt, scmd, &path)
                        .unwrap_or_else(|e| debug!("Error building Entry: {:?}", e))
                });
        });
}

fn create_from_cli_spec(rt: &Runtime, matches: &ArgMatches, path: &PathBuf) -> Result<()> {
    let content = matches.subcommand_matches("entry")
        .map(|entry_subcommand| {
            debug!("Found entry subcommand, parsing content");
            entry_subcommand
                .value_of("content")
                .map(String::from)
                .unwrap_or_else(|| {
                    entry_subcommand
                        .value_of("content-from")
                        .map(|src| entry_from_raw(src))
                        .unwrap_or(String::new())
                })
        })
        .unwrap_or_else(|| {
            debug!("Didn't find entry subcommand, getting raw content");
            matches.value_of("from-raw")
                .map(|raw_src| entry_from_raw(raw_src))
                .unwrap_or(String::new())
        });

    debug!("Got content with len = {}", content.len());

    rt.store()
        .create(PathBuf::from(path))
        .map(|mut element| {
            {
                let mut e_content = element.get_content_mut();
                *e_content = content;
                debug!("New content set");
            }
            {
                let mut e_header  = element.get_header_mut();
                matches.subcommand_matches("entry")
                    .map(|entry_matches| {
                        *e_header = build_toml_header(entry_matches, EntryHeader::new());
                        debug!("New header set");
                    });
            }
        })
        .map_err(|e| StoreError::new(StoreErrorKind::BackendError, Some(Box::new(e))))
}

fn create_from_source(rt: &Runtime, matches: &ArgMatches, path: &PathBuf) -> Result<()> {
    let content = matches
        .value_of("from-raw")
        .ok_or(StoreError::new(StoreErrorKind::NoCommandlineCall, None))
        .map(|raw_src| entry_from_raw(raw_src));

    if content.is_err() {
        return content.map(|_| ());
    }
    let content = content.unwrap();
    debug!("Content with len = {}", content.len());

    Entry::from_str(path.clone(), &content[..])
        .map(|mut new_e| {
            rt.store()
                .create(path.clone())
                .map(|mut old_e| {
                    *old_e.deref_mut() = new_e;
                });

            debug!("Entry build");
        })
        .map_err(|serr| StoreError::new(StoreErrorKind::BackendError, Some(Box::new(serr))))
}

fn entry_from_raw(raw_src: &str) -> String {
    let mut content = String::new();
    if raw_src == "-" {
        debug!("Reading entry from stdin");
        let res = stdin().read_to_string(&mut content);
        debug!("Read {:?} bytes", res);
    } else {
        debug!("Reading entry from file at {:?}", raw_src);
        OpenOptions::new()
            .read(true)
            .write(false)
            .create(false)
            .open(raw_src)
            .and_then(|mut f| f.read_to_string(&mut content));
    }
    content
}

<commit_msg>create() fixup: entry_from_raw() -> string_from_raw_src()<commit_after>use std::path::PathBuf;
use std::io::stdin;
use std::fs::OpenOptions;
use std::result::Result as RResult;
use std::io::Read;
use std::ops::DerefMut;

use clap::ArgMatches;

use libimagrt::runtime::Runtime;
use libimagstore::store::Entry;
use libimagstore::store::EntryHeader;

use error::StoreError;
use error::StoreErrorKind;
use util::build_entry_path;
use util::build_toml_header;

type Result<T> = RResult<T, StoreError>;

pub fn create(rt: &Runtime) {
    rt.cli()
        .subcommand_matches("create")
        .map(|scmd| {
            debug!("Found 'create' subcommand...");

            // unwrap is safe as value is required
            let path = build_entry_path(rt, scmd.value_of("path").unwrap());
            debug!("path = {:?}", path);

            scmd.subcommand_matches("entry")
                .map(|entry| create_from_cli_spec(rt, scmd, &path))
                .ok_or(()) // hackythehackhack
                .map_err(|_| {
                    create_from_source(rt, scmd, &path)
                        .unwrap_or_else(|e| debug!("Error building Entry: {:?}", e))
                });
        });
}

fn create_from_cli_spec(rt: &Runtime, matches: &ArgMatches, path: &PathBuf) -> Result<()> {
    let content = matches.subcommand_matches("entry")
        .map(|entry_subcommand| {
            debug!("Found entry subcommand, parsing content");
            entry_subcommand
                .value_of("content")
                .map(String::from)
                .unwrap_or_else(|| {
                    entry_subcommand
                        .value_of("content-from")
                        .map(|src| string_from_raw_src(src))
                        .unwrap_or(String::new())
                })
        })
        .unwrap_or_else(|| {
            debug!("Didn't find entry subcommand, getting raw content");
            matches.value_of("from-raw")
                .map(|raw_src| string_from_raw_src(raw_src))
                .unwrap_or(String::new())
        });

    debug!("Got content with len = {}", content.len());

    rt.store()
        .create(PathBuf::from(path))
        .map(|mut element| {
            {
                let mut e_content = element.get_content_mut();
                *e_content = content;
                debug!("New content set");
            }
            {
                let mut e_header  = element.get_header_mut();
                matches.subcommand_matches("entry")
                    .map(|entry_matches| {
                        *e_header = build_toml_header(entry_matches, EntryHeader::new());
                        debug!("New header set");
                    });
            }
        })
        .map_err(|e| StoreError::new(StoreErrorKind::BackendError, Some(Box::new(e))))
}

fn create_from_source(rt: &Runtime, matches: &ArgMatches, path: &PathBuf) -> Result<()> {
    let content = matches
        .value_of("from-raw")
        .ok_or(StoreError::new(StoreErrorKind::NoCommandlineCall, None))
        .map(|raw_src| string_from_raw_src(raw_src));

    if content.is_err() {
        return content.map(|_| ());
    }
    let content = content.unwrap();
    debug!("Content with len = {}", content.len());

    Entry::from_str(path.clone(), &content[..])
        .map(|mut new_e| {
            rt.store()
                .create(path.clone())
                .map(|mut old_e| {
                    *old_e.deref_mut() = new_e;
                });

            debug!("Entry build");
        })
        .map_err(|serr| StoreError::new(StoreErrorKind::BackendError, Some(Box::new(serr))))
}

fn string_from_raw_src(raw_src: &str) -> String {
    let mut content = String::new();
    if raw_src == "-" {
        debug!("Reading entry from stdin");
        let res = stdin().read_to_string(&mut content);
        debug!("Read {:?} bytes", res);
    } else {
        debug!("Reading entry from file at {:?}", raw_src);
        OpenOptions::new()
            .read(true)
            .write(false)
            .create(false)
            .open(raw_src)
            .and_then(|mut f| f.read_to_string(&mut content));
    }
    content
}
<|endoftext|>