<commit_before>#![deny(warnings)]
#![feature(const_fn)]

extern crate orbclient;
extern crate orbimage;
extern crate orbfont;

pub const ICON_SIZE: i32 = 48;
pub const ICON_SMALL_SIZE: i32 = 32;

#[cfg(target_os = "redox")]
static UI_PATH: &'static str = "/ui";

#[cfg(not(target_os = "redox"))]
static UI_PATH: &'static str = "ui";

#[cfg(not(target_os = "redox"))]
fn wait(status: &mut i32) -> usize {
    extern crate libc;

    use std::io::Error;

    let pid = unsafe { libc::waitpid(0, status as *mut i32, libc::WNOHANG) };
    if pid < 0 {
        panic!("waitpid failed: {}", Error::last_os_error());
    }
    pid as usize
}

#[cfg(target_os = "redox")]
fn wait(status: &mut usize) -> usize {
    extern crate syscall;

    syscall::waitpid(0, status, syscall::WNOHANG).unwrap()
}

fn load_icon(path: &str) -> Image {
    let icon = Image::from_path(path).unwrap_or(Image::default());
    if icon.width() == ICON_SIZE as u32 && icon.height() == ICON_SIZE as u32 {
        icon
    } else {
        icon.resize(ICON_SIZE as u32, ICON_SIZE as u32, orbimage::ResizeType::Lanczos3).unwrap()
    }
}

fn load_icon_small(path: &str) -> Image {
    let icon = Image::from_path(path).unwrap_or(Image::default());
    if icon.width() == ICON_SMALL_SIZE as u32 && icon.height() == ICON_SMALL_SIZE as u32 {
        icon
    } else {
        icon.resize(ICON_SMALL_SIZE as u32, ICON_SMALL_SIZE as u32, orbimage::ResizeType::Lanczos3).unwrap()
    }
}

use std::env;
use std::os::unix::process::ExitStatusExt;
use std::path::Path;
use std::process::{Command, ExitStatus};

use orbclient::{EventOption, Renderer, Window, K_ESC};
use orbimage::Image;
use orbfont::Font;

use package::Package;
use theme::{BAR_COLOR, BAR_HIGHLIGHT_COLOR, TEXT_COLOR, TEXT_HIGHLIGHT_COLOR};

mod package;
mod theme;

fn get_packages() -> Vec<Package> {
    let read_dir = Path::new(&format!("{}/apps/", UI_PATH)).read_dir().expect("failed to read apps directory");

    let mut entries = vec![];
    for dir in read_dir {
        let dir = match dir {
            Ok(x) => x,
            Err(_) => continue,
        };
        let file_name = dir.file_name().to_string_lossy().into_owned();
        if dir.file_type().expect("failed to get file_type").is_file() {
            entries.push(file_name);
        }
    }

    entries.sort();

    let mut packages: Vec<Package> = Vec::new();
    for entry in entries.iter() {
        packages.push(Package::from_path(&format!("{}/apps/{}", UI_PATH, entry)));
    }

    packages
}

fn draw(window: &mut Window, packages: &Vec<Package>, start: &Image, selected: i32){
    let h = window.height();
    window.set(BAR_COLOR);

    let mut x = 0;
    let mut i = 0;

    {
        let y = h as isize - start.height() as isize;

        if i == selected {
            window.rect(x as i32, y as i32,
                              start.width() as u32, start.height() as u32,
                              BAR_HIGHLIGHT_COLOR);
        }

        start.draw(window, x as i32, y as i32);

        x += start.width() as i32;
        i += 1;
    }

    for package in packages.iter() {
        let y = h as isize - package.icon.height() as isize;

        if i == selected {
            window.rect(x as i32, y as i32,
                              package.icon.width() as u32, package.icon.height() as u32,
                              BAR_HIGHLIGHT_COLOR);
        }

        package.icon.draw(window, x as i32, y as i32);

        x += package.icon.width() as i32;
        i += 1;
    }

    window.sync();
}

fn draw_chooser(window: &mut Window, font: &Font, packages: &Vec<Package>, selected: i32){
    let w = window.width();

    window.set(BAR_COLOR);

    let mut y = 0;
    for (i, package) in packages.iter().enumerate() {
        if i as i32 == selected {
            window.rect(0, y, w, ICON_SMALL_SIZE as u32, BAR_HIGHLIGHT_COLOR);
        }

        package.icon_small.draw(window, 0, y);

        font.render(&package.name, 16.0).draw(window, ICON_SMALL_SIZE + 8, y + 8, if i as i32 == selected { TEXT_HIGHLIGHT_COLOR } else { TEXT_COLOR });

        y += ICON_SMALL_SIZE;
    }

    window.sync();
}

fn bar_main() {
    let mut children = Vec::new();

    let packages = get_packages();

    let start = load_icon(&format!("{}/icons/places/start-here.png", UI_PATH));

    let mut logout_package = Package::new();
    logout_package.name = "Logout".to_string();
    logout_package.icon = load_icon(&format!("{}/icons/actions/system-log-out.png", UI_PATH));
    logout_package.icon_small = load_icon_small(&format!("{}/icons/actions/system-log-out.png", UI_PATH));
    logout_package.binary = "exit".to_string();

    let mut start_packages = packages.clone();
    start_packages.push(logout_package);

    let (width, height) = orbclient::get_display_size().expect("launcher: failed to get display size");
    let mut window = Window::new(0, height as i32 - ICON_SIZE, width, ICON_SIZE as u32, "").expect("launcher: failed to open window");

    let mut selected = -1;
    let mut last_left_button = false;

    draw(&mut window, &packages, &start, selected);
    'running: loop {
        for event in window.events() {
            match event.to_option() {
                EventOption::Mouse(mouse_event) => {
                    let mut now_selected = -1;

                    {
                        let mut x = 0;
                        let mut i = 0;

                        {
                            let y = window.height() as i32 - start.height() as i32;
                            if mouse_event.y >= y && mouse_event.x >= x &&
                               mouse_event.x < x + start.width() as i32 {
                                   now_selected = i;
                            }
                            x += start.width() as i32;
                            i += 1;
                        }

                        for package in packages.iter() {
                            let y = window.height() as i32 - package.icon.height() as i32;
                            if mouse_event.y >= y && mouse_event.x >= x &&
                               mouse_event.x < x + package.icon.width() as i32 {
                                now_selected = i;
                            }
                            x += package.icon.width() as i32;
                            i += 1;
                        }
                    }

                    if now_selected != selected {
                        selected = now_selected;
                        draw(&mut window, &packages, &start, selected);
                    }

                    if ! mouse_event.left_button && last_left_button {
                        let mut i = 0;

                        if i == selected {
                            let start_h = start_packages.len() as u32 * ICON_SMALL_SIZE as u32;
                            let mut start_window = Window::new(0, height as i32 - ICON_SIZE - start_h as i32, 320, start_h, "").unwrap();
                            let font = Font::find(Some("Sans"), None, None).unwrap();

                            let mut selected = -1;
                            let mut last_left_button = false;

                            draw_chooser(&mut start_window, &font, &start_packages, selected);
                            'start_choosing: loop {
                                for event in start_window.events() {
                                    match event.to_option() {
                                        EventOption::Mouse(mouse_event) => {
                                            let mut now_selected = -1;

                                            let mut y = 0;
                                            for (j, _package) in start_packages.iter().enumerate() {
                                                if mouse_event.y >= y && mouse_event.y < y + ICON_SMALL_SIZE {
                                                    now_selected = j as i32;
                                                }
                                                y += ICON_SMALL_SIZE;
                                            }

                                            if now_selected != selected {
                                                selected = now_selected;
                                                draw_chooser(&mut start_window, &font, &start_packages, selected);
                                            }

                                            if ! mouse_event.left_button && last_left_button {
                                                let mut y = 0;
                                                for package in start_packages.iter() {
                                                    if mouse_event.y >= y && mouse_event.y < y + ICON_SMALL_SIZE {
                                                        if package.binary == "exit" {
                                                            break 'running;
                                                        } else {
                                                            match Command::new(&package.binary).spawn() {
                                                                Ok(child) => children.push(child),
                                                                Err(err) => println!("launcher: failed to launch {}: {}", package.binary, err)
                                                            }
                                                        }
                                                        break 'start_choosing;
                                                    }
                                                    y += ICON_SMALL_SIZE;
                                                }
                                            }

                                            last_left_button = mouse_event.left_button;
                                        },
                                        EventOption::Key(key_event) => {
                                            match key_event.scancode {
                                                K_ESC => break 'start_choosing,
                                                _ => ()
                                            }
                                        },
                                        EventOption::Focus(focus_event) => if ! focus_event.focused {
                                            break 'start_choosing;
                                        },
                                        EventOption::Quit(_) => break 'start_choosing,
                                        _ => ()
                                    }
                                }
                            }
                        }
                        i += 1;

                        for package in packages.iter() {
                            if i == selected {
                                match Command::new(&package.binary).spawn() {
                                    Ok(child) => children.push(child),
                                    Err(err) => println!("launcher: failed to launch {}: {}", package.binary, err)
                                }
                            }
                            i += 1;
                        }
                    }

                    last_left_button = mouse_event.left_button;
                },
                EventOption::Quit(_) => break 'running,
                _ => ()
            }
        }
    }

    for mut child in children {
        let pid = child.id();
        match child.kill() {
            Ok(()) => (),
            Err(err) => println!("launcher: failed to kill {}: {}", pid, err),
        }
        match child.wait() {
            Ok(status) => println!("launcher: {} exited with {}", pid, status),
            Err(err) => println!("launcher: failed to wait for {}: {}", pid, err),
        }
    }

    loop {
        let mut status = 0;
        let pid = wait(&mut status);
        if pid == 0 {
            break;
        } else {
            println!("launcher: reaping zombie {}: {}", pid, ExitStatus::from_raw(status as i32));
        }
    }
}

fn chooser_main(paths: env::Args) {
    for ref path in paths.skip(1) {
        let mut packages = get_packages();

        packages.retain(|package| -> bool {
            for accept in package.accepts.iter() {
                if (accept.starts_with('*') && path.ends_with(&accept[1 ..])) ||
                   (accept.ends_with('*') && path.starts_with(&accept[.. accept.len() - 1])) {
                    return true;
                }
            }
            false
        });

        if packages.len() > 1 {
            let mut window = Window::new(-1, -1, 320, packages.len() as u32 * ICON_SMALL_SIZE as u32, path).expect("launcher: failed to open window");
            let font = Font::find(Some("Sans"), None, None).expect("launcher: failed to open font");

            let mut selected = -1;
            let mut last_left_button = false;

            draw_chooser(&mut window, &font, &packages, selected);
            'choosing: loop {
                for event in window.events() {
                    match event.to_option() {
                        EventOption::Mouse(mouse_event) => {
                            let mut now_selected = -1;

                            let mut y = 0;
                            for (i, _package) in packages.iter().enumerate() {
                                if mouse_event.y >= y && mouse_event.y < y + ICON_SIZE {
                                    now_selected = i as i32;
                                }
                                y += ICON_SMALL_SIZE;
                            }

                            if now_selected != selected {
                                selected = now_selected;
                                draw_chooser(&mut window, &font, &packages, selected);
                            }

                            if ! mouse_event.left_button && last_left_button {
                                let mut y = 0;
                                for package in packages.iter() {
                                    if mouse_event.y >= y && mouse_event.y < y + ICON_SMALL_SIZE {
                                        if let Err(err) = Command::new(&package.binary).arg(path).spawn() {
                                            println!("launcher: failed to launch {}: {}", package.binary, err);
                                        }
                                        break 'choosing;
                                    }
                                    y += ICON_SMALL_SIZE;
                                }
                            }

                            last_left_button = mouse_event.left_button;
                        },
                        EventOption::Quit(_) => break 'choosing,
                        _ => ()
                    }
                }
            }
        } else if let Some(package) = packages.get(0) {
            if let Err(err) = Command::new(&package.binary).arg(&path).spawn() {
                println!("launcher: failed to launch '{}': {}", package.binary, err);
            }
        } else {
            println!("launcher: no application found for '{}'", path);
        }
    }
}

fn main() {
    let paths = env::args();
    if paths.len() > 1 {
        chooser_main(paths);
    } else {
        bar_main();
    }
}
<commit_msg>Simplify drawing in launcher<commit_after>#![deny(warnings)]
#![feature(const_fn)]

extern crate orbclient;
extern crate orbimage;
extern crate orbfont;

pub const ICON_SIZE: i32 = 48;
pub const ICON_SMALL_SIZE: i32 = 32;

#[cfg(target_os = "redox")]
static UI_PATH: &'static str = "/ui";

#[cfg(not(target_os = "redox"))]
static UI_PATH: &'static str = "ui";

#[cfg(not(target_os = "redox"))]
fn wait(status: &mut i32) -> usize {
    extern crate libc;

    use std::io::Error;

    let pid = unsafe { libc::waitpid(0, status as *mut i32, libc::WNOHANG) };
    if pid < 0 {
        panic!("waitpid failed: {}", Error::last_os_error());
    }
    pid as usize
}

#[cfg(target_os = "redox")]
fn wait(status: &mut usize) -> usize {
    extern crate syscall;

    syscall::waitpid(0, status, syscall::WNOHANG).unwrap()
}

fn load_icon(path: &str) -> Image {
    let icon = Image::from_path(path).unwrap_or(Image::default());
    if icon.width() == ICON_SIZE as u32 && icon.height() == ICON_SIZE as u32 {
        icon
    } else {
        icon.resize(ICON_SIZE as u32, ICON_SIZE as u32, orbimage::ResizeType::Lanczos3).unwrap()
    }
}

fn load_icon_small(path: &str) -> Image {
    let icon = Image::from_path(path).unwrap_or(Image::default());
    if icon.width() == ICON_SMALL_SIZE as u32 && icon.height() == ICON_SMALL_SIZE as u32 {
        icon
    } else {
        icon.resize(ICON_SMALL_SIZE as u32, ICON_SMALL_SIZE as u32, orbimage::ResizeType::Lanczos3).unwrap()
    }
}

use std::env;
use std::os::unix::process::ExitStatusExt;
use std::path::Path;
use std::process::{Command, ExitStatus};

use orbclient::{EventOption, Renderer, Window, K_ESC};
use orbimage::Image;
use orbfont::Font;

use package::Package;
use theme::{BAR_COLOR, BAR_HIGHLIGHT_COLOR, TEXT_COLOR, TEXT_HIGHLIGHT_COLOR};

mod package;
mod theme;

fn get_packages() -> Vec<Package> {
    let read_dir = Path::new(&format!("{}/apps/", UI_PATH)).read_dir().expect("failed to read apps directory");

    let mut entries = vec![];
    for dir in read_dir {
        let dir = match dir {
            Ok(x) => x,
            Err(_) => continue,
        };
        let file_name = dir.file_name().to_string_lossy().into_owned();
        if dir.file_type().expect("failed to get file_type").is_file() {
            entries.push(file_name);
        }
    }

    entries.sort();

    let mut packages: Vec<Package> = Vec::new();
    for entry in entries.iter() {
        packages.push(Package::from_path(&format!("{}/apps/{}", UI_PATH, entry)));
    }

    packages
}

fn draw(window: &mut Window, packages: &Vec<Package>, start: &Image, selected: i32){
    window.set(BAR_COLOR);

    let mut x = 0;
    let y = 0;
    let mut i = 0;

    {
        if i == selected {
            window.rect(x as i32, y as i32,
                              start.width() as u32, start.height() as u32,
                              BAR_HIGHLIGHT_COLOR);
        }

        start.draw(window, x as i32, y as i32);

        x += start.width() as i32;
        i += 1;
    }

    for package in packages.iter() {
        if i == selected {
            window.rect(x as i32, y as i32,
                              package.icon.width() as u32, package.icon.height() as u32,
                              BAR_HIGHLIGHT_COLOR);
        }

        package.icon.draw(window, x as i32, y as i32);

        x += package.icon.width() as i32;
        i += 1;
    }

    window.sync();
}

fn draw_chooser(window: &mut Window, font: &Font, packages: &Vec<Package>, selected: i32){
    let w = window.width();

    window.set(BAR_COLOR);

    let mut y = 0;
    for (i, package) in packages.iter().enumerate() {
        if i as i32 == selected {
            window.rect(0, y, w, ICON_SMALL_SIZE as u32, BAR_HIGHLIGHT_COLOR);
        }

        package.icon_small.draw(window, 0, y);

        font.render(&package.name, 16.0).draw(window, ICON_SMALL_SIZE + 8, y + 8, if i as i32 == selected { TEXT_HIGHLIGHT_COLOR } else { TEXT_COLOR });

        y += ICON_SMALL_SIZE;
    }

    window.sync();
}

fn bar_main() {
    let mut children = Vec::new();

    let packages = get_packages();

    let start = load_icon(&format!("{}/icons/places/start-here.png", UI_PATH));

    let mut logout_package = Package::new();
    logout_package.name = "Logout".to_string();
    logout_package.icon = load_icon(&format!("{}/icons/actions/system-log-out.png", UI_PATH));
    logout_package.icon_small = load_icon_small(&format!("{}/icons/actions/system-log-out.png", UI_PATH));
    logout_package.binary = "exit".to_string();

    let mut start_packages = packages.clone();
    start_packages.push(logout_package);

    let (width, height) = orbclient::get_display_size().expect("launcher: failed to get display size");
    let mut window = Window::new(0, height as i32 - ICON_SIZE, width, ICON_SIZE as u32, "").expect("launcher: failed to open window");

    let mut selected = -1;
    let mut last_left_button = false;

    draw(&mut window, &packages, &start, selected);
    'running: loop {
        for event in window.events() {
            match event.to_option() {
                EventOption::Mouse(mouse_event) => {
                    let mut now_selected = -1;

                    {
                        let mut x = 0;
                        let y = 0;
                        let mut i = 0;

                        {
                            if mouse_event.y >= y && mouse_event.x >= x &&
                               mouse_event.x < x + start.width() as i32 {
                                   now_selected = i;
                            }
                            x += start.width() as i32;
                            i += 1;
                        }

                        for package in packages.iter() {
                            if mouse_event.y >= y && mouse_event.x >= x &&
                               mouse_event.x < x + package.icon.width() as i32 {
                                now_selected = i;
                            }
                            x += package.icon.width() as i32;
                            i += 1;
                        }
                    }

                    if now_selected != selected {
                        selected = now_selected;
                        draw(&mut window, &packages, &start, selected);
                    }

                    if ! mouse_event.left_button && last_left_button {
                        let mut i = 0;

                        if i == selected {
                            let start_h = start_packages.len() as u32 * ICON_SMALL_SIZE as u32;
                            let mut start_window = Window::new(0, height as i32 - ICON_SIZE - start_h as i32, 320, start_h, "").unwrap();
                            let font = Font::find(Some("Sans"), None, None).unwrap();

                            let mut selected = -1;
                            let mut last_left_button = false;

                            draw_chooser(&mut start_window, &font, &start_packages, selected);
                            'start_choosing: loop {
                                for event in start_window.events() {
                                    match event.to_option() {
                                        EventOption::Mouse(mouse_event) => {
                                            let mut now_selected = -1;

                                            let mut y = 0;
                                            for (j, _package) in start_packages.iter().enumerate() {
                                                if mouse_event.y >= y && mouse_event.y < y + ICON_SMALL_SIZE {
                                                    now_selected = j as i32;
                                                }
                                                y += ICON_SMALL_SIZE;
                                            }

                                            if now_selected != selected {
                                                selected = now_selected;
                                                draw_chooser(&mut start_window, &font, &start_packages, selected);
                                            }

                                            if ! mouse_event.left_button && last_left_button {
                                                let mut y = 0;
                                                for package in start_packages.iter() {
                                                    if mouse_event.y >= y && mouse_event.y < y + ICON_SMALL_SIZE {
                                                        if package.binary == "exit" {
                                                            break 'running;
                                                        } else {
                                                            match Command::new(&package.binary).spawn() {
                                                                Ok(child) => children.push(child),
                                                                Err(err) => println!("launcher: failed to launch {}: {}", package.binary, err)
                                                            }
                                                        }
                                                        break 'start_choosing;
                                                    }
                                                    y += ICON_SMALL_SIZE;
                                                }
                                            }

                                            last_left_button = mouse_event.left_button;
                                        },
                                        EventOption::Key(key_event) => {
                                            match key_event.scancode {
                                                K_ESC => break 'start_choosing,
                                                _ => ()
                                            }
                                        },
                                        EventOption::Focus(focus_event) => if ! focus_event.focused {
                                            break 'start_choosing;
                                        },
                                        EventOption::Quit(_) => break 'start_choosing,
                                        _ => ()
                                    }
                                }
                            }
                        }
                        i += 1;

                        for package in packages.iter() {
                            if i == selected {
                                match Command::new(&package.binary).spawn() {
                                    Ok(child) => children.push(child),
                                    Err(err) => println!("launcher: failed to launch {}: {}", package.binary, err)
                                }
                            }
                            i += 1;
                        }
                    }

                    last_left_button = mouse_event.left_button;
                },
                EventOption::Quit(_) => break 'running,
                _ => ()
            }
        }
    }

    for mut child in children {
        let pid = child.id();
        match child.kill() {
            Ok(()) => (),
            Err(err) => println!("launcher: failed to kill {}: {}", pid, err),
        }
        match child.wait() {
            Ok(status) => println!("launcher: {} exited with {}", pid, status),
            Err(err) => println!("launcher: failed to wait for {}: {}", pid, err),
        }
    }

    loop {
        let mut status = 0;
        let pid = wait(&mut status);
        if pid == 0 {
            break;
        } else {
            println!("launcher: reaping zombie {}: {}", pid, ExitStatus::from_raw(status as i32));
        }
    }
}

fn chooser_main(paths: env::Args) {
    for ref path in paths.skip(1) {
        let mut packages = get_packages();

        packages.retain(|package| -> bool {
            for accept in package.accepts.iter() {
                if (accept.starts_with('*') && path.ends_with(&accept[1 ..])) ||
                   (accept.ends_with('*') && path.starts_with(&accept[.. accept.len() - 1])) {
                    return true;
                }
            }
            false
        });

        if packages.len() > 1 {
            let mut window = Window::new(-1, -1, 320, packages.len() as u32 * ICON_SMALL_SIZE as u32, path).expect("launcher: failed to open window");
            let font = Font::find(Some("Sans"), None, None).expect("launcher: failed to open font");

            let mut selected = -1;
            let mut last_left_button = false;

            draw_chooser(&mut window, &font, &packages, selected);
            'choosing: loop {
                for event in window.events() {
                    match event.to_option() {
                        EventOption::Mouse(mouse_event) => {
                            let mut now_selected = -1;

                            let mut y = 0;
                            for (i, _package) in packages.iter().enumerate() {
                                if mouse_event.y >= y && mouse_event.y < y + ICON_SIZE {
                                    now_selected = i as i32;
                                }
                                y += ICON_SMALL_SIZE;
                            }

                            if now_selected != selected {
                                selected = now_selected;
                                draw_chooser(&mut window, &font, &packages, selected);
                            }

                            if ! mouse_event.left_button && last_left_button {
                                let mut y = 0;
                                for package in packages.iter() {
                                    if mouse_event.y >= y && mouse_event.y < y + ICON_SMALL_SIZE {
                                        if let Err(err) = Command::new(&package.binary).arg(path).spawn() {
                                            println!("launcher: failed to launch {}: {}", package.binary, err);
                                        }
                                        break 'choosing;
                                    }
                                    y += ICON_SMALL_SIZE;
                                }
                            }

                            last_left_button = mouse_event.left_button;
                        },
                        EventOption::Quit(_) => break 'choosing,
                        _ => ()
                    }
                }
            }
        } else if let Some(package) = packages.get(0) {
            if let Err(err) = Command::new(&package.binary).arg(&path).spawn() {
                println!("launcher: failed to launch '{}': {}", package.binary, err);
            }
        } else {
            println!("launcher: no application found for '{}'", path);
        }
    }
}

fn main() {
    let paths = env::args();
    if paths.len() > 1 {
        chooser_main(paths);
    } else {
        bar_main();
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*! The Clone trait for types that cannot be "implicitly copied"

In Rust, some simple types are "implicitly copyable" and when you
assign them or pass them as arguments, the receiver will get a copy,
leaving the original value in place. These types do not require
allocation to copy and do not have finalizers (i.e. they do not
contain owned boxes or implement `Drop`), so the compiler considers
them cheap and safe to copy and automatically implements the `Copy`
trait for them. For other types copies must be made explicitly,
by convention implementing the `Clone` trait and calling the
`clone` method.

*/

pub trait Clone {
    /// Return a deep copy of the owned object tree. Types with shared ownership like managed boxes
    /// are cloned with a shallow copy.
    fn clone(&self) -> Self;
}

impl<T: Clone> Clone for ~T {
    /// Return a deep copy of the owned box.
    #[inline(always)]
    fn clone(&self) -> ~T { ~(**self).clone() }
}

impl<T> Clone for @T {
    /// Return a shallow copy of the managed box.
    #[inline(always)]
    fn clone(&self) -> @T { *self }
}

impl<T> Clone for @mut T {
    /// Return a shallow copy of the managed box.
    #[inline(always)]
    fn clone(&self) -> @mut T { *self }
}

macro_rules! clone_impl(
    ($t:ty) => {
        impl Clone for $t {
            /// Return a deep copy of the value.
            #[inline(always)]
            fn clone(&self) -> $t { *self }
        }
    }
)

clone_impl!(int)
clone_impl!(i8)
clone_impl!(i16)
clone_impl!(i32)
clone_impl!(i64)

clone_impl!(uint)
clone_impl!(u8)
clone_impl!(u16)
clone_impl!(u32)
clone_impl!(u64)

clone_impl!(float)
clone_impl!(f32)
clone_impl!(f64)

clone_impl!(())
clone_impl!(bool)
clone_impl!(char)

pub trait DeepClone {
    /// Return a deep copy of the object tree. Types with shared ownership are also copied via a
    /// deep copy, unlike `Clone`. Note that this is currently unimplemented for managed boxes, as
    /// it would need to handle cycles.
    fn deep_clone(&self) -> Self;
}

macro_rules! deep_clone_impl(
    ($t:ty) => {
        impl DeepClone for $t {
            /// Return a deep copy of the value.
            #[inline(always)]
            fn deep_clone(&self) -> $t { *self }
        }
    }
)

impl<T: DeepClone> DeepClone for ~T {
    /// Return a deep copy of the owned box.
    #[inline(always)]
    fn deep_clone(&self) -> ~T { ~(**self).deep_clone() }
}

deep_clone_impl!(int)
deep_clone_impl!(i8)
deep_clone_impl!(i16)
deep_clone_impl!(i32)
deep_clone_impl!(i64)

deep_clone_impl!(uint)
deep_clone_impl!(u8)
deep_clone_impl!(u16)
deep_clone_impl!(u32)
deep_clone_impl!(u64)

deep_clone_impl!(float)
deep_clone_impl!(f32)
deep_clone_impl!(f64)

deep_clone_impl!(())
deep_clone_impl!(bool)
deep_clone_impl!(char)

#[test]
fn test_owned_clone() {
    let a: ~int = ~5i;
    let b: ~int = a.clone();
    assert!(a == b);
}

#[test]
fn test_managed_clone() {
    let a: @int = @5i;
    let b: @int = a.clone();
    assert!(a == b);
}

#[test]
fn test_managed_mut_clone() {
    let a: @mut int = @mut 5i;
    let b: @mut int = a.clone();
    assert!(a == b);
    *b = 10;
    assert!(a == b);
}
<commit_msg>auto merge of #6527 : thestinger/rust/deep_clone, r=pcwalton<commit_after>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*! The Clone trait for types that cannot be "implicitly copied"

In Rust, some simple types are "implicitly copyable" and when you
assign them or pass them as arguments, the receiver will get a copy,
leaving the original value in place. These types do not require
allocation to copy and do not have finalizers (i.e. they do not
contain owned boxes or implement `Drop`), so the compiler considers
them cheap and safe to copy and automatically implements the `Copy`
trait for them. For other types copies must be made explicitly,
by convention implementing the `Clone` trait and calling the
`clone` method.

*/

use core::kinds::Const;

pub trait Clone {
    /// Return a deep copy of the owned object tree. Types with shared ownership like managed boxes
    /// are cloned with a shallow copy.
    fn clone(&self) -> Self;
}

impl<T: Clone> Clone for ~T {
    /// Return a deep copy of the owned box.
    #[inline(always)]
    fn clone(&self) -> ~T { ~(**self).clone() }
}

impl<T> Clone for @T {
    /// Return a shallow copy of the managed box.
    #[inline(always)]
    fn clone(&self) -> @T { *self }
}

impl<T> Clone for @mut T {
    /// Return a shallow copy of the managed box.
    #[inline(always)]
    fn clone(&self) -> @mut T { *self }
}

macro_rules! clone_impl(
    ($t:ty) => {
        impl Clone for $t {
            /// Return a deep copy of the value.
            #[inline(always)]
            fn clone(&self) -> $t { *self }
        }
    }
)

clone_impl!(int)
clone_impl!(i8)
clone_impl!(i16)
clone_impl!(i32)
clone_impl!(i64)

clone_impl!(uint)
clone_impl!(u8)
clone_impl!(u16)
clone_impl!(u32)
clone_impl!(u64)

clone_impl!(float)
clone_impl!(f32)
clone_impl!(f64)

clone_impl!(())
clone_impl!(bool)
clone_impl!(char)

pub trait DeepClone {
    /// Return a deep copy of the object tree. Types with shared ownership are also copied via a
    /// deep copy, unlike `Clone`.
    fn deep_clone(&self) -> Self;
}

impl<T: DeepClone> DeepClone for ~T {
    /// Return a deep copy of the owned box.
    #[inline(always)]
    fn deep_clone(&self) -> ~T { ~(**self).deep_clone() }
}

// FIXME: #6525: should also be implemented for `T: Owned + DeepClone`
impl<T: Const + DeepClone> DeepClone for @T {
    /// Return a deep copy of the managed box. The `Const` trait is required to prevent performing
    /// a deep clone of a potentially cyclical type.
    #[inline(always)]
    fn deep_clone(&self) -> @T { @(**self).deep_clone() }
}

// FIXME: #6525: should also be implemented for `T: Owned + DeepClone`
impl<T: Const + DeepClone> DeepClone for @mut T {
    /// Return a deep copy of the managed box. The `Const` trait is required to prevent performing
    /// a deep clone of a potentially cyclical type.
    #[inline(always)]
    fn deep_clone(&self) -> @mut T { @mut (**self).deep_clone() }
}

macro_rules! deep_clone_impl(
    ($t:ty) => {
        impl DeepClone for $t {
            /// Return a deep copy of the value.
            #[inline(always)]
            fn deep_clone(&self) -> $t { *self }
        }
    }
)

deep_clone_impl!(int)
deep_clone_impl!(i8)
deep_clone_impl!(i16)
deep_clone_impl!(i32)
deep_clone_impl!(i64)

deep_clone_impl!(uint)
deep_clone_impl!(u8)
deep_clone_impl!(u16)
deep_clone_impl!(u32)
deep_clone_impl!(u64)

deep_clone_impl!(float)
deep_clone_impl!(f32)
deep_clone_impl!(f64)

deep_clone_impl!(())
deep_clone_impl!(bool)
deep_clone_impl!(char)

#[test]
fn test_owned_clone() {
    let a = ~5i;
    let b: ~int = a.clone();
    assert!(a == b);
}

#[test]
fn test_managed_clone() {
    let a = @5i;
    let b: @int = a.clone();
    assert!(a == b);
}

#[test]
fn test_managed_mut_deep_clone() {
    let x = @mut 5i;
    let y: @mut int = x.deep_clone();
    *x = 20;
    assert_eq!(*y, 5);
}

#[test]
fn test_managed_mut_clone() {
    let a = @mut 5i;
    let b: @mut int = a.clone();
    assert!(a == b);
    *b = 10;
    assert!(a == b);
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::any::Any;
use std::marker::PhantomData;
use serde::{Serialize, Serializer, Error, de};

use {ExtendedTime, StewardRc, FieldRc, ColumnId, Column, Basics};

pub trait ColumnListUser {
  fn apply<C: Column>(&mut self);
}
pub trait ColumnList: Any {
  fn apply<U: ColumnListUser>(user: &mut U);
}
impl<C: Column> ColumnList for PhantomData<C> {
  #[inline]
  fn apply<U: ColumnListUser>(user: &mut U) {
    user.apply::<C>();
  }
}
macro_rules! tuple_impls {
  ($TL: ident $(, $T: ident)*) => {
    impl<$($T,)* $TL> ColumnList for ($($T,)* $TL,)
      where $($T: ColumnList,)* $TL: ColumnList
    {
      #[inline]
      fn apply <U: ColumnListUser> (user: &mut U) {
        $($T::apply(user);)*
        $TL::apply(user);
      }
    }
    tuple_impls! ($($T),*);
  };
  () => {};
}
tuple_impls! (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, T31);
// Today I Learned that macro hygiene is not applied to type parameter lists
//
// macro_rules! escalate {
// ([$first:tt $($whatever:tt)*] $($T: ident)*) => {escalate! ([$($whatever)*] foo $($T)*);};
// ([] $($T: ident)*) => {tuple_impls! ($($T),*);};
// }
// escalate! ([!!!!!!!! !!!!!!!! !!!!!!!! !!!!!!!!]);
//

fn check_equality<C: Column>(first: &FieldRc, second: &FieldRc)->bool {
  ::unwrap_field::<C>(first) == ::unwrap_field::<C>(second)
}
pub struct FieldEqualityTable (HashMap<ColumnId, fn(&FieldRc, &FieldRc)->bool>);
impl ColumnListUser for FieldEqualityTable{
  fn apply<C: Column>(&mut self) {
    self.0.insert(C::column_id(), check_equality::<C>);
  }
}
impl FieldEqualityTable{
  pub fn new <C: ColumnList>()->FieldEqualityTable{
    let mut result = FieldEqualityTable(HashMap::new());
    C::apply (&mut result);
    result
  }
  pub fn fields_are_equal(&self, column_id: ColumnId, first: & FieldRc, second: & FieldRc)->bool {
    (self.0.get (&column_id).expect ("Column missing from equality table; did you forget to list a column in Basics::Columns?")) (first, second)
  }
  pub fn options_are_equal(&self, column_id: ColumnId, first: Option <& FieldRc>, second: Option <& FieldRc>)->bool {
    match (first, second) {
      (None, None) => true,
      (Some (first), Some (second)) => self.fields_are_equal (column_id, first, second),
      _ => false,
    }
  }
}

  fn serialize_column<C: Column, S: Serializer>(field: &FieldRc,
                                                serializer: &mut S)
                                                -> Result<(), S::Error> {
    try!(::unwrap_field::<C>(field).serialize(serializer));
    Ok(())
  }

  fn deserialize_column<B: Basics, C: Column, M: de::MapVisitor>
    (visitor: &mut M)
     -> Result<(FieldRc, ExtendedTime<B>), M::Error> {
    let (data, time) = try!(visitor.visit_value::<(C::FieldType, ExtendedTime<B>)>());
    Ok((StewardRc::new(data), time))
  }

  pub struct FieldSerializationTable<S: Serializer>(HashMap<ColumnId,
                                                           fn(&FieldRc, &mut S)
                                                              -> Result<(), S::Error>>);
  pub struct FieldDeserializationTable <B: Basics, M: de::MapVisitor> (HashMap<ColumnId, fn (&mut M)->Result <(FieldRc, ExtendedTime <B>), M::Error>>);

  impl<S: Serializer> ColumnListUser for FieldSerializationTable<S> {
    fn apply<C: Column>(&mut self) {
      self.0.insert(C::column_id(), serialize_column::<C, S>);
    }
  }
  impl<B: Basics, M: de::MapVisitor> ColumnListUser for FieldDeserializationTable <B, M> {
    fn apply<C: Column>(&mut self) {
      self.0.insert(C::column_id(), deserialize_column::<B, C, M>);
    }
  }
  impl<S: Serializer> FieldSerializationTable<S> {
  pub fn new <C: ColumnList>()->FieldSerializationTable <S> {
    let mut result = FieldSerializationTable(HashMap::new());
    C::apply (&mut result);
    result
  }
  pub fn serialize_field(&self, column_id: ColumnId, first: & FieldRc,
                                                  serializer: &mut S)->Result<(), S::Error>{
    use serde::ser::Error;
    try!(self.0.get (&column_id).ok_or (S::Error::custom ("Column missing from serialization table; did you forget to list a column in Basics::Columns?"))) (first, serializer)
  }
  }
  impl<B: Basics, M: de::MapVisitor> FieldDeserializationTable <B, M> {
  pub fn new <C: ColumnList>()->FieldDeserializationTable <B, M> {
    let mut result = FieldDeserializationTable (HashMap::new());
    C::apply (&mut result);
    result
  }
  pub fn deserialize_field(&self, column_id: ColumnId, visitor: &mut M)->Result<(FieldRc, ExtendedTime<B>), M::Error>{
    try!(self.0.get (&column_id).ok_or (M::Error::custom ("Column missing from deserialization table; did you forget to list a column in Basics::Columns?"))) (visitor)
  }
  }

<commit_msg>Abstracted over the trait we're making a list of implementors for<commit_after>use std::collections::HashMap;
use std::any::Any;
use std::marker::PhantomData;
use serde::{Serialize, Serializer, Error, de};

use {ExtendedTime, StewardRc, FieldRc, ColumnId, Column, Basics};

macro_rules! type_list_definitions {
($Trait: ident, $List: ident, $User: ident, $field: ident) => {
pub trait $User {
  fn apply<C: $Trait>(&mut self);
}
pub trait $List: Any {
  fn apply<U: $User>(user: &mut U);
}
impl<C: Column> $List for PhantomData<C> {
  #[inline]
  fn apply<U: $User>(user: &mut U) {
    user.apply::<C>();
  }
}
tuple_impls! ($List, $User, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, T31);
}
}
macro_rules! tuple_impls {
  ($List: ident, $User: ident, $TL: ident $(, $T: ident)*) => {
    impl<$($T,)* $TL> $List for ($($T,)* $TL,)
      where $($T: $List,)* $TL: ColumnList
    {
      #[inline]
      fn apply <U: $User> (user: &mut U) {
        $($T::apply(user);)*
        $TL::apply(user);
      }
    }
    tuple_impls! ($List, $User, $($T),*);
  };
  ($List: ident, $User: ident,) => {};
}
type_list_definitions! (Column, ColumnList, ColumnListUser, whatever);

// Today I Learned that macro hygiene is not applied to type parameter lists
//
// macro_rules! escalate {
// ([$first:tt $($whatever:tt)*] $($T: ident)*) => {escalate! ([$($whatever)*] foo $($T)*);};
// ([] $($T: ident)*) => {tuple_impls! ($($T),*);};
// }
// escalate! ([!!!!!!!! !!!!!!!! !!!!!!!! !!!!!!!!]);
//

fn check_equality<C: Column>(first: &FieldRc, second: &FieldRc)->bool {
  ::unwrap_field::<C>(first) == ::unwrap_field::<C>(second)
}
pub struct FieldEqualityTable (HashMap<ColumnId, fn(&FieldRc, &FieldRc)->bool>);
impl ColumnListUser for FieldEqualityTable{
  fn apply<C: Column>(&mut self) {
    self.0.insert(C::column_id(), check_equality::<C>);
  }
}
impl FieldEqualityTable{
  pub fn new <C: ColumnList>()->FieldEqualityTable{
    let mut result = FieldEqualityTable(HashMap::new());
    C::apply (&mut result);
    result
  }
  pub fn fields_are_equal(&self, column_id: ColumnId, first: & FieldRc, second: & FieldRc)->bool {
    (self.0.get (&column_id).expect ("Column missing from equality table; did you forget to list a column in Basics::Columns?")) (first, second)
  }
  pub fn options_are_equal(&self, column_id: ColumnId, first: Option <& FieldRc>, second: Option <& FieldRc>)->bool {
    match (first, second) {
      (None, None) => true,
      (Some (first), Some (second)) => self.fields_are_equal (column_id, first, second),
      _ => false,
    }
  }
}

  fn serialize_column<C: Column, S: Serializer>(field: &FieldRc,
                                                serializer: &mut S)
                                                -> Result<(), S::Error> {
    try!(::unwrap_field::<C>(field).serialize(serializer));
    Ok(())
  }

  fn deserialize_column<B: Basics, C: Column, M: de::MapVisitor>
    (visitor: &mut M)
     -> Result<(FieldRc, ExtendedTime<B>), M::Error> {
    let (data, time) = try!(visitor.visit_value::<(C::FieldType, ExtendedTime<B>)>());
    Ok((StewardRc::new(data), time))
  }

  pub struct FieldSerializationTable<S: Serializer>(HashMap<ColumnId,
                                                           fn(&FieldRc, &mut S)
                                                              -> Result<(), S::Error>>);
  pub struct FieldDeserializationTable <B: Basics, M: de::MapVisitor> (HashMap<ColumnId, fn (&mut M)->Result <(FieldRc, ExtendedTime <B>), M::Error>>);

  impl<S: Serializer> ColumnListUser for FieldSerializationTable<S> {
    fn apply<C: Column>(&mut self) {
      self.0.insert(C::column_id(), serialize_column::<C, S>);
    }
  }
  impl<B: Basics, M: de::MapVisitor> ColumnListUser for FieldDeserializationTable <B, M> {
    fn apply<C: Column>(&mut self) {
      self.0.insert(C::column_id(), deserialize_column::<B, C, M>);
    }
  }
  impl<S: Serializer> FieldSerializationTable<S> {
  pub fn new <C: ColumnList>()->FieldSerializationTable <S> {
    let mut result = FieldSerializationTable(HashMap::new());
    C::apply (&mut result);
    result
  }
  pub fn serialize_field(&self, column_id: ColumnId, first: & FieldRc,
                                                  serializer: &mut S)->Result<(), S::Error>{
    use serde::ser::Error;
    try!(self.0.get (&column_id).ok_or (S::Error::custom ("Column missing from serialization table; did you forget to list a column in Basics::Columns?"))) (first, serializer)
  }
  }
  impl<B: Basics, M: de::MapVisitor> FieldDeserializationTable <B, M> {
  pub fn new <C: ColumnList>()->FieldDeserializationTable <B, M> {
    let mut result = FieldDeserializationTable (HashMap::new());
    C::apply (&mut result);
    result
  }
  pub fn deserialize_field(&self, column_id: ColumnId, visitor: &mut M)->Result<(FieldRc, ExtendedTime<B>), M::Error>{
    try!(self.0.get (&column_id).ok_or (M::Error::custom ("Column missing from deserialization table; did you forget to list a column in Basics::Columns?"))) (visitor)
  }
  }

<|endoftext|>


<commit_before>// see 'rustc -W help'
#![warn(
    missing_docs,
    unused,
    unused_results,
    nonstandard_style,
    rust_2018_compatibility,
    rust_2018_idioms
)]

//! Test all of the tests in chats/, which are .txt files formatted with IRC
//! input beginning with <, expected IRC output beginning with >, and expected
//! github output beginning with !.

use futures::prelude::*;
use futures::task::Poll;
use irc::client::prelude::{Client as IrcClient, Config as IrcConfig};
use lazy_static::lazy_static;
use log::{debug, info};
use std::cell::{Cell, RefCell};
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::str;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::net::TcpListener;
use tokio::time::{Duration, Instant};
use wgmeeting_github_ircbot::*;

const MOCK_SERVER_HOST: &str = "127.0.0.1";
const MOCK_SERVER_PORT: u16 = 43210;

#[tokio::test]
async fn test_chats() -> Result<(), failure::Error> {
    env_logger::init();

    let chats_dir = Path::new(file!()).parent().unwrap().join("chats");
    info!("Going through {:?}", chats_dir);
    let mut fail_count = 0;
    for direntry in chats_dir.read_dir()? {
        if let Ok(direntry) = direntry {
            if !test_one_chat(direntry.path().as_path()).await? {
                fail_count += 1;
            }
        }
    }
    assert!(
        fail_count == 0,
        format!("{} chat test failure(s), see above", fail_count)
    );

    Ok(())
}

async fn test_one_chat(path: &Path) -> Result<bool, failure::Error> {
    info!("Testing {:?}", path);

    // We're given the path to a file (the chat file) that represents a dialog between the bot
    // and other users on the IRC server, and also contains the comments the bot makes on github
    // issues.

    // All of the lines in the chat file, as a vec (lines) of vecs (bytes).
    let chat_file_lines = {
        let mut file_bytes: Vec<u8> = Vec::new();
        let _size = File::open(path)?.read_to_end(&mut file_bytes)?;
        file_bytes
    }
    .split(|byte| *byte == '\n' as u8)
    .map(|arr| arr.to_vec())
    .collect::<Vec<Vec<u8>>>();

    let is_finished = Cell::new(false);

    let server = mock_irc_server(&chat_file_lines, &is_finished);
    let bot = run_irc_bot(&is_finished);

    let (actual_lines, bot_result) = future::join(server, bot).await;
    let _ = bot_result?;
    let actual_lines = actual_lines?;

    let actual_str = str::from_utf8(actual_lines.as_slice())?;
    let expected_lines = chat_lines_to_expected_lines(&chat_file_lines);
    let expected_str = str::from_utf8(expected_lines.as_slice())?;
    let test_pass = actual_str == expected_str;
    println!("\n{:?} {}", path, if test_pass { "PASS" } else { "FAIL" });
    if !test_pass {
        for d in diff::lines(expected_str, actual_str) {
            match d {
                diff::Result::Left(actual) => println!("-{}", actual),
                diff::Result::Both(actual, _) => println!(" {}", actual),
                diff::Result::Right(expected) => println!("+{}", expected),
            }
        }
    }

    Ok(test_pass)
}

/// Run the fake IRC server for the chat test, driving the dialog based on the chat file.
/// Record the entire conversation and return that recording for comparison with the expected
/// result.
async fn mock_irc_server(
    chat_file_lines: &Vec<Vec<u8>>,
    is_finished: &Cell<bool>,
) -> Result<Vec<u8>, failure::Error> {
    let actual_lines = RefCell::new(Vec::<u8>::new());

    struct WaitLinesData {
        expect_lines: i32,
        wait_deadline: Instant,
    };

    impl WaitLinesData {
        pub fn should_wait(&self) -> bool {
            let time_remains = self.wait_deadline > Instant::now();
            let result = self.expect_lines > 0 && time_remains;
            debug!(
                "should_wait: expect_lines={}, time_remains={} ==> {}",
                self.expect_lines, time_remains, result
            );
            if !time_remains {
                info!("wait for {} lines timed out", self.expect_lines);
            }
            result
        }
    };

    const WAIT_DURATION: Duration = Duration::from_millis(100u64);
    const SERVER_SHUTDOWN_DURATION: Duration = Duration::from_millis(10u64);

    let wait_lines_data = RefCell::new(WaitLinesData {
        expect_lines: 3, // length of identify sequence
        wait_deadline: Instant::now() + WAIT_DURATION,
    });

    let irc_server_addr = format!("{}:{}", MOCK_SERVER_HOST, MOCK_SERVER_PORT);
    let mut irc_server_listener = TcpListener::bind(&irc_server_addr).await?;
    let (mut tcp_stream, _socket_addr) = irc_server_listener.accept().await?;
    tcp_stream.set_nodelay(true)?;
    debug!(
        "IRC server got incoming connection: nodelay={} recv_buffer_size={} send_buffer_size={}",
        tcp_stream.nodelay()?,
        tcp_stream.recv_buffer_size()?,
        tcp_stream.send_buffer_size()?
    );
    let (reader, mut writer) = tcp_stream.split();

    let reader_future = async {
        let mut lines = BufReader::new(reader).lines();
        while let Some(line) = lines.next_line().await? {
            if line.starts_with("PING ") {
                continue;
            }
            debug!("IRC server read line: {}", line);

            {
                let mut wait_lines_data = wait_lines_data.borrow_mut();
                wait_lines_data.expect_lines = wait_lines_data.expect_lines - 1;
            }

            {
                let mut actual_lines = actual_lines.borrow_mut();
                actual_lines.append(&mut ">".bytes().collect());
                actual_lines.extend_from_slice(
                    line.chars()
                        .flat_map(|c| c.escape_default())
                        .collect::<String>()
                        .as_bytes(),
                );
                actual_lines.append(&mut "\r\n".bytes().collect());
            }
        }

        Ok::<(), failure::Error>(())
    };

    let writer_future = async {
        for line in chat_file_lines.iter() {
            let first_char = line.first().map(|b| *b as char);
            if first_char == Some('>') || first_char == Some('!') {
                // This is a line we should expect to recieve from the bot.  Note this in
                // |wait_lines_data|, which |reader_future| will use to adjust its timing.
                let mut wait_lines_data = wait_lines_data.borrow_mut();
                wait_lines_data.expect_lines = wait_lines_data.expect_lines + 1;
                wait_lines_data.wait_deadline = Instant::now() + WAIT_DURATION;
            }

            if first_char != Some('<') {
                continue;
            }

            while wait_lines_data.borrow().should_wait() {
                tokio::time::delay_for(Duration::from_millis(1)).await;
            }

            // note that line still begins with '<'
            // FIXME: Clean up this total hack for \u{1} !
            // (The other direction uses escape_default().)
            let mut line_str = str::from_utf8(&line[1..])?.replace("\\u{1}", "\u{1}");
            debug!("IRC server writing line: {}", line_str);
            line_str.push_str("\r\n");

            {
                let mut actual_lines = actual_lines.borrow_mut();
                actual_lines.extend_from_slice(&line);
                actual_lines.append(&mut "\r\n".bytes().collect());
            }

            let _ = writer.write_all(line_str.as_bytes()).await?;
        }

        tokio::time::delay_for(SERVER_SHUTDOWN_DURATION).await;

        debug!("SHUTTING DOWN THE SERVER");
        is_finished.set(true);
        // This seems (to my surprise) to be good enough to make the reader terminate as well.
        writer.shutdown().await?;

        Ok::<(), failure::Error>(())
    };

    let (reader_result, writer_result) = future::join(reader_future, writer_future).await;
    reader_result?;
    writer_result?;

    Ok(actual_lines.into_inner())
}

/// Run the IRC bot side of the chat test (i.e., the code we're testing).
async fn run_irc_bot(is_finished: &Cell<bool>) -> Result<(), failure::Error> {
    let irc_config = IrcConfig {
        use_mock_connection: false,
        owners: vec![format!("dbaron")],
        nickname: Some(format!("test-github-bot")),
        alt_nicks: vec![format!("test-github-bot-"), format!("test-github-bot--")],
        username: Some(format!("dbaron-gh-bot")),
        realname: Some(format!("Bot to add meeting minutes to github issues.")),
        server: Some(format!("{}", MOCK_SERVER_HOST)),
        port: Some(MOCK_SERVER_PORT),
        use_tls: Some(false),
        encoding: Some(format!("UTF-8")),
        channels: vec![format!("#meetingbottest"), format!("#testchannel2")],
        user_info: Some(format!("Bot to add meeting minutes to github issues.")),

        // In testing mode, we send the github comments as IRC messages, so we
        // need to be able to handle more substantial bursts of messages
        // without delay.
        burst_window_length: Some(0),
        max_messages_in_burst: Some(50),
        ..Default::default()
    };
    lazy_static! {
        static ref BOT_CONFIG: BotConfig = BotConfig {
            source: "https://github.com/dbaron/wgmeeting-github-ircbot".to_string(),
            channels: vec![
                (format!("#meetingbottest"), ChannelConfig {
                    group: format!("Bot-Testing Working Group"),
                    github_repos_allowed: vec![
                        "dbaron/wgmeeting-github-ircbot".to_string(),
                        "dbaron/nonexistentrepo".to_string(),
                        "upsuper/*".to_string(),
                    ],
                    publish_resolutions_only: false,
                }),
                (format!("#testchannel2"), ChannelConfig {
                    group: format!("Second Bot-Testing Working Group"),
                    github_repos_allowed: vec![
                        "dbaron/wgmeeting-github-ircbot".to_string(),
                    ],
                    publish_resolutions_only: false,
                }),
            ].into_iter().collect(),
            // Use of a 0 value disables timeouts, which is needed to avoid intermittent
            // failures (using really-0 timeouts) or having the event loop wait until the
            // timeout completes (positive timeouts).
            activity_timeout_minutes: 0,
            owners: vec![format!("dbaron")],
            ..Default::default()
        };
    }

    let mut irc_state = IRCState::new(GithubType::MockGithubConnection);

    let irc_client: &'static mut _ = Box::leak(Box::new(IrcClient::from_config(irc_config).await?));

    irc_client.identify()?;

    let finished_cb = {
        future::poll_fn(move |_cx| {
            if is_finished.get() {
                debug!("in take_until callback for messages stream: terminating");
                Poll::Ready(())
            } else {
                debug!("in take_until callback for messages stream: continuing");
                Poll::Pending
            }
        })
    };

    let mut irc_stream = irc_client.stream()?.take_until(finished_cb);
    while let Some(message) = irc_stream.next().await.transpose()? {
        // This is where we call into the IRC bot's logic, i.e., where we call the code that's
        // being tested here.
        process_irc_message(irc_client, &mut irc_state, &BOT_CONFIG, message);
    }

    Ok(())
}

/// Convert the lines in the chat file to the dialog that the test should expect to have been
/// recorded by the IRC server.
fn chat_lines_to_expected_lines(chat_file_lines: &Vec<Vec<u8>>) -> Vec<u8> {
    let mut expected_lines = ">CAP END\r\n>NICK test-github-bot\r\n>USER dbaron-gh-bot 0 * :Bot to add meeting minutes to github issues.\r\n".bytes().collect::<Vec<u8>>();

    for line in chat_file_lines.iter() {
        match line.first().map(|b| *b as char) {
            // just skip blank lines (and the empty string after the last
            // line, given the use of split)
            None => (),
            Some('<') => {
                let line = str::from_utf8(line).unwrap();
                expected_lines.extend_from_slice(line.as_bytes());
                expected_lines.append(&mut "\r\n".bytes().collect());
            }
            Some('>') => {
                expected_lines.extend(
                    str::from_utf8(line)
                        .unwrap()
                        .replace("[[CODE_DESCRIPTION]]", &*code_description())
                        .bytes(),
                );
                expected_lines.append(&mut "\r\n".bytes().collect());
            }
            Some('!') => {
                // for now, we send the github comments over IRC when
                // testing, but we don't encode that into the chat
                // format
                expected_lines.append(&mut ">PRIVMSG github-comments ".bytes().collect());
                // Match use of ":" in the stringify function in irc-proto's
                // src/command.rs.
                if line.len() == 1 || line.contains(&0x20u8 /* space */) {
                    expected_lines.append(&mut ":".bytes().collect());
                }
                expected_lines.extend_from_slice(&line[1..]);
                expected_lines.append(&mut "\r\n".bytes().collect());
            }
            _ => {
                panic!(
                    //"Unexpected line in test file {:?}:\n{}",
                    // path,
                    "Unexpected line in test file:\n{}",
                    str::from_utf8(line).unwrap_or("[non-UTF-8 line]")
                );
            }
        }
    }

    expected_lines
}
<commit_msg>Fix new-ish rust warnings (reported by rustc 1.52.1).<commit_after>// see 'rustc -W help'
#![warn(
    missing_docs,
    unused,
    unused_results,
    nonstandard_style,
    rust_2018_compatibility,
    rust_2018_idioms
)]

//! Test all of the tests in chats/, which are .txt files formatted with IRC
//! input beginning with <, expected IRC output beginning with >, and expected
//! github output beginning with !.

use futures::prelude::*;
use futures::task::Poll;
use irc::client::prelude::{Client as IrcClient, Config as IrcConfig};
use lazy_static::lazy_static;
use log::{debug, info};
use std::cell::{Cell, RefCell};
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::str;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::net::TcpListener;
use tokio::time::{Duration, Instant};
use wgmeeting_github_ircbot::*;

const MOCK_SERVER_HOST: &str = "127.0.0.1";
const MOCK_SERVER_PORT: u16 = 43210;

#[tokio::test]
async fn test_chats() -> Result<(), failure::Error> {
    env_logger::init();

    let chats_dir = Path::new(file!()).parent().unwrap().join("chats");
    info!("Going through {:?}", chats_dir);
    let mut fail_count = 0;
    for direntry in chats_dir.read_dir()? {
        if let Ok(direntry) = direntry {
            if !test_one_chat(direntry.path().as_path()).await? {
                fail_count += 1;
            }
        }
    }
    assert!(
        fail_count == 0,
        "{} chat test failure(s), see above",
        fail_count
    );

    Ok(())
}

async fn test_one_chat(path: &Path) -> Result<bool, failure::Error> {
    info!("Testing {:?}", path);

    // We're given the path to a file (the chat file) that represents a dialog between the bot
    // and other users on the IRC server, and also contains the comments the bot makes on github
    // issues.

    // All of the lines in the chat file, as a vec (lines) of vecs (bytes).
    let chat_file_lines = {
        let mut file_bytes: Vec<u8> = Vec::new();
        let _size = File::open(path)?.read_to_end(&mut file_bytes)?;
        file_bytes
    }
    .split(|byte| *byte == '\n' as u8)
    .map(|arr| arr.to_vec())
    .collect::<Vec<Vec<u8>>>();

    let is_finished = Cell::new(false);

    let server = mock_irc_server(&chat_file_lines, &is_finished);
    let bot = run_irc_bot(&is_finished);

    let (actual_lines, bot_result) = future::join(server, bot).await;
    let _ = bot_result?;
    let actual_lines = actual_lines?;

    let actual_str = str::from_utf8(actual_lines.as_slice())?;
    let expected_lines = chat_lines_to_expected_lines(&chat_file_lines);
    let expected_str = str::from_utf8(expected_lines.as_slice())?;
    let test_pass = actual_str == expected_str;
    println!("\n{:?} {}", path, if test_pass { "PASS" } else { "FAIL" });
    if !test_pass {
        for d in diff::lines(expected_str, actual_str) {
            match d {
                diff::Result::Left(actual) => println!("-{}", actual),
                diff::Result::Both(actual, _) => println!(" {}", actual),
                diff::Result::Right(expected) => println!("+{}", expected),
            }
        }
    }

    Ok(test_pass)
}

/// Run the fake IRC server for the chat test, driving the dialog based on the chat file.
/// Record the entire conversation and return that recording for comparison with the expected
/// result.
async fn mock_irc_server(
    chat_file_lines: &Vec<Vec<u8>>,
    is_finished: &Cell<bool>,
) -> Result<Vec<u8>, failure::Error> {
    let actual_lines = RefCell::new(Vec::<u8>::new());

    struct WaitLinesData {
        expect_lines: i32,
        wait_deadline: Instant,
    }

    impl WaitLinesData {
        pub fn should_wait(&self) -> bool {
            let time_remains = self.wait_deadline > Instant::now();
            let result = self.expect_lines > 0 && time_remains;
            debug!(
                "should_wait: expect_lines={}, time_remains={} ==> {}",
                self.expect_lines, time_remains, result
            );
            if !time_remains {
                info!("wait for {} lines timed out", self.expect_lines);
            }
            result
        }
    }

    const WAIT_DURATION: Duration = Duration::from_millis(100u64);
    const SERVER_SHUTDOWN_DURATION: Duration = Duration::from_millis(10u64);

    let wait_lines_data = RefCell::new(WaitLinesData {
        expect_lines: 3, // length of identify sequence
        wait_deadline: Instant::now() + WAIT_DURATION,
    });

    let irc_server_addr = format!("{}:{}", MOCK_SERVER_HOST, MOCK_SERVER_PORT);
    let mut irc_server_listener = TcpListener::bind(&irc_server_addr).await?;
    let (mut tcp_stream, _socket_addr) = irc_server_listener.accept().await?;
    tcp_stream.set_nodelay(true)?;
    debug!(
        "IRC server got incoming connection: nodelay={} recv_buffer_size={} send_buffer_size={}",
        tcp_stream.nodelay()?,
        tcp_stream.recv_buffer_size()?,
        tcp_stream.send_buffer_size()?
    );
    let (reader, mut writer) = tcp_stream.split();

    let reader_future = async {
        let mut lines = BufReader::new(reader).lines();
        while let Some(line) = lines.next_line().await? {
            if line.starts_with("PING ") {
                continue;
            }
            debug!("IRC server read line: {}", line);

            {
                let mut wait_lines_data = wait_lines_data.borrow_mut();
                wait_lines_data.expect_lines = wait_lines_data.expect_lines - 1;
            }

            {
                let mut actual_lines = actual_lines.borrow_mut();
                actual_lines.append(&mut ">".bytes().collect());
                actual_lines.extend_from_slice(
                    line.chars()
                        .flat_map(|c| c.escape_default())
                        .collect::<String>()
                        .as_bytes(),
                );
                actual_lines.append(&mut "\r\n".bytes().collect());
            }
        }

        Ok::<(), failure::Error>(())
    };

    let writer_future = async {
        for line in chat_file_lines.iter() {
            let first_char = line.first().map(|b| *b as char);
            if first_char == Some('>') || first_char == Some('!') {
                // This is a line we should expect to recieve from the bot.  Note this in
                // |wait_lines_data|, which |reader_future| will use to adjust its timing.
                let mut wait_lines_data = wait_lines_data.borrow_mut();
                wait_lines_data.expect_lines = wait_lines_data.expect_lines + 1;
                wait_lines_data.wait_deadline = Instant::now() + WAIT_DURATION;
            }

            if first_char != Some('<') {
                continue;
            }

            while wait_lines_data.borrow().should_wait() {
                tokio::time::delay_for(Duration::from_millis(1)).await;
            }

            // note that line still begins with '<'
            // FIXME: Clean up this total hack for \u{1} !
            // (The other direction uses escape_default().)
            let mut line_str = str::from_utf8(&line[1..])?.replace("\\u{1}", "\u{1}");
            debug!("IRC server writing line: {}", line_str);
            line_str.push_str("\r\n");

            {
                let mut actual_lines = actual_lines.borrow_mut();
                actual_lines.extend_from_slice(&line);
                actual_lines.append(&mut "\r\n".bytes().collect());
            }

            let _ = writer.write_all(line_str.as_bytes()).await?;
        }

        tokio::time::delay_for(SERVER_SHUTDOWN_DURATION).await;

        debug!("SHUTTING DOWN THE SERVER");
        is_finished.set(true);
        // This seems (to my surprise) to be good enough to make the reader terminate as well.
        writer.shutdown().await?;

        Ok::<(), failure::Error>(())
    };

    let (reader_result, writer_result) = future::join(reader_future, writer_future).await;
    reader_result?;
    writer_result?;

    Ok(actual_lines.into_inner())
}

/// Run the IRC bot side of the chat test (i.e., the code we're testing).
async fn run_irc_bot(is_finished: &Cell<bool>) -> Result<(), failure::Error> {
    let irc_config = IrcConfig {
        use_mock_connection: false,
        owners: vec![format!("dbaron")],
        nickname: Some(format!("test-github-bot")),
        alt_nicks: vec![format!("test-github-bot-"), format!("test-github-bot--")],
        username: Some(format!("dbaron-gh-bot")),
        realname: Some(format!("Bot to add meeting minutes to github issues.")),
        server: Some(format!("{}", MOCK_SERVER_HOST)),
        port: Some(MOCK_SERVER_PORT),
        use_tls: Some(false),
        encoding: Some(format!("UTF-8")),
        channels: vec![format!("#meetingbottest"), format!("#testchannel2")],
        user_info: Some(format!("Bot to add meeting minutes to github issues.")),

        // In testing mode, we send the github comments as IRC messages, so we
        // need to be able to handle more substantial bursts of messages
        // without delay.
        burst_window_length: Some(0),
        max_messages_in_burst: Some(50),
        ..Default::default()
    };
    lazy_static! {
        static ref BOT_CONFIG: BotConfig = BotConfig {
            source: "https://github.com/dbaron/wgmeeting-github-ircbot".to_string(),
            channels: vec![
                (format!("#meetingbottest"), ChannelConfig {
                    group: format!("Bot-Testing Working Group"),
                    github_repos_allowed: vec![
                        "dbaron/wgmeeting-github-ircbot".to_string(),
                        "dbaron/nonexistentrepo".to_string(),
                        "upsuper/*".to_string(),
                    ],
                    publish_resolutions_only: false,
                }),
                (format!("#testchannel2"), ChannelConfig {
                    group: format!("Second Bot-Testing Working Group"),
                    github_repos_allowed: vec![
                        "dbaron/wgmeeting-github-ircbot".to_string(),
                    ],
                    publish_resolutions_only: false,
                }),
            ].into_iter().collect(),
            // Use of a 0 value disables timeouts, which is needed to avoid intermittent
            // failures (using really-0 timeouts) or having the event loop wait until the
            // timeout completes (positive timeouts).
            activity_timeout_minutes: 0,
            owners: vec![format!("dbaron")],
            ..Default::default()
        };
    }

    let mut irc_state = IRCState::new(GithubType::MockGithubConnection);

    let irc_client: &'static mut _ = Box::leak(Box::new(IrcClient::from_config(irc_config).await?));

    irc_client.identify()?;

    let finished_cb = {
        future::poll_fn(move |_cx| {
            if is_finished.get() {
                debug!("in take_until callback for messages stream: terminating");
                Poll::Ready(())
            } else {
                debug!("in take_until callback for messages stream: continuing");
                Poll::Pending
            }
        })
    };

    let mut irc_stream = irc_client.stream()?.take_until(finished_cb);
    while let Some(message) = irc_stream.next().await.transpose()? {
        // This is where we call into the IRC bot's logic, i.e., where we call the code that's
        // being tested here.
        process_irc_message(irc_client, &mut irc_state, &BOT_CONFIG, message);
    }

    Ok(())
}

/// Convert the lines in the chat file to the dialog that the test should expect to have been
/// recorded by the IRC server.
fn chat_lines_to_expected_lines(chat_file_lines: &Vec<Vec<u8>>) -> Vec<u8> {
    let mut expected_lines = ">CAP END\r\n>NICK test-github-bot\r\n>USER dbaron-gh-bot 0 * :Bot to add meeting minutes to github issues.\r\n".bytes().collect::<Vec<u8>>();

    for line in chat_file_lines.iter() {
        match line.first().map(|b| *b as char) {
            // just skip blank lines (and the empty string after the last
            // line, given the use of split)
            None => (),
            Some('<') => {
                let line = str::from_utf8(line).unwrap();
                expected_lines.extend_from_slice(line.as_bytes());
                expected_lines.append(&mut "\r\n".bytes().collect());
            }
            Some('>') => {
                expected_lines.extend(
                    str::from_utf8(line)
                        .unwrap()
                        .replace("[[CODE_DESCRIPTION]]", &*code_description())
                        .bytes(),
                );
                expected_lines.append(&mut "\r\n".bytes().collect());
            }
            Some('!') => {
                // for now, we send the github comments over IRC when
                // testing, but we don't encode that into the chat
                // format
                expected_lines.append(&mut ">PRIVMSG github-comments ".bytes().collect());
                // Match use of ":" in the stringify function in irc-proto's
                // src/command.rs.
                if line.len() == 1 || line.contains(&0x20u8 /* space */) {
                    expected_lines.append(&mut ":".bytes().collect());
                }
                expected_lines.extend_from_slice(&line[1..]);
                expected_lines.append(&mut "\r\n".bytes().collect());
            }
            _ => {
                panic!(
                    //"Unexpected line in test file {:?}:\n{}",
                    // path,
                    "Unexpected line in test file:\n{}",
                    str::from_utf8(line).unwrap_or("[non-UTF-8 line]")
                );
            }
        }
    }

    expected_lines
}
<|endoftext|>


<commit_before>/// Represents an audio file
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Audio {
    pub file_id: String,
    pub duration: i64,
    pub performer: Option<String>,
    pub title: Option<String>,
    pub mime_type: Option<String>,
    pub file_size: Option<i64>,
}
<commit_msg>added . after 'audio file'<commit_after>/// Represents an audio file.
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Audio {
    pub file_id: String,
    pub duration: i64,
    pub performer: Option<String>,
    pub title: Option<String>,
    pub mime_type: Option<String>,
    pub file_size: Option<i64>,
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use python::{Python, PythonObject, ToPythonPointer, PyDrop};
use err::{self, PyErr, PyResult};
use super::object::PyObject;
use super::exc;
use ffi::{self, Py_ssize_t};
use conversion::{FromPyObject, ToPyObject};
use std::slice;

/// Represents a Python tuple object.
pub struct PyTuple(PyObject);

pyobject_newtype!(PyTuple, PyTuple_Check, PyTuple_Type);

impl PyTuple {
    /// Construct a new tuple with the given elements.
    pub fn new(py: Python, elements: &[PyObject]) -> PyTuple {
        unsafe {
            let len = elements.len();
            let ptr = ffi::PyTuple_New(len as Py_ssize_t);
            let t = err::result_cast_from_owned_ptr::<PyTuple>(py, ptr).unwrap();
            for (i, e) in elements.iter().enumerate() {
                ffi::PyTuple_SetItem(ptr, i as Py_ssize_t, e.steal_ptr(py));
            }
            t
        }
    }

    /// Retrieves the empty tuple.
    pub fn empty(py: Python) -> PyTuple {
        unsafe {
            err::result_cast_from_owned_ptr::<PyTuple>(py, ffi::PyTuple_New(0)).unwrap()
        }
    }

    /// Gets the length of the tuple.
    #[inline]
    pub fn len(&self, _py: Python) -> usize {
        unsafe {
            // non-negative Py_ssize_t should always fit into Rust uint
            ffi::PyTuple_GET_SIZE(self.0.as_ptr()) as usize
        }
    }

    /// Gets the item at the specified index.
    ///
    /// Panics if the index is out of range.
    pub fn get_item(&self, py: Python, index: usize) -> PyObject {
        // TODO: reconsider whether we should panic
        // It's quite inconsistent that this method takes `Python` when `len()` does not.
        assert!(index < self.len(py));
        unsafe {
            PyObject::from_borrowed_ptr(py, ffi::PyTuple_GET_ITEM(self.0.as_ptr(), index as Py_ssize_t))
        }
    }

    #[inline]
    pub fn as_slice<'a>(&'a self) -> &'a [PyObject] {
        // This is safe because PyObject has the same memory layout as *mut ffi::PyObject,
        // and because tuples are immutable.
        // (We don't even need a Python token, thanks to immutability)
        unsafe {
            let ptr = self.0.as_ptr() as *mut ffi::PyTupleObject;
            PyObject::borrow_from_owned_ptr_slice(
                slice::from_raw_parts(
                    (*ptr).ob_item.as_ptr(),
                    ffi::PyTuple_GET_SIZE(self.0.as_ptr()) as usize
                ))
        }
    }
}

impl ::std::ops::Index<usize> for PyTuple {
    type Output = PyObject;

    #[inline]
    fn index(&self, index: usize) -> &PyObject {
        &self.as_slice()[index]
    }
}

impl <'a> IntoIterator for &'a PyTuple {
    type Item = &'a PyObject;
    type IntoIter = slice::Iter<'a, PyObject>;

    #[inline]
    fn into_iter(self) -> Self::IntoIter {
        self.as_slice().iter()
    }
}

fn wrong_tuple_length(py: Python, t: &PyTuple, expected_length: usize) -> PyErr {
    let msg = format!("Expected tuple of length {}, but got tuple of length {}.", expected_length, t.len(py));
    PyErr::new_lazy_init(py.get_type::<exc::ValueError>(), Some(msg.to_py_object(py).into_object()))
}

macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+} => {
    impl <$($T: ToPyObject),+> ToPyObject for ($($T,)+) {
        type ObjectType = PyTuple;

        fn to_py_object(&self, py: Python) -> PyTuple {
            PyTuple::new(py, &[
                $(py_coerce_expr!(self.$n.to_py_object(py)).into_object(),)+
            ])
        }

        fn into_py_object(self, py: Python) -> PyTuple {
            PyTuple::new(py, &[
                $(py_coerce_expr!(self.$n.into_py_object(py)).into_object(),)+
            ])
        }
    }

    impl <'s, $($T: FromPyObject<'s>),+> FromPyObject<'s> for ($($T,)+) {
        fn extract(py: Python, obj: &'s PyObject) -> PyResult<Self> {
            let t = try!(obj.cast_as::<PyTuple>(py));
            let slice = t.as_slice();
            if slice.len() == $length {
                Ok((
                    $( try!(slice[$n].extract::<$T>(py)), )+
                ))
            } else {
                Err(wrong_tuple_length(py, t, $length))
            }
        }
    }
});

tuple_conversion!(1, (ref0, 0, A));
tuple_conversion!(2, (ref0, 0, A), (ref1, 1, B));
tuple_conversion!(3, (ref0, 0, A), (ref1, 1, B), (ref2, 2, C));
tuple_conversion!(4, (ref0, 0, A), (ref1, 1, B), (ref2, 2, C), (ref3, 3, D));
tuple_conversion!(5, (ref0, 0, A), (ref1, 1, B), (ref2, 2, C), (ref3, 3, D),
  (ref4, 4, E));
tuple_conversion!(6, (ref0, 0, A), (ref1, 1, B), (ref2, 2, C), (ref3, 3, D),
  (ref4, 4, E), (ref5, 5, F));
tuple_conversion!(7, (ref0, 0, A), (ref1, 1, B), (ref2, 2, C), (ref3, 3, D),
  (ref4, 4, E), (ref5, 5, F), (ref6, 6, G));
tuple_conversion!(8, (ref0, 0, A), (ref1, 1, B), (ref2, 2, C), (ref3, 3, D),
  (ref4, 4, E), (ref5, 5, F), (ref6, 6, G), (ref7, 7, H));
tuple_conversion!(9, (ref0, 0, A), (ref1, 1, B), (ref2, 2, C), (ref3, 3, D),
  (ref4, 4, E), (ref5, 5, F), (ref6, 6, G), (ref7, 7, H), (ref8, 8, I));

// Empty tuple:

/// An empty struct that represents the empty argument list.
/// Corresponds to the empty tuple `()` in Python.
///
/// # Example
/// ```
/// let gil_guard = cpython::Python::acquire_gil();
/// let py = gil_guard.python();
/// let os = py.import("os").unwrap();
/// let pid = os.call(py, "get_pid", cpython::NoArgs, None);
/// ```
#[derive(Copy, Clone, Debug)]
pub struct NoArgs;

/// Converts `NoArgs` to an empty Python tuple.
impl ToPyObject for NoArgs {
    type ObjectType = PyTuple;

    fn to_py_object(&self, py: Python) -> PyTuple {
        PyTuple::empty(py)
    }
}

/// Returns `Ok(NoArgs)` if the input is an empty Python tuple.
/// Otherwise, returns an error.
extract!(obj to NoArgs; py => {
    let t = try!(obj.cast_as::<PyTuple>(py));
    if t.len(py) == 0 {
        Ok(NoArgs)
    } else {
        Err(wrong_tuple_length(py, t, 0))
    }
});

#[cfg(test)]
mod test {
    use python::{Python, PythonObject};
    use conversion::ToPyObject;

    #[test]
    fn test_len() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let tuple = (1, 2, 3).to_py_object(py);
        assert_eq!(3, tuple.len(py));
        assert_eq!((1, 2, 3), tuple.into_object().extract(py).unwrap());
    }
}

<commit_msg>PyTuple: Add back iter() method (fixes build); require py: Python for as_slice().<commit_after>// Copyright (c) 2015 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use python::{Python, PythonObject, ToPythonPointer, PyDrop};
use err::{self, PyErr, PyResult};
use super::object::PyObject;
use super::exc;
use ffi::{self, Py_ssize_t};
use conversion::{FromPyObject, ToPyObject};
use std::slice;

/// Represents a Python tuple object.
pub struct PyTuple(PyObject);

pyobject_newtype!(PyTuple, PyTuple_Check, PyTuple_Type);

impl PyTuple {
    /// Construct a new tuple with the given elements.
    pub fn new(py: Python, elements: &[PyObject]) -> PyTuple {
        unsafe {
            let len = elements.len();
            let ptr = ffi::PyTuple_New(len as Py_ssize_t);
            let t = err::result_cast_from_owned_ptr::<PyTuple>(py, ptr).unwrap();
            for (i, e) in elements.iter().enumerate() {
                ffi::PyTuple_SetItem(ptr, i as Py_ssize_t, e.steal_ptr(py));
            }
            t
        }
    }

    /// Retrieves the empty tuple.
    pub fn empty(py: Python) -> PyTuple {
        unsafe {
            err::result_cast_from_owned_ptr::<PyTuple>(py, ffi::PyTuple_New(0)).unwrap()
        }
    }

    /// Gets the length of the tuple.
    #[inline]
    pub fn len(&self, _py: Python) -> usize {
        unsafe {
            // non-negative Py_ssize_t should always fit into Rust uint
            ffi::PyTuple_GET_SIZE(self.0.as_ptr()) as usize
        }
    }

    /// Gets the item at the specified index.
    ///
    /// Panics if the index is out of range.
    pub fn get_item(&self, py: Python, index: usize) -> PyObject {
        // TODO: reconsider whether we should panic
        // It's quite inconsistent that this method takes `Python` when `len()` does not.
        assert!(index < self.len(py));
        unsafe {
            PyObject::from_borrowed_ptr(py, ffi::PyTuple_GET_ITEM(self.0.as_ptr(), index as Py_ssize_t))
        }
    }

    #[inline]
    pub fn as_slice<'a>(&'a self, py: Python) -> &'a [PyObject] {
        // This is safe because PyObject has the same memory layout as *mut ffi::PyObject,
        // and because tuples are immutable.
        // (We don't even need a Python token, thanks to immutability)
        unsafe {
            let ptr = self.0.as_ptr() as *mut ffi::PyTupleObject;
            PyObject::borrow_from_owned_ptr_slice(
                slice::from_raw_parts(
                    (*ptr).ob_item.as_ptr(),
                    self.len(py)
                ))
        }
    }

    #[inline]
    pub fn iter(&self, py: Python) -> slice::Iter<PyObject> {
        self.as_slice(py).iter()
    }
}

fn wrong_tuple_length(py: Python, t: &PyTuple, expected_length: usize) -> PyErr {
    let msg = format!("Expected tuple of length {}, but got tuple of length {}.", expected_length, t.len(py));
    PyErr::new_lazy_init(py.get_type::<exc::ValueError>(), Some(msg.to_py_object(py).into_object()))
}

macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+} => {
    impl <$($T: ToPyObject),+> ToPyObject for ($($T,)+) {
        type ObjectType = PyTuple;

        fn to_py_object(&self, py: Python) -> PyTuple {
            PyTuple::new(py, &[
                $(py_coerce_expr!(self.$n.to_py_object(py)).into_object(),)+
            ])
        }

        fn into_py_object(self, py: Python) -> PyTuple {
            PyTuple::new(py, &[
                $(py_coerce_expr!(self.$n.into_py_object(py)).into_object(),)+
            ])
        }
    }

    impl <'s, $($T: FromPyObject<'s>),+> FromPyObject<'s> for ($($T,)+) {
        fn extract(py: Python, obj: &'s PyObject) -> PyResult<Self> {
            let t = try!(obj.cast_as::<PyTuple>(py));
            let slice = t.as_slice(py);
            if slice.len() == $length {
                Ok((
                    $( try!(slice[$n].extract::<$T>(py)), )+
                ))
            } else {
                Err(wrong_tuple_length(py, t, $length))
            }
        }
    }
});

tuple_conversion!(1, (ref0, 0, A));
tuple_conversion!(2, (ref0, 0, A), (ref1, 1, B));
tuple_conversion!(3, (ref0, 0, A), (ref1, 1, B), (ref2, 2, C));
tuple_conversion!(4, (ref0, 0, A), (ref1, 1, B), (ref2, 2, C), (ref3, 3, D));
tuple_conversion!(5, (ref0, 0, A), (ref1, 1, B), (ref2, 2, C), (ref3, 3, D),
  (ref4, 4, E));
tuple_conversion!(6, (ref0, 0, A), (ref1, 1, B), (ref2, 2, C), (ref3, 3, D),
  (ref4, 4, E), (ref5, 5, F));
tuple_conversion!(7, (ref0, 0, A), (ref1, 1, B), (ref2, 2, C), (ref3, 3, D),
  (ref4, 4, E), (ref5, 5, F), (ref6, 6, G));
tuple_conversion!(8, (ref0, 0, A), (ref1, 1, B), (ref2, 2, C), (ref3, 3, D),
  (ref4, 4, E), (ref5, 5, F), (ref6, 6, G), (ref7, 7, H));
tuple_conversion!(9, (ref0, 0, A), (ref1, 1, B), (ref2, 2, C), (ref3, 3, D),
  (ref4, 4, E), (ref5, 5, F), (ref6, 6, G), (ref7, 7, H), (ref8, 8, I));

// Empty tuple:

/// An empty struct that represents the empty argument list.
/// Corresponds to the empty tuple `()` in Python.
///
/// # Example
/// ```
/// let gil_guard = cpython::Python::acquire_gil();
/// let py = gil_guard.python();
/// let os = py.import("os").unwrap();
/// let pid = os.call(py, "get_pid", cpython::NoArgs, None);
/// ```
#[derive(Copy, Clone, Debug)]
pub struct NoArgs;

/// Converts `NoArgs` to an empty Python tuple.
impl ToPyObject for NoArgs {
    type ObjectType = PyTuple;

    fn to_py_object(&self, py: Python) -> PyTuple {
        PyTuple::empty(py)
    }
}

/// Returns `Ok(NoArgs)` if the input is an empty Python tuple.
/// Otherwise, returns an error.
extract!(obj to NoArgs; py => {
    let t = try!(obj.cast_as::<PyTuple>(py));
    if t.len(py) == 0 {
        Ok(NoArgs)
    } else {
        Err(wrong_tuple_length(py, t, 0))
    }
});

#[cfg(test)]
mod test {
    use python::{Python, PythonObject};
    use conversion::ToPyObject;

    #[test]
    fn test_len() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let tuple = (1, 2, 3).to_py_object(py);
        assert_eq!(3, tuple.len(py));
        assert_eq!((1, 2, 3), tuple.into_object().extract(py).unwrap());
    }
}

<|endoftext|>


<commit_before>extern crate encoding;

use std::fmt;

use self::encoding::types::EncodingRef;

/// ParseOptions allows setting options for parsing MO catalogs.
pub struct ParseOptions {
    force_encoding: Option<EncodingRef>,
}

impl ParseOptions {
    /// Returns a new instance of ParseOptions with default options.
    pub fn new() -> Self {
        ParseOptions { force_encoding: None }
    }

    /// Forces a use of a specific encoding
    /// when parsing strings from a catalog.
    /// If this option is not enabled,
    /// the parser tries to use the encoding specified in the metadata
    /// or UTF-8 if metadata is non-existent.
    pub fn force_encoding(&mut self, encoding: EncodingRef) -> &mut Self {
        self.force_encoding = Some(encoding);
        self
    }
}

// Cannot derive as Encoding does not implement Debug.
impl fmt::Debug for ParseOptions {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let mut debug_fmt = fmt.debug_struct("ParseOptions");
        match self.force_encoding {
            opt @ Some(_) => {
                debug_fmt.field("force_encoding", &opt.map(|e| e.name()));
            }
            opt @ None => {
                // HACK: convert Option<EncodingRef> to Option<()> with map()
                // to get a Debug impl.
                debug_fmt.field("force_encoding", &opt.map(|_| ()));
            }
        }
        debug_fmt.finish()
    }
}
<commit_msg>Remove Debug implementation for now.<commit_after>extern crate encoding;

use std::fmt;

use self::encoding::types::EncodingRef;

/// ParseOptions allows setting options for parsing MO catalogs.
#[allow(missing_debug_implementations)]
pub struct ParseOptions {
    force_encoding: Option<EncodingRef>,
}

impl ParseOptions {
    /// Returns a new instance of ParseOptions with default options.
    pub fn new() -> Self {
        ParseOptions { force_encoding: None }
    }

    /// Forces a use of a specific encoding
    /// when parsing strings from a catalog.
    /// If this option is not enabled,
    /// the parser tries to use the encoding specified in the metadata
    /// or UTF-8 if metadata is non-existent.
    pub fn force_encoding(&mut self, encoding: EncodingRef) -> &mut Self {
        self.force_encoding = Some(encoding);
        self
    }
}
<|endoftext|>