<commit_before>#![feature(core_intrinsics)]

use std::io::Write;
use std::intrinsics;

fn main() {
    let _ = ::std::iter::repeat('a' as u8).take(10).collect::<Vec<_>>();
    let stderr = ::std::io::stderr();
    let mut stderr = stderr.lock();

    writeln!(stderr, "some {} text", "<unknown>").unwrap();

    let _ = std::process::Command::new("true").env("c", "d").spawn();

    println!("cargo:rustc-link-lib=z");

    static ONCE: std::sync::Once = std::sync::ONCE_INIT;
    ONCE.call_once(|| {});

    LoopState::Continue(()) == LoopState::Break(());

    // Make sure ByValPair values with differently sized components are correctly passed
    map(None::<(u8, Box<Instruction>)>);

    println!("{}", 2.3f32.exp());
    println!("{}", 2.3f32.exp2());
    println!("{}", 2.3f32.abs());
    println!("{}", 2.3f32.sqrt());
    println!("{}", 2.3f32.floor());
    println!("{}", 2.3f32.ceil());
    println!("{}", 2.3f32.min(1.0));
    println!("{}", 2.3f32.max(1.0));

    assert_eq!(0b0000000000000000000000000010000010000000000000000000000000000000_0000000000100000000000000000000000001000000000000100000000000000u128.leading_zeros(), 26);
    assert_eq!(0b0000000000000000000000000010000000000000000000000000000000000000_0000000000000000000000000000000000001000000000000000000010000000u128.trailing_zeros(), 7);

    checked_div_i128(0i128, 2i128);
    checked_div_u128(0u128, 2u128);
    assert_eq!(1u128 + 2, 3);

    assert_eq!(0b100010000000000000000000000000000u128 >> 10, 0b10001000000000000000000u128);
    assert_eq!(0xFEDCBA987654321123456789ABCDEFu128 >> 64, 0xFEDCBA98765432u128);
    assert_eq!(0xFEDCBA987654321123456789ABCDEFu128 as i128 >> 64, 0xFEDCBA98765432i128);
    assert_eq!(353985398u128 * 932490u128, 330087843781020u128);
}

#[derive(PartialEq)]
enum LoopState {
    Continue(()),
    Break(())
}

pub enum Instruction {
    Increment,
    Loop,
}

fn map(a: Option<(u8, Box<Instruction>)>) -> Option<Box<Instruction>> {
    match a {
        None => None,
        Some((_, instr)) => Some(instr),
    }
}

fn checked_div_i128(lhs: i128, rhs: i128) -> Option<i128> {
    if rhs == 0 || (lhs == -170141183460469231731687303715884105728 && rhs == -1) {
        None
    } else {
        Some(unsafe { intrinsics::unchecked_div(lhs, rhs) })
    }
}

fn checked_div_u128(lhs: u128, rhs: u128) -> Option<u128> {
    match rhs {
        0 => None,
        rhs => Some(unsafe { intrinsics::unchecked_div(lhs, rhs) })
    }
}
<commit_msg>Use std checked_div<commit_after>#![feature(core_intrinsics)]

use std::io::Write;
use std::intrinsics;

fn main() {
    let _ = ::std::iter::repeat('a' as u8).take(10).collect::<Vec<_>>();
    let stderr = ::std::io::stderr();
    let mut stderr = stderr.lock();

    writeln!(stderr, "some {} text", "<unknown>").unwrap();

    let _ = std::process::Command::new("true").env("c", "d").spawn();

    println!("cargo:rustc-link-lib=z");

    static ONCE: std::sync::Once = std::sync::ONCE_INIT;
    ONCE.call_once(|| {});

    LoopState::Continue(()) == LoopState::Break(());

    // Make sure ByValPair values with differently sized components are correctly passed
    map(None::<(u8, Box<Instruction>)>);

    println!("{}", 2.3f32.exp());
    println!("{}", 2.3f32.exp2());
    println!("{}", 2.3f32.abs());
    println!("{}", 2.3f32.sqrt());
    println!("{}", 2.3f32.floor());
    println!("{}", 2.3f32.ceil());
    println!("{}", 2.3f32.min(1.0));
    println!("{}", 2.3f32.max(1.0));

    assert_eq!(0b0000000000000000000000000010000010000000000000000000000000000000_0000000000100000000000000000000000001000000000000100000000000000u128.leading_zeros(), 26);
    assert_eq!(0b0000000000000000000000000010000000000000000000000000000000000000_0000000000000000000000000000000000001000000000000000000010000000u128.trailing_zeros(), 7);

    0i128.checked_div(2i128);
    0u128.checked_div(2u128);
    assert_eq!(1u128 + 2, 3);

    assert_eq!(0b100010000000000000000000000000000u128 >> 10, 0b10001000000000000000000u128);
    assert_eq!(0xFEDCBA987654321123456789ABCDEFu128 >> 64, 0xFEDCBA98765432u128);
    assert_eq!(0xFEDCBA987654321123456789ABCDEFu128 as i128 >> 64, 0xFEDCBA98765432i128);
    assert_eq!(353985398u128 * 932490u128, 330087843781020u128);
}

#[derive(PartialEq)]
enum LoopState {
    Continue(()),
    Break(())
}

pub enum Instruction {
    Increment,
    Loop,
}

fn map(a: Option<(u8, Box<Instruction>)>) -> Option<Box<Instruction>> {
    match a {
        None => None,
        Some((_, instr)) => Some(instr),
    }
}
<|endoftext|>


<commit_before>use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use lazy_static::lazy_static;
use std::fmt::Debug;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, SystemTime, UNIX_EPOCH};

#[derive(Clone, Debug)]
struct Rng(u64);

impl Rng {
    fn new() -> Self {
        let start = SystemTime::now();
        let since_the_epoch = start
            .duration_since(UNIX_EPOCH)
            .expect("Time went backwards")
            .as_secs();
        Self(since_the_epoch)
    }

    fn gen_range(&mut self, n: u64) -> u64 {
        let mut state = self.0;
        state ^= state >> 12;
        state ^= state << 25;
        state ^= state >> 27;
        state = state.wrapping_mul(2685821657736338717u64);
        self.0 = state;
        state % n
    }
}

#[derive(Debug, Clone)]
enum Action {
    AddProgressBar(usize),
    IncProgressBar(usize),
}

#[derive(Clone, Debug)]
struct Elem {
    key: String,
    index: usize,
    indent: usize,
    progress_bar: ProgressBar,
}

lazy_static! {
    static ref ELEMENTS: [Elem; 9] = [
        Elem { indent: 1, index: 0, progress_bar: ProgressBar::new(32), key: "jumps".to_string() },
        Elem { indent: 2, index: 1, progress_bar: ProgressBar::new(32), key: "lazy".to_string() },
        Elem { indent: 0, index: 0, progress_bar: ProgressBar::new(32), key: "the".to_string() },
        Elem { indent: 3, index: 3, progress_bar: ProgressBar::new(32), key: "dog".to_string() },
        Elem { indent: 2, index: 2, progress_bar: ProgressBar::new(32), key: "over".to_string() },
        Elem { indent: 2, index: 1, progress_bar: ProgressBar::new(32), key: "brown".to_string() },
        Elem { indent: 1, index: 1, progress_bar: ProgressBar::new(32), key: "quick".to_string() },
        Elem { indent: 3, index: 5, progress_bar: ProgressBar::new(32), key: "a".to_string() },
        Elem { indent: 3, index: 3, progress_bar: ProgressBar::new(32), key: "fox".to_string() },
    ];
}

fn main() {
    let mp = Arc::new(MultiProgress::new());
    let sty_main = ProgressStyle::default_bar().template("{bar:40.green/yellow} {pos:>4}/{len:4}");
    let sty_aux = ProgressStyle::default_bar().template("{spinner:.green} {msg} {pos:>4}/{len:4}");

    let pb_main = mp.add(ProgressBar::new(
        ELEMENTS.iter().map(|e| e.progress_bar.length()).sum(),
    ));
    pb_main.set_style(sty_main);
    for elem in ELEMENTS.iter() {
        elem.progress_bar.set_style(sty_aux.clone());
    }

    let tree: Arc<Mutex<Vec<&Elem>>> = Arc::new(Mutex::new(Vec::with_capacity(ELEMENTS.len())));
    let tree2 = Arc::clone(&tree);

    let mp2 = Arc::clone(&mp);
    let _ = thread::spawn(move || {
        let mut rng = Rng::new();
        pb_main.tick();
        loop {
            match get_action(&mut rng, &tree) {
                None => {
                    // all elements were exhausted
                    pb_main.finish();
                    return;
                }
                Some(Action::AddProgressBar(el_idx)) => {
                    let elem = &ELEMENTS[el_idx];
                    let pb = mp2.insert(elem.index + 1, elem.progress_bar.clone());
                    pb.set_message(&format!("{}  {}", "  ".repeat(elem.indent), elem.key));
                    tree.lock().unwrap().insert(elem.index, &elem);
                }
                Some(Action::IncProgressBar(el_idx)) => {
                    let elem = &tree.lock().unwrap()[el_idx];
                    elem.progress_bar.inc(1);
                    let pos = elem.progress_bar.position();
                    let len = elem.progress_bar.length();
                    if pos >= len {
                        elem.progress_bar.finish_with_message(&format!(
                            "{}{} {}",
                            "  ".repeat(elem.indent),
                            "✔",
                            elem.key
                        ));
                    }
                    pb_main.inc(1);
                }
            }
            thread::sleep(Duration::from_millis(15));
        }
    });

    mp.join().unwrap();

    println!("===============================");
    println!("the tree should be the same as:");
    for elem in tree2.lock().unwrap().iter() {
        println!("{}  {}", "  ".repeat(elem.indent), elem.key);
    }
}

fn get_action<'a>(rng: &'a mut Rng, tree: &Mutex<Vec<&Elem>>) -> Option<Action> {
    let elem_len = ELEMENTS.len() as u64;
    let list_len = tree.lock().unwrap().len() as u64;
    let sum_free = tree
        .lock()
        .unwrap()
        .iter()
        .map(|e| {
            let pos = e.progress_bar.position();
            let len = e.progress_bar.length();
            len - pos
        })
        .sum::<u64>();
    if sum_free == 0 && list_len == elem_len {
        // nothing to do more
        return None;
    } else if sum_free == 0 && list_len < elem_len {
        // there is no place to make an increment
        return Some(Action::AddProgressBar(tree.lock().unwrap().len()));
    } else {
        loop {
            let list = tree.lock().unwrap();
            let k = rng.gen_range(17);
            if k == 0 && list_len < elem_len {
                return Some(Action::AddProgressBar(list.len()));
            } else {
                let l = (k % list_len) as usize;
                let pos = list[l].progress_bar.position();
                let len = list[l].progress_bar.length();
                if pos < len {
                    return Some(Action::IncProgressBar(l));
                }
            }
        }
    }
}
<commit_msg>Switch custom Rng implementation for rand dependency<commit_after>use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use lazy_static::lazy_static;
use rand::{rngs::ThreadRng, Rng, RngCore};
use std::fmt::Debug;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;

#[derive(Debug, Clone)]
enum Action {
    AddProgressBar(usize),
    IncProgressBar(usize),
}

#[derive(Clone, Debug)]
struct Elem {
    key: String,
    index: usize,
    indent: usize,
    progress_bar: ProgressBar,
}

lazy_static! {
    static ref ELEMENTS: [Elem; 9] = [
        Elem { indent: 1, index: 0, progress_bar: ProgressBar::new(32), key: "jumps".to_string() },
        Elem { indent: 2, index: 1, progress_bar: ProgressBar::new(32), key: "lazy".to_string() },
        Elem { indent: 0, index: 0, progress_bar: ProgressBar::new(32), key: "the".to_string() },
        Elem { indent: 3, index: 3, progress_bar: ProgressBar::new(32), key: "dog".to_string() },
        Elem { indent: 2, index: 2, progress_bar: ProgressBar::new(32), key: "over".to_string() },
        Elem { indent: 2, index: 1, progress_bar: ProgressBar::new(32), key: "brown".to_string() },
        Elem { indent: 1, index: 1, progress_bar: ProgressBar::new(32), key: "quick".to_string() },
        Elem { indent: 3, index: 5, progress_bar: ProgressBar::new(32), key: "a".to_string() },
        Elem { indent: 3, index: 3, progress_bar: ProgressBar::new(32), key: "fox".to_string() },
    ];
}

fn main() {
    let mp = Arc::new(MultiProgress::new());
    let sty_main = ProgressStyle::default_bar().template("{bar:40.green/yellow} {pos:>4}/{len:4}");
    let sty_aux = ProgressStyle::default_bar().template("{spinner:.green} {msg} {pos:>4}/{len:4}");

    let pb_main = mp.add(ProgressBar::new(
        ELEMENTS.iter().map(|e| e.progress_bar.length()).sum(),
    ));
    pb_main.set_style(sty_main);
    for elem in ELEMENTS.iter() {
        elem.progress_bar.set_style(sty_aux.clone());
    }

    let tree: Arc<Mutex<Vec<&Elem>>> = Arc::new(Mutex::new(Vec::with_capacity(ELEMENTS.len())));
    let tree2 = Arc::clone(&tree);

    let mp2 = Arc::clone(&mp);
    let _ = thread::spawn(move || {
        let mut rng = ThreadRng::default();
        pb_main.tick();
        loop {
            match get_action(&mut rng, &tree) {
                None => {
                    // all elements were exhausted
                    pb_main.finish();
                    return;
                }
                Some(Action::AddProgressBar(el_idx)) => {
                    let elem = &ELEMENTS[el_idx];
                    let pb = mp2.insert(elem.index + 1, elem.progress_bar.clone());
                    pb.set_message(&format!("{}  {}", "  ".repeat(elem.indent), elem.key));
                    tree.lock().unwrap().insert(elem.index, &elem);
                }
                Some(Action::IncProgressBar(el_idx)) => {
                    let elem = &tree.lock().unwrap()[el_idx];
                    elem.progress_bar.inc(1);
                    let pos = elem.progress_bar.position();
                    let len = elem.progress_bar.length();
                    if pos >= len {
                        elem.progress_bar.finish_with_message(&format!(
                            "{}{} {}",
                            "  ".repeat(elem.indent),
                            "✔",
                            elem.key
                        ));
                    }
                    pb_main.inc(1);
                }
            }
            thread::sleep(Duration::from_millis(15));
        }
    });

    mp.join().unwrap();

    println!("===============================");
    println!("the tree should be the same as:");
    for elem in tree2.lock().unwrap().iter() {
        println!("{}  {}", "  ".repeat(elem.indent), elem.key);
    }
}

fn get_action<'a>(rng: &'a mut dyn RngCore, tree: &Mutex<Vec<&Elem>>) -> Option<Action> {
    let elem_len = ELEMENTS.len() as u64;
    let list_len = tree.lock().unwrap().len() as u64;
    let sum_free = tree
        .lock()
        .unwrap()
        .iter()
        .map(|e| {
            let pos = e.progress_bar.position();
            let len = e.progress_bar.length();
            len - pos
        })
        .sum::<u64>();
    if sum_free == 0 && list_len == elem_len {
        // nothing to do more
        return None;
    } else if sum_free == 0 && list_len < elem_len {
        // there is no place to make an increment
        return Some(Action::AddProgressBar(tree.lock().unwrap().len()));
    } else {
        loop {
            let list = tree.lock().unwrap();
            let k = rng.gen_range(0..17);
            if k == 0 && list_len < elem_len {
                return Some(Action::AddProgressBar(list.len()));
            } else {
                let l = (k % list_len) as usize;
                let pos = list[l].progress_bar.position();
                let len = list[l].progress_bar.length();
                if pos < len {
                    return Some(Action::IncProgressBar(l));
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use crate::msg::diagnostic_msgs::{DiagnosticArray, DiagnosticStatus};
use crate::msg::std_msgs::Header;
use crate::{Level, Status, Task};
use rosrust::{error::Result, Publisher};

pub struct Updater {
    publisher: Publisher<DiagnosticArray>,
    tasks: Vec<Box<dyn Task>>,
    hardware_id: String,
}

impl Updater {
    pub fn new() -> Result<Self> {
        let publisher = rosrust::publish("/diagnostics", 10)?;
        Ok(Self {
            publisher,
            tasks: vec![],
            hardware_id: String::new(),
        })
    }

    pub fn set_hardware_id(&mut self, hardware_id: impl std::string::ToString) {
        self.hardware_id = hardware_id.to_string();
    }

    pub fn add_task(&mut self, task: impl Task + 'static) {
        self.tasks.push(Box::new(task))
    }

    pub fn update_with_extra<'a>(&'a self, extra_tasks: &[&'a dyn Task]) -> Result<()> {
        self.publish(self.perform_checks(extra_tasks))
    }

    pub fn update(&self) -> Result<()> {
        self.update_with_extra(&[])
    }

    pub fn perform_checks<'a>(&self, extra_tasks: &[&'a dyn Task]) -> Vec<DiagnosticStatus> {
        Iterator::chain(
            self.tasks.iter().map(|v| &(**v)),
            extra_tasks.iter().cloned(),
        )
        .map(|task| self.perform_passed_check(task))
        .collect()
    }

    pub fn perform_passed_check(&self, task: &dyn Task) -> DiagnosticStatus {
        let mut status = Status {
            name: task.name().into(),
            hardware_id: self.hardware_id.clone(),
            level: Level::Error,
            message: "No message was set".into(),
            values: vec![],
        };
        task.run(&mut status);
        status.into()
    }

    pub fn publish(&self, status: Vec<DiagnosticStatus>) -> Result<()> {
        let message = DiagnosticArray {
            header: Header::default(),
            status,
        };
        self.publisher.send(message)
    }
}
<commit_msg>Add notes<commit_after>use crate::msg::diagnostic_msgs::{DiagnosticArray, DiagnosticStatus};
use crate::msg::std_msgs::Header;
use crate::{Level, Status, Task};
use rosrust::{error::Result, Publisher};

pub struct Updater {
    publisher: Publisher<DiagnosticArray>,
    tasks: Vec<Box<dyn Task>>,
    hardware_id: String,
}

impl Updater {
    pub fn new() -> Result<Self> {
        let publisher = rosrust::publish("/diagnostics", 10)?;
        Ok(Self {
            publisher,
            tasks: vec![],
            hardware_id: String::new(),
        })
    }

    pub fn set_hardware_id(&mut self, hardware_id: impl std::string::ToString) {
        self.hardware_id = hardware_id.to_string();
    }

    pub fn add_task(&mut self, task: impl Task + 'static) {
        self.tasks.push(Box::new(task))
    }

    pub fn update_with_extra<'a>(&'a self, extra_tasks: &[&'a dyn Task]) -> Result<()> {
        self.publish(self.perform_checks(extra_tasks))
    }

    pub fn update(&self) -> Result<()> {
        self.update_with_extra(&[])
    }

    pub fn perform_checks<'a>(&self, extra_tasks: &[&'a dyn Task]) -> Vec<DiagnosticStatus> {
        Iterator::chain(
            self.tasks.iter().map(|v| &(**v)),
            extra_tasks.iter().cloned(),
        )
        .map(|task| self.perform_passed_check(task))
        .collect()
    }

    pub fn perform_passed_check(&self, task: &dyn Task) -> DiagnosticStatus {
        let mut status = Status {
            name: task.name().into(),
            hardware_id: self.hardware_id.clone(),
            level: Level::Error,
            message: "No message was set".into(),
            values: vec![],
        };
        task.run(&mut status);
        // TODO: Add support for higher verbosity and unset hardware id warnings
        status.into()
    }

    pub fn publish(&self, status: Vec<DiagnosticStatus>) -> Result<()> {
        let message = DiagnosticArray {
            header: Header::default(),
            status,
        };
        self.publisher.send(message)
    }
}
<|endoftext|>


<commit_before>use futures::{
    channel::{mpsc as async_mpsc, oneshot},
    future::{abortable, AbortHandle, LocalBoxFuture},
    prelude::*,
    stream,
};

use gettextrs::gettext;
use gtk::prelude::*;

use log::debug;

use std::{
    collections::HashSet,
    fmt,
    path::Path,
    rc::Rc,
    sync::{Arc, RwLock},
    time::Duration,
};

use media::MediaEvent;
use metadata::{Format, MediaInfo};

use crate::{generic_output, info_bar, main, prelude::*, spawn, UIEvent};

pub const MEDIA_EVENT_CHANNEL_CAPACITY: usize = 1;
const PROGRESS_TIMER_PERIOD: Duration = Duration::from_millis(250);

pub enum ProcessingType {
    Async(async_mpsc::Receiver<media::MediaEvent>),
    Sync,
}

pub enum MediaEventHandling {
    Done,
    ExpectingMore,
}

impl MediaEventHandling {
    pub fn is_done(&self) -> bool {
        matches!(self, MediaEventHandling::Done)
    }
}

#[derive(Debug, PartialEq)]
enum ProcessingState {
    AllDone,
    ConfirmedOutputTo(Rc<Path>),
    Init,
    SkipCurrent,
    DoneWithCurrent,
    WouldOutputTo(Rc<Path>),
}

#[derive(Debug)]
pub struct MediaProcessorError(String);

impl From<String> for MediaProcessorError {
    fn from(msg: String) -> Self {
        MediaProcessorError(msg)
    }
}

impl fmt::Display for MediaProcessorError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl std::error::Error for MediaProcessorError {}

pub trait MediaProcessorImpl: Iterator<Item = Rc<Path>> {
    fn process<'a>(
        &'a mut self,
        output_path: &'a Path,
    ) -> LocalBoxFuture<'a, Result<ProcessingType, MediaProcessorError>>;
    fn cancel(&mut self);
    fn handle_media_event(
        &mut self,
        event: MediaEvent,
    ) -> Result<MediaEventHandling, MediaProcessorError>;
    fn report_progress(&mut self) -> f64;
    fn completion_msg() -> String;
}

struct MediaProcessor<CtrlImpl: OutputControllerImpl + 'static> {
    impl_: CtrlImpl::MediaProcessorImplType,
    progress_bar: gtk::ProgressBar,
    btn: gtk::Button,
}

impl<CtrlImpl: OutputControllerImpl + 'static> MediaProcessor<CtrlImpl> {
    pub(super) async fn spawn(
        impl_: CtrlImpl::MediaProcessorImplType,
        progress_bar: gtk::ProgressBar,
        btn: gtk::Button,
    ) -> AbortHandle {
        let mut this = MediaProcessor {
            impl_,
            progress_bar,
            btn,
        };

        let (sender, receiver) = oneshot::channel();
        spawn(async move {
            let (abortable_run, abort_handle) = abortable(Self::run(&mut this));
            sender.send(abort_handle).unwrap();

            if abortable_run.await.is_err() {
                this.impl_.cancel();
            }

            UIEventChannel::send(CtrlImpl::OutputEvent::from(
                generic_output::Event::ActionOver,
            ));
        });

        receiver.await.unwrap()
    }

    async fn run(&mut self) {
        use ProcessingState::*;

        let mut state = ProcessingState::Init;
        let mut overwrite_all = false;

        loop {
            match state {
                AllDone => {
                    info_bar::show_info(CtrlImpl::MediaProcessorImplType::completion_msg());
                    break;
                }
                ConfirmedOutputTo(path) => {
                    if let Err(err) = self.process(path.as_ref()).await {
                        info_bar::show_error(err);
                        break;
                    }

                    state = DoneWithCurrent;
                }
                Init | DoneWithCurrent => {
                    state = self.impl_.next().map_or(AllDone, WouldOutputTo);
                }
                SkipCurrent => {
                    match self.impl_.next() {
                        Some(path) => state = WouldOutputTo(path),
                        None => {
                            // Don't display the success message when the user decided
                            // to skip (not overwrite) last part as it seems missleading
                            break;
                        }
                    }
                }
                WouldOutputTo(path) => {
                    if !path.exists() || overwrite_all {
                        state = ConfirmedOutputTo(path);
                        continue;
                    }

                    // Path exists and overwrite_all is not true
                    self.btn.set_sensitive(false);
                    main::reset_cursor();

                    let filename = path.file_name().expect("no `filename` in `path`");
                    let filename = filename
                        .to_str()
                        .expect("can't get printable `str` from `filename`");
                    let question = gettext("{output_file}\nalready exists. Overwrite?").replacen(
                        "{output_file}",
                        filename,
                        1,
                    );

                    let response = info_bar::ask_question(question).await;
                    self.btn.set_sensitive(true);

                    let next_state = match response {
                        gtk::ResponseType::Apply => {
                            // This one is used for "Yes to all"
                            overwrite_all = true;
                            ConfirmedOutputTo(Rc::clone(&path))
                        }
                        gtk::ResponseType::Cancel => {
                            self.impl_.cancel();
                            break;
                        }
                        gtk::ResponseType::No => SkipCurrent,
                        gtk::ResponseType::Yes => ConfirmedOutputTo(Rc::clone(&path)),
                        other => unimplemented!(
                            "Response {:?} in OutputBaseController::ask_overwrite_question",
                            other,
                        ),
                    };

                    main::set_cursor_waiting();
                    state = next_state;
                }
            }
        }
    }

    async fn process(&mut self, output_path: &Path) -> Result<(), MediaProcessorError> {
        enum Item {
            MediaEvent(MediaEvent),
            Tick,
        }

        let media_event_stream = match self.impl_.process(output_path).await? {
            ProcessingType::Async(media_event_stream) => media_event_stream,
            ProcessingType::Sync => return Ok(()),
        };

        // async processing

        let tick_stream = glib::interval_stream(PROGRESS_TIMER_PERIOD);
        let mut combined_streams = stream::select(
            media_event_stream.map(Item::MediaEvent),
            tick_stream.map(|_| Item::Tick),
        );

        while let Some(item) = combined_streams.next().await {
            match item {
                Item::MediaEvent(media_event) => {
                    debug!("handling media event {:?}", media_event);
                    if self.impl_.handle_media_event(media_event)?.is_done() {
                        break;
                    }
                }
                Item::Tick => {
                    self.progress_bar.set_fraction(self.impl_.report_progress());
                }
            }
        }

        Ok(())
    }
}

pub trait OutputControllerImpl: UIController {
    type MediaProcessorImplType: MediaProcessorImpl + 'static;
    type OutputEvent: From<generic_output::Event>
        + Into<generic_output::Event>
        + Into<UIEvent>
        + 'static;

    const FOCUS_CONTEXT: UIFocusContext;
    const BTN_NAME: &'static str;
    const LIST_NAME: &'static str;
    const PROGRESS_BAR_NAME: &'static str;

    fn new_processor(&self) -> Self::MediaProcessorImplType;
}

pub struct OutputMediaFileInfo {
    pub format: Format,
    pub path: Rc<Path>,
    pub extension: String,
    pub stream_ids: Arc<RwLock<HashSet<String>>>,
}

impl OutputMediaFileInfo {
    pub fn new(format: Format, src_info: &MediaInfo) -> Self {
        let (stream_ids, content) = src_info.streams.ids_to_export(format);
        let extension = metadata::Factory::extension(format, content).to_owned();

        OutputMediaFileInfo {
            path: src_info.path.with_extension(&extension).into(),
            extension,
            format,
            stream_ids: Arc::new(RwLock::new(stream_ids)),
        }
    }
}

pub struct Controller<Impl> {
    pub(super) impl_: Impl,

    progress_bar: gtk::ProgressBar,
    list: gtk::ListBox,
    pub(super) btn: gtk::Button,
    btn_default_label: glib::GString,

    perspective_selector: gtk::MenuButton,
    pub(super) open_action: Option<gio::SimpleAction>,
    pub(super) page: gtk::Widget,

    pub(crate) is_busy: bool,
    processor_abort_handle: Option<AbortHandle>,
}

impl<Impl: OutputControllerImpl + 'static> Controller<Impl> {
    pub fn new_generic(impl_: Impl, builder: &gtk::Builder) -> Self {
        let btn: gtk::Button = builder.get_object(Impl::BTN_NAME).unwrap();
        let list: gtk::ListBox = builder.get_object(Impl::LIST_NAME).unwrap();
        let page: gtk::Widget = list
            .get_parent()
            .unwrap_or_else(|| panic!("Couldn't get parent for list {}", Impl::LIST_NAME));

        let ctrl = Controller {
            impl_,

            btn_default_label: btn.get_label().unwrap(),
            btn,
            list,
            progress_bar: builder.get_object(Impl::PROGRESS_BAR_NAME).unwrap(),

            perspective_selector: builder.get_object("perspective-menu-btn").unwrap(),
            open_action: None,
            page,

            is_busy: false,
            processor_abort_handle: None,
        };

        ctrl.btn.set_sensitive(false);

        ctrl
    }

    pub async fn start(&mut self) {
        self.switch_to_busy();

        self.processor_abort_handle = Some(
            MediaProcessor::<Impl>::spawn(
                self.impl_.new_processor(),
                self.progress_bar.clone(),
                self.btn.clone(),
            )
            .await,
        );
    }

    pub fn cancel(&mut self) {
        if let Some(abort_handle) = self.processor_abort_handle.take() {
            abort_handle.abort();
        }
    }

    fn switch_to_busy(&mut self) {
        self.list.set_sensitive(false);
        self.btn.set_label(&gettext("Cancel"));

        self.perspective_selector.set_sensitive(false);
        self.open_action.as_ref().unwrap().set_enabled(false);

        main::set_cursor_waiting();

        self.is_busy = true;
    }

    pub(crate) fn switch_to_available(&mut self) {
        self.processor_abort_handle = None;
        self.is_busy = false;

        self.progress_bar.set_fraction(0f64);
        self.list.set_sensitive(true);
        self.btn.set_label(self.btn_default_label.as_str());

        self.perspective_selector.set_sensitive(true);
        self.open_action.as_ref().unwrap().set_enabled(true);

        main::reset_cursor();
    }
}

impl<Impl: OutputControllerImpl> UIController for Controller<Impl> {
    fn new_media(&mut self, pipeline: &PlaybackPipeline) {
        self.btn.set_sensitive(true);
        self.impl_.new_media(pipeline);
    }

    fn cleanup(&mut self) {
        self.progress_bar.set_fraction(0f64);
        self.btn.set_sensitive(false);
        self.impl_.cleanup();
    }

    fn streams_changed(&mut self, info: &MediaInfo) {
        self.impl_.streams_changed(info);
    }

    fn grab_focus(&self) {
        self.btn.grab_default();
        if let Some(selected_row) = self.list.get_selected_row() {
            selected_row.grab_focus();
        }
    }
}
<commit_msg>ui/generic_output cleanups<commit_after>use futures::{
    channel::{mpsc as async_mpsc, oneshot},
    future::{abortable, AbortHandle, LocalBoxFuture},
    prelude::*,
    stream,
};

use gettextrs::gettext;
use gtk::prelude::*;

use log::debug;

use std::{
    collections::HashSet,
    fmt,
    path::Path,
    rc::Rc,
    sync::{Arc, RwLock},
    time::Duration,
};

use media::MediaEvent;
use metadata::{Format, MediaInfo};

use crate::{generic_output, info_bar, main, prelude::*, spawn, UIEvent};

pub const MEDIA_EVENT_CHANNEL_CAPACITY: usize = 1;
const PROGRESS_TIMER_PERIOD: Duration = Duration::from_millis(250);

pub enum ProcessingType {
    Async(async_mpsc::Receiver<media::MediaEvent>),
    Sync,
}

pub enum MediaEventHandling {
    Done,
    ExpectingMore,
}

impl MediaEventHandling {
    pub fn is_done(&self) -> bool {
        matches!(self, MediaEventHandling::Done)
    }
}

#[derive(Debug, PartialEq)]
enum ProcessingState {
    AllDone,
    ConfirmedOutputTo(Rc<Path>),
    Init,
    SkipCurrent,
    DoneWithCurrent,
    WouldOutputTo(Rc<Path>),
}

#[derive(Debug)]
pub struct MediaProcessorError(String);

impl From<String> for MediaProcessorError {
    fn from(msg: String) -> Self {
        MediaProcessorError(msg)
    }
}

impl fmt::Display for MediaProcessorError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl std::error::Error for MediaProcessorError {}

pub trait MediaProcessorImpl: Iterator<Item = Rc<Path>> {
    fn process<'a>(
        &'a mut self,
        output_path: &'a Path,
    ) -> LocalBoxFuture<'a, Result<ProcessingType, MediaProcessorError>>;
    fn cancel(&mut self);
    fn handle_media_event(
        &mut self,
        event: MediaEvent,
    ) -> Result<MediaEventHandling, MediaProcessorError>;
    fn report_progress(&mut self) -> f64;
    fn completion_msg() -> String;
}

struct MediaProcessor<CtrlImpl: OutputControllerImpl + 'static> {
    impl_: CtrlImpl::MediaProcessorImplType,
    progress_bar: gtk::ProgressBar,
    btn: gtk::Button,
}

impl<CtrlImpl: OutputControllerImpl + 'static> MediaProcessor<CtrlImpl> {
    pub(super) async fn spawn(
        impl_: CtrlImpl::MediaProcessorImplType,
        progress_bar: gtk::ProgressBar,
        btn: gtk::Button,
    ) -> AbortHandle {
        let mut this = MediaProcessor {
            impl_,
            progress_bar,
            btn,
        };

        let (sender, receiver) = oneshot::channel();
        spawn(async move {
            let (abortable_run, abort_handle) = abortable(Self::run(&mut this));
            sender.send(abort_handle).unwrap();

            if abortable_run.await.is_err() {
                this.impl_.cancel();
            }

            UIEventChannel::send(CtrlImpl::OutputEvent::from(
                generic_output::Event::ActionOver,
            ));
        });

        receiver.await.unwrap()
    }

    async fn run(&mut self) {
        use ProcessingState::*;

        let mut state = ProcessingState::Init;
        let mut overwrite_all = false;

        loop {
            match state {
                AllDone => {
                    info_bar::show_info(CtrlImpl::MediaProcessorImplType::completion_msg());
                    break;
                }
                ConfirmedOutputTo(path) => {
                    if let Err(err) = self.process(path.as_ref()).await {
                        info_bar::show_error(err);
                        break;
                    }

                    state = DoneWithCurrent;
                }
                Init | DoneWithCurrent => {
                    state = self.impl_.next().map_or(AllDone, WouldOutputTo);
                }
                SkipCurrent => {
                    match self.impl_.next() {
                        Some(path) => state = WouldOutputTo(path),
                        None => {
                            // Don't display the success message when the user decided
                            // to skip (not overwrite) last part as it seems missleading
                            break;
                        }
                    }
                }
                WouldOutputTo(path) => {
                    if !path.exists() || overwrite_all {
                        state = ConfirmedOutputTo(path);
                        continue;
                    }

                    // Path exists and overwrite_all is not true
                    self.btn.set_sensitive(false);
                    main::reset_cursor();

                    let filename = path
                        .file_name()
                        .expect("no `filename` in `path`")
                        .to_str()
                        .expect("can't get printable `str` from `filename`");
                    let question = gettext("{output_file}\nalready exists. Overwrite?").replacen(
                        "{output_file}",
                        filename,
                        1,
                    );

                    let response = info_bar::ask_question(question).await;
                    self.btn.set_sensitive(true);

                    let next_state = match response {
                        gtk::ResponseType::Apply => {
                            // This one is used for "Yes to all"
                            overwrite_all = true;
                            ConfirmedOutputTo(Rc::clone(&path))
                        }
                        gtk::ResponseType::Cancel => {
                            self.impl_.cancel();
                            break;
                        }
                        gtk::ResponseType::No => SkipCurrent,
                        gtk::ResponseType::Yes => ConfirmedOutputTo(Rc::clone(&path)),
                        other => unimplemented!("{:?}", other),
                    };

                    main::set_cursor_waiting();
                    state = next_state;
                }
            }
        }
    }

    async fn process(&mut self, output_path: &Path) -> Result<(), MediaProcessorError> {
        enum Item {
            MediaEvent(MediaEvent),
            Tick,
        }

        let media_event_stream = match self.impl_.process(output_path).await? {
            ProcessingType::Async(media_event_stream) => media_event_stream,
            ProcessingType::Sync => return Ok(()),
        };

        // async processing

        let tick_stream = glib::interval_stream(PROGRESS_TIMER_PERIOD);
        let mut combined_streams = stream::select(
            media_event_stream.map(Item::MediaEvent),
            tick_stream.map(|_| Item::Tick),
        );

        while let Some(item) = combined_streams.next().await {
            match item {
                Item::MediaEvent(media_event) => {
                    debug!("handling media event {:?}", media_event);
                    if self.impl_.handle_media_event(media_event)?.is_done() {
                        break;
                    }
                }
                Item::Tick => {
                    self.progress_bar.set_fraction(self.impl_.report_progress());
                }
            }
        }

        Ok(())
    }
}

pub trait OutputControllerImpl: UIController {
    type MediaProcessorImplType: MediaProcessorImpl + 'static;
    type OutputEvent: From<generic_output::Event>
        + Into<generic_output::Event>
        + Into<UIEvent>
        + 'static;

    const FOCUS_CONTEXT: UIFocusContext;
    const BTN_NAME: &'static str;
    const LIST_NAME: &'static str;
    const PROGRESS_BAR_NAME: &'static str;

    fn new_processor(&self) -> Self::MediaProcessorImplType;
}

pub struct OutputMediaFileInfo {
    pub format: Format,
    pub path: Rc<Path>,
    pub extension: String,
    pub stream_ids: Arc<RwLock<HashSet<String>>>,
}

impl OutputMediaFileInfo {
    pub fn new(format: Format, src_info: &MediaInfo) -> Self {
        let (stream_ids, content) = src_info.streams.ids_to_export(format);
        let extension = metadata::Factory::extension(format, content).to_owned();

        OutputMediaFileInfo {
            path: src_info.path.with_extension(&extension).into(),
            extension,
            format,
            stream_ids: Arc::new(RwLock::new(stream_ids)),
        }
    }
}

pub struct Controller<Impl> {
    pub(super) impl_: Impl,

    progress_bar: gtk::ProgressBar,
    list: gtk::ListBox,
    pub(super) btn: gtk::Button,
    btn_default_label: glib::GString,

    perspective_selector: gtk::MenuButton,
    pub(super) open_action: Option<gio::SimpleAction>,
    pub(super) page: gtk::Widget,

    pub(super) is_busy: bool,
    processor_abort_handle: Option<AbortHandle>,
}

impl<Impl: OutputControllerImpl + 'static> Controller<Impl> {
    pub fn new_generic(impl_: Impl, builder: &gtk::Builder) -> Self {
        let btn: gtk::Button = builder.get_object(Impl::BTN_NAME).unwrap();
        btn.set_sensitive(false);
        let list: gtk::ListBox = builder.get_object(Impl::LIST_NAME).unwrap();
        let page: gtk::Widget = list
            .get_parent()
            .unwrap_or_else(|| panic!("Couldn't get parent for list {}", Impl::LIST_NAME));

        Controller {
            impl_,
            btn_default_label: btn.get_label().unwrap(),
            btn,
            list,
            progress_bar: builder.get_object(Impl::PROGRESS_BAR_NAME).unwrap(),
            perspective_selector: builder.get_object("perspective-menu-btn").unwrap(),
            open_action: None,
            page,
            is_busy: false,
            processor_abort_handle: None,
        }
    }

    pub async fn start(&mut self) {
        self.switch_to_busy();

        self.processor_abort_handle = Some(
            MediaProcessor::<Impl>::spawn(
                self.impl_.new_processor(),
                self.progress_bar.clone(),
                self.btn.clone(),
            )
            .await,
        );
    }

    pub fn cancel(&mut self) {
        if let Some(abort_handle) = self.processor_abort_handle.take() {
            abort_handle.abort();
        }
    }

    fn switch_to_busy(&mut self) {
        self.list.set_sensitive(false);
        self.btn.set_label(&gettext("Cancel"));

        self.perspective_selector.set_sensitive(false);
        self.open_action.as_ref().unwrap().set_enabled(false);

        main::set_cursor_waiting();

        self.is_busy = true;
    }

    pub(crate) fn switch_to_available(&mut self) {
        self.processor_abort_handle = None;
        self.is_busy = false;

        self.progress_bar.set_fraction(0f64);
        self.list.set_sensitive(true);
        self.btn.set_label(self.btn_default_label.as_str());

        self.perspective_selector.set_sensitive(true);
        self.open_action.as_ref().unwrap().set_enabled(true);

        main::reset_cursor();
    }
}

impl<Impl: OutputControllerImpl> UIController for Controller<Impl> {
    fn new_media(&mut self, pipeline: &PlaybackPipeline) {
        self.btn.set_sensitive(true);
        self.impl_.new_media(pipeline);
    }

    fn cleanup(&mut self) {
        self.progress_bar.set_fraction(0f64);
        self.btn.set_sensitive(false);
        self.impl_.cleanup();
    }

    fn streams_changed(&mut self, info: &MediaInfo) {
        self.impl_.streams_changed(info);
    }

    fn grab_focus(&self) {
        self.btn.grab_default();
        if let Some(selected_row) = self.list.get_selected_row() {
            selected_row.grab_focus();
        }
    }
}
<|endoftext|>


<commit_before>//! `prost-build` is a small library which makes it easy to add build-time code generation of
//! `.proto` files to a Cargo project.
//!
//! ## Example
//!
//! Let's create a small crate, `snazzy`, that defines a collection of
//! snazzy new items in a protobuf file.
//!
//! ```bash
//! $ cargo new snazzy && cd snazzy
//! ```
//!
//! First, add `prost-build`, `prost` and its public dependencies to `Cargo.toml`
//! (see [crates.io](https://crates.io/crates/prost) for the current versions):
//!
//! ```toml
//! [dependencies]
//! bytes = <bytes-version>
//! prost = <prost-version>
//! prost-derive = <prost-version>
//!
//! [build-dependencies]
//! prost-build = <prost-version>
//! ```
//!
//! Next, add `src/items.proto` to the project:
//!
//! ```proto
//! syntax = "proto3";
//!
//! package snazzy.items;
//!
//! // A snazzy new shirt!
//! message Shirt {
//! enum Size {
//!     SMALL = 0;
//!     MEDIUM = 1;
//!     LARGE = 2;
//! }
//!
//! string color = 1;
//! Size size = 2;
//! }
//! ```
//!
//! To generate Rust code from `items.proto`, we use `prost-build` in the crate's
//! `build.rs` build-script:
//!
//! ```rust,no_run
//! extern crate prost_build;
//!
//! fn main() {
//!     prost_build::compile_protos(&["src/items.proto"],
//!                                 &["src/"]).unwrap();
//! }
//! ```
//!
//! And finally, in `lib.rs`, include the generated code:
//!
//! ```rust,ignore
//! extern crate prost;
//! #[macro_use]
//! extern crate prost_derive;
//!
//! // Include the `items` module, which is generated from items.proto.
//! pub mod items {
//!     include!(concat!(env!("OUT_DIR"), "/items.rs"));
//! }
//!
//! pub fn create_large_shirt(color: String) -> items::Shirt {
//!     let mut shirt = items::Shirt::default();
//!     shirt.color = color;
//!     shirt.set_size(items::shirt::Size::Large);
//!     shirt
//! }
//! ```
//!
//! That's it! Run `cargo doc` to see documentation for the generated code. The full
//! example project can be found on [GitHub](https://github.com/danburkert/snazzy).

#![doc(html_root_url = "https://docs.rs/prost-build/0.1.1")]

extern crate bytes;
extern crate curl;
extern crate prost;
extern crate prost_codegen;
extern crate tempdir;
extern crate zip;

use std::env;
use std::fs;
use std::io::{
    self,
    Cursor,
    Error,
    ErrorKind,
    Read,
    Result,
    Write,
};
use std::path::{
    Path,
    PathBuf,
};
use std::process::Command;

use bytes::Buf;
use curl::easy::Easy;
use zip::ZipArchive;

use prost::Message;
use prost_codegen::google::protobuf::FileDescriptorSet;

use prost_codegen::CodeGeneratorConfig;

/// Compile `.proto` files into Rust files during a Cargo build.
///
/// The generated `.rs` files will be written to the Cargo `OUT_DIR` directory, suitable for use
/// with the [include!][1] macro. See the [Cargo `build.rs` code generation][2] example for more
/// info.
///
/// This function should be called in a project's `build.rs`.
///
/// # Sourcing `protoc`
///
/// `prost` uses the Protocol Buffers compiler, `protoc`, to parse `.proto` files into a
/// representation that can be transformed into Rust. If present, `compile_protos` will use the
/// `PROTOC` and `PROTOC_INCLUDE` environment variables for locating `protoc` and the protobuf
/// built-in includes. For example, on a macOS system where protobuf is installed with Homebrew,
/// set the environment to:
///
/// ```bash
/// PROTOC=/usr/local/bin/protoc
/// PROTOC_INCLUDE=/usr/local/include
/// ```
///
/// and in a typical Linux installation:
///
/// ```bash
/// PROTOC=/usr/bin/protoc
/// PROTOC_INCLUDE=/usr/include
/// ```
///
/// If `PROTOC` and `PROTOC_INCLUDE` are not found in the environment, then a pre-compiled `protoc`
/// binary will be downloaded and cached in the target directory. Pre-compiled `protoc` binaries
/// exist for Linux, macOS, and Windows systems.
///
/// # Arguments
///
/// **`protos`** - Paths to `.proto` files to compile. Any transitively [imported][3] `.proto`
/// files will automatically be included.
///
/// **`includes`** - Paths to directories in which to search for imports. Directories will be
/// searched in order. The `.proto` files passed in **`protos`** must be found
/// in one of the provided include directories.
///
/// # Errors
///
/// This function can fail for a number of reasons:
///
///   - Failure to locate or download `protoc`.
///   - Failure to parse the `.proto`s.
///   - Failure to locate an imported `.proto`.
///
/// It's expected that this function call be `unwrap`ed in a `build.rs`; there is typically no
/// reason to gracefully recover from errors during a build.
///
/// # Example `build.rs`
///
/// ```norun
/// extern crate prost_build;
///
/// fn main() {
///     prost_build::compile_protos(&["src/frontend.proto",
///                                   "src/backend.proto"],
///                                 &["src"]).unwrap();
/// }
/// ```
///
/// [1]: https://doc.rust-lang.org/std/macro.include.html
/// [2]: http://doc.crates.io/build-script.html#case-study-code-generation
/// [3]: https://developers.google.com/protocol-buffers/docs/proto3#importing-definitions
pub fn compile_protos<P>(protos: &[P], includes: &[P]) -> Result<()> where P: AsRef<Path> {
    compile_protos_with_config(&CodeGeneratorConfig::new(),
                               protos,
                               includes)
}

/// Compile `.proto` files into Rust files during a Cargo build with additional code generator
/// configuration options.
///
/// See `CodeGeneratorConfig` for the available options. Other than providing additional control
/// over the generated code, this function works identically to `compile_protos`.
///
/// # Example `build.rs`
///
/// ```rust,ignore
/// extern crate prost_build;
/// extern crate prost_codegen;
///
/// fn main() {
///     let config = prost_codegen::CodeGeneratorConfig::new();
/// #   // TODO: Add some configuration.
///     prost_build::compile_protos(&config,
///                                 &["src/frontend.proto",
///                                   "src/backend.proto"],
///                                 &["src"]).unwrap();
/// }
/// ```
pub fn compile_protos_with_config<P>(config: &CodeGeneratorConfig,
                                     protos: &[P],
                                     includes: &[P])
                                     -> Result<()> where P: AsRef<Path> {

    let target = match env::var("OUT_DIR") {
        Ok(val) => PathBuf::from(val),
        Err(env::VarError::NotPresent) => return Err(Error::new(ErrorKind::Other,
                                                                "OUT_DIR environment variable not set")),
        Err(env::VarError::NotUnicode(..)) => return Err(Error::new(ErrorKind::InvalidData,
                                                                    "OUT_DIR environment variable")),
    };

    let tmp = tempdir::TempDir::new("proto-build")?;

    // TODO: We should probably emit 'rerun-if-changed=PATH' directives for
    // cargo, however according to
    // http://doc.crates.io/build-script.html#outputs-of-the-build-script if we
    // output any, those paths will replace the default crate root, which we
    // don't want. Figure out how to do it in an additive way, perhaps gcc-rs
    // has this figured out.

    // If the protoc directory doesn't already exist from a previous build,
    // create it, and extract the protoc release into it.
    let protoc_dir = target.join("protoc");
    if !protoc_dir.exists() {
        fs::create_dir(&protoc_dir)?;
        download_protoc(&protoc_dir)?;
    }

    let mut protoc = protoc_dir.join("bin");
    protoc.push("protoc");
    protoc.set_extension(env::consts::EXE_EXTENSION);

    let descriptor_set = tmp.path().join("proto-descriptor-set");

    let mut cmd = Command::new(protoc);
    cmd.arg("-I").arg(protoc_dir.join("include"))
       .arg("--include_imports")
       .arg("--include_source_info")
       .arg("-o").arg(&descriptor_set);

    for include in includes {
        cmd.arg("-I").arg(include.as_ref());
    }

    for proto in protos {
        cmd.arg(proto.as_ref());
    }

    let output = cmd.output()?;
    if !output.status.success() {
        return Err(Error::new(ErrorKind::Other,
                              format!("protoc failed: {}",
                                      String::from_utf8_lossy(&output.stderr))));
    }

    let mut buf = Vec::new();
    fs::File::open(descriptor_set)?.read_to_end(&mut buf)?;
    let len = buf.len();
    let descriptor_set = FileDescriptorSet::decode(&mut <Cursor<Vec<u8>> as Buf>::take(Cursor::new(buf), len))?;

    let modules = prost_codegen::generate(config, descriptor_set.file);
    for (module, content) in modules {
        let mut filename = match module.last() {
            Some(filename) => PathBuf::from(filename),
            None => return Err(Error::new(ErrorKind::InvalidInput, ".proto must have a package")),
        };
        filename.set_extension("rs");
        let mut file = fs::File::create(target.join(filename))?;
        file.write_all(content.as_bytes())?;
        file.flush()?;
    }

    Ok(())
}

/// Downloads and unpacks the protoc package for the current architecture to the target path.
fn download_protoc(target: &Path) -> Result<()> {
    let url = protoc_url()?;
    let mut data = Vec::new();
    let mut handle = Easy::new();

    handle.url(url)?;
    handle.follow_location(true)?;
    {
        let mut transfer = handle.transfer();
        transfer.write_function(|new_data| {
            data.extend_from_slice(new_data);
            Ok(new_data.len())
        })?;
        transfer.perform()?;
    }

    let mut archive = ZipArchive::new(Cursor::new(data))?;

    for i in 0..archive.len()
    {
        let mut src = archive.by_index(i)?;

        let mut path = target.to_owned();
        path.push(src.name());

        if src.name().ends_with('/') {
            fs::create_dir(&path)?;
        } else {
            let mut dest = &mut fs::File::create(&path)?;
            io::copy(&mut src, &mut dest)?;

            #[cfg(unix)]
            fn convert_permissions(mode: u32) -> Option<fs::Permissions> {
                use std::os::unix::fs::PermissionsExt;
                Some(fs::Permissions::from_mode(mode))
            }
            #[cfg(not(unix))]
            fn convert_permissions(_mode: u32) -> Option<fs::Permissions> {
                None
            }
            if let Some(permissions) = src.unix_mode().and_then(convert_permissions) {
                fs::set_permissions(&path, permissions)?;
            }
        }
    }

    Ok(())
}

fn protoc_url() -> Result<&'static str> {
    match (env::consts::OS, env::consts::ARCH) {
        ("linux", "x86")    => Ok("https://github.com/google/protobuf/releases/download/v3.3.0/protoc-3.3.0-linux-x86_32.zip"),
        ("linux", "x86_64") => Ok("https://github.com/google/protobuf/releases/download/v3.3.0/protoc-3.3.0-linux-x86_64.zip"),
        ("macos", "x86")    => Ok("https://github.com/google/protobuf/releases/download/v3.3.0/protoc-3.3.0-osx-x86_32.zip"),
        ("macos", "x86_64") => Ok("https://github.com/google/protobuf/releases/download/v3.3.0/protoc-3.3.0-osx-x86_64.zip"),
        ("windows", _)      => Ok("https://github.com/google/protobuf/releases/download/v3.3.0/protoc-3.3.0-win32.zip"),
        _ => Err(Error::new(ErrorKind::NotFound,
                            format!("no precompiled protoc binary for current the platform: {}-{}",
                                    env::consts::OS, env::consts::ARCH))),
    }
}

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

    #[test]
    fn test_download_protoc() {
        let dir = tempdir::TempDir::new("protoc").unwrap();
        download_protoc(dir.path()).unwrap();
    }
}
<commit_msg>prost-build: use PROTOC and PROTOC_INCLUDE env paths is set<commit_after>//! `prost-build` is a small library which makes it easy to add build-time code generation of
//! `.proto` files to a Cargo project.
//!
//! ## Example
//!
//! Let's create a small crate, `snazzy`, that defines a collection of
//! snazzy new items in a protobuf file.
//!
//! ```bash
//! $ cargo new snazzy && cd snazzy
//! ```
//!
//! First, add `prost-build`, `prost` and its public dependencies to `Cargo.toml`
//! (see [crates.io](https://crates.io/crates/prost) for the current versions):
//!
//! ```toml
//! [dependencies]
//! bytes = <bytes-version>
//! prost = <prost-version>
//! prost-derive = <prost-version>
//!
//! [build-dependencies]
//! prost-build = <prost-version>
//! ```
//!
//! Next, add `src/items.proto` to the project:
//!
//! ```proto
//! syntax = "proto3";
//!
//! package snazzy.items;
//!
//! // A snazzy new shirt!
//! message Shirt {
//! enum Size {
//!     SMALL = 0;
//!     MEDIUM = 1;
//!     LARGE = 2;
//! }
//!
//! string color = 1;
//! Size size = 2;
//! }
//! ```
//!
//! To generate Rust code from `items.proto`, we use `prost-build` in the crate's
//! `build.rs` build-script:
//!
//! ```rust,no_run
//! extern crate prost_build;
//!
//! fn main() {
//!     prost_build::compile_protos(&["src/items.proto"],
//!                                 &["src/"]).unwrap();
//! }
//! ```
//!
//! And finally, in `lib.rs`, include the generated code:
//!
//! ```rust,ignore
//! extern crate prost;
//! #[macro_use]
//! extern crate prost_derive;
//!
//! // Include the `items` module, which is generated from items.proto.
//! pub mod items {
//!     include!(concat!(env!("OUT_DIR"), "/items.rs"));
//! }
//!
//! pub fn create_large_shirt(color: String) -> items::Shirt {
//!     let mut shirt = items::Shirt::default();
//!     shirt.color = color;
//!     shirt.set_size(items::shirt::Size::Large);
//!     shirt
//! }
//! ```
//!
//! That's it! Run `cargo doc` to see documentation for the generated code. The full
//! example project can be found on [GitHub](https://github.com/danburkert/snazzy).

#![doc(html_root_url = "https://docs.rs/prost-build/0.1.1")]

extern crate bytes;
extern crate curl;
extern crate prost;
extern crate prost_codegen;
extern crate tempdir;
extern crate zip;

use std::env;
use std::fs;
use std::io::{
    self,
    Cursor,
    Error,
    ErrorKind,
    Read,
    Result,
    Write,
};
use std::path::{
    Path,
    PathBuf,
};
use std::process::Command;

use bytes::Buf;
use curl::easy::Easy;
use zip::ZipArchive;

use prost::Message;
use prost_codegen::google::protobuf::FileDescriptorSet;

use prost_codegen::CodeGeneratorConfig;

/// Compile `.proto` files into Rust files during a Cargo build.
///
/// The generated `.rs` files will be written to the Cargo `OUT_DIR` directory, suitable for use
/// with the [include!][1] macro. See the [Cargo `build.rs` code generation][2] example for more
/// info.
///
/// This function should be called in a project's `build.rs`.
///
/// # Sourcing `protoc`
///
/// `prost` uses the Protocol Buffers compiler, `protoc`, to parse `.proto` files into a
/// representation that can be transformed into Rust. If present, `compile_protos` will use the
/// `PROTOC` and `PROTOC_INCLUDE` environment variables for locating `protoc` and the protobuf
/// built-in includes. For example, on a macOS system where protobuf is installed with Homebrew,
/// set the environment to:
///
/// ```bash
/// PROTOC=/usr/local/bin/protoc
/// PROTOC_INCLUDE=/usr/local/include
/// ```
///
/// and in a typical Linux installation:
///
/// ```bash
/// PROTOC=/usr/bin/protoc
/// PROTOC_INCLUDE=/usr/include
/// ```
///
/// If `PROTOC` and `PROTOC_INCLUDE` are not found in the environment, then a pre-compiled `protoc`
/// binary will be downloaded and cached in the target directory. Pre-compiled `protoc` binaries
/// exist for Linux, macOS, and Windows systems.
///
/// # Arguments
///
/// **`protos`** - Paths to `.proto` files to compile. Any transitively [imported][3] `.proto`
/// files will automatically be included.
///
/// **`includes`** - Paths to directories in which to search for imports. Directories will be
/// searched in order. The `.proto` files passed in **`protos`** must be found
/// in one of the provided include directories.
///
/// # Errors
///
/// This function can fail for a number of reasons:
///
///   - Failure to locate or download `protoc`.
///   - Failure to parse the `.proto`s.
///   - Failure to locate an imported `.proto`.
///
/// It's expected that this function call be `unwrap`ed in a `build.rs`; there is typically no
/// reason to gracefully recover from errors during a build.
///
/// # Example `build.rs`
///
/// ```norun
/// extern crate prost_build;
///
/// fn main() {
///     prost_build::compile_protos(&["src/frontend.proto",
///                                   "src/backend.proto"],
///                                 &["src"]).unwrap();
/// }
/// ```
///
/// [1]: https://doc.rust-lang.org/std/macro.include.html
/// [2]: http://doc.crates.io/build-script.html#case-study-code-generation
/// [3]: https://developers.google.com/protocol-buffers/docs/proto3#importing-definitions
pub fn compile_protos<P>(protos: &[P], includes: &[P]) -> Result<()> where P: AsRef<Path> {
    compile_protos_with_config(&CodeGeneratorConfig::new(),
                               protos,
                               includes)
}

/// Compile `.proto` files into Rust files during a Cargo build with additional code generator
/// configuration options.
///
/// See `CodeGeneratorConfig` for the available options. Other than providing additional control
/// over the generated code, this function works identically to `compile_protos`.
///
/// # Example `build.rs`
///
/// ```rust,ignore
/// extern crate prost_build;
/// extern crate prost_codegen;
///
/// fn main() {
///     let config = prost_codegen::CodeGeneratorConfig::new();
/// #   // TODO: Add some configuration.
///     prost_build::compile_protos(&config,
///                                 &["src/frontend.proto",
///                                   "src/backend.proto"],
///                                 &["src"]).unwrap();
/// }
/// ```
pub fn compile_protos_with_config<P>(config: &CodeGeneratorConfig,
                                     protos: &[P],
                                     includes: &[P])
                                     -> Result<()> where P: AsRef<Path> {

    let target = match env::var("OUT_DIR") {
        Ok(val) => PathBuf::from(val),
        Err(env::VarError::NotPresent) => {
            return Err(Error::new(ErrorKind::Other,
                                  "OUT_DIR environment variable is not set"));
        },
        Err(env::VarError::NotUnicode(..)) => {
            return Err(Error::new(ErrorKind::InvalidData,
                                  "OUT_DIR environment variable is not valid UTF-8"));
        },
    };

    // TODO: We should probably emit 'rerun-if-changed=PATH' directives for
    // cargo, however according to
    // http://doc.crates.io/build-script.html#outputs-of-the-build-script if we
    // output any, those paths will replace the default crate root, which we
    // don't want. Figure out how to do it in an additive way, perhaps gcc-rs
    // has this figured out.

    // Find protoc.
    let (protoc, protoc_include) = match find_protoc()? {
        Some(paths) => paths,
        None => {
            // If the protoc directory doesn't already exist from a previous build,
            // create it, and extract the protoc release into it.
            let protoc_dir = target.join("protoc");
            if !protoc_dir.exists() {
                fs::create_dir(&protoc_dir)?;
                download_protoc(&protoc_dir)?;
            }

            let mut protoc = protoc_dir.join("bin");
            protoc.push("protoc");
            protoc.set_extension(env::consts::EXE_EXTENSION);

            (protoc, protoc_dir.join("include"))
        },
    };

    let tmp = tempdir::TempDir::new("prost-build")?;
    let descriptor_set = tmp.path().join("prost-descriptor-set");

    let mut cmd = Command::new(protoc);
    cmd.arg("--include_imports")
       .arg("--include_source_info")
       .arg("-o").arg(&descriptor_set);

    for include in includes {
        cmd.arg("-I").arg(include.as_ref());
    }

    // Set the protoc include after the user includes in case the user wants to
    // override one of the built-in .protos.
    cmd.arg("-I").arg(protoc_include);

    for proto in protos {
        cmd.arg(proto.as_ref());
    }

    let output = cmd.output()?;
    if !output.status.success() {
        return Err(Error::new(ErrorKind::Other,
                              format!("protoc failed: {}",
                                      String::from_utf8_lossy(&output.stderr))));
    }

    let mut buf = Vec::new();
    fs::File::open(descriptor_set)?.read_to_end(&mut buf)?;
    let len = buf.len();
    let descriptor_set = FileDescriptorSet::decode(&mut <Cursor<Vec<u8>> as Buf>::take(Cursor::new(buf), len))?;

    let modules = prost_codegen::generate(config, descriptor_set.file);
    for (module, content) in modules {
        let mut filename = match module.last() {
            Some(filename) => PathBuf::from(filename),
            None => return Err(Error::new(ErrorKind::InvalidInput, ".proto must have a package")),
        };
        filename.set_extension("rs");
        let mut file = fs::File::create(target.join(filename))?;
        file.write_all(content.as_bytes())?;
        file.flush()?;
    }

    Ok(())
}

/// Finds `protoc` and the protobuf include dir in the environment, if it is available.
fn find_protoc() -> Result<Option<(PathBuf, PathBuf)>> {
    let protoc = match env::var("PROTOC") {
        Ok(val) => PathBuf::from(val),
        Err(env::VarError::NotPresent) => return Ok(None),
        Err(env::VarError::NotUnicode(..)) => {
            return Err(Error::new(ErrorKind::InvalidData,
                                  "PROTOC environment variable is not valid UTF-8"));
        },
    };

    let protoc_include = match env::var("PROTOC_INCLUDE") {
        Ok(val) => PathBuf::from(val),
        Err(env::VarError::NotPresent) => {
            // We could fall back to downloading protoc here, but if PROTOC is set without
            // PROTOC_INCLUDE, it indicates a misconfiguration, so lets bubble that back to the
            // user.
            return Err(Error::new(ErrorKind::InvalidInput,
                                  "PROTOC_INCLUDE environment variable not set (PROTOC is set)"));
        }
        Err(env::VarError::NotUnicode(..)) => {
            return Err(Error::new(ErrorKind::InvalidData,
                                  "PROTOC_INCLUDE environment variable is not valid UTF-8"));
        },
    };

    Ok(Some((protoc, protoc_include)))
}

/// Downloads and unpacks the protoc package for the current architecture to the target path.
/// Returns the paths to `protoc` and the include directory.
fn download_protoc(target: &Path) -> Result<()> {
    let url = protoc_url()?;
    let mut data = Vec::new();
    let mut handle = Easy::new();

    handle.url(url)?;
    handle.follow_location(true)?;
    {
        let mut transfer = handle.transfer();
        transfer.write_function(|new_data| {
            data.extend_from_slice(new_data);
            Ok(new_data.len())
        })?;
        transfer.perform()?;
    }

    let mut archive = ZipArchive::new(Cursor::new(data))?;

    for i in 0..archive.len()
    {
        let mut src = archive.by_index(i)?;

        let mut path = target.to_owned();
        path.push(src.name());

        if src.name().ends_with('/') {
            fs::create_dir(&path)?;
        } else {
            let mut dest = &mut fs::File::create(&path)?;
            io::copy(&mut src, &mut dest)?;

            #[cfg(unix)]
            fn convert_permissions(mode: u32) -> Option<fs::Permissions> {
                use std::os::unix::fs::PermissionsExt;
                Some(fs::Permissions::from_mode(mode))
            }
            #[cfg(not(unix))]
            fn convert_permissions(_mode: u32) -> Option<fs::Permissions> {
                None
            }
            if let Some(permissions) = src.unix_mode().and_then(convert_permissions) {
                fs::set_permissions(&path, permissions)?;
            }
        }
    }

    Ok(())
}

fn protoc_url() -> Result<&'static str> {
    match (env::consts::OS, env::consts::ARCH) {
        ("linux", "x86")    => Ok("https://github.com/google/protobuf/releases/download/v3.3.0/protoc-3.3.0-linux-x86_32.zip"),
        ("linux", "x86_64") => Ok("https://github.com/google/protobuf/releases/download/v3.3.0/protoc-3.3.0-linux-x86_64.zip"),
        ("macos", "x86")    => Ok("https://github.com/google/protobuf/releases/download/v3.3.0/protoc-3.3.0-osx-x86_32.zip"),
        ("macos", "x86_64") => Ok("https://github.com/google/protobuf/releases/download/v3.3.0/protoc-3.3.0-osx-x86_64.zip"),
        ("windows", _)      => Ok("https://github.com/google/protobuf/releases/download/v3.3.0/protoc-3.3.0-win32.zip"),
        _ => Err(Error::new(ErrorKind::NotFound,
                            format!("no precompiled protoc binary for current the platform: {}-{}",
                                    env::consts::OS, env::consts::ARCH))),
    }
}

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

    #[test]
    fn test_download_protoc() {
        let dir = tempdir::TempDir::new("protoc").unwrap();
        download_protoc(dir.path()).unwrap();
    }
}
<|endoftext|>


<commit_before>//! The state of the game itself.

use message;

use std::collections::HashMap;
use std::sync::mpsc;

use math::distance_between;
use rand::{thread_rng, Rng};

use self::super::Client;
use self::super::WebSocketEvent;

static BULLET_RADIUS: f32 = 5.0;
static PLAYER_RADIUS: f32 = 10.0;
static BULLET_SPEED: f32 = 3.0;
static PLAYER_SPEED: f32 = 2.0;
static MAP_HEIGHT: f32 = 500.0;
static MAP_WIDTH: f32 = 500.0;

/// The `GameState` contains the whole state of the game.
///
/// It consists of both players, and all the clients which are currently connected.
#[derive(Debug)]
pub struct GameState {
    players: HashMap<u32, message::Player>,
    bullets: HashMap<u32, message::Bullet>,
    clients: HashMap<u32, Client>,
    next_bullet_id: u32,
}

impl GameState {
    /// Create a new game state.
    pub fn new() -> GameState {
        GameState {
            players: HashMap::new(),
            bullets: HashMap::new(),
            clients: HashMap::new(),
            next_bullet_id: 0,
        }
    }

    /// Tries to process every available websocket event without blocking.
    pub fn process_websocket_events(&mut self, game_messages: &mpsc::Receiver<WebSocketEvent>) {
        loop {
            match game_messages.try_recv() {
                Ok(message) => self.process_websocket_event(message),
                Err(mpsc::TryRecvError::Empty) => return,
                Err(mpsc::TryRecvError::Disconnected) => panic!("Now I am disconnected?"),
            }
        }
    }

    /// Updates the game state in one tick.
    pub fn process_game_update(&mut self) {
        // Do a normal position update
        let player_ids: Vec<_> = self.players.keys().map(|i| *i).collect();
        for cur_player_id in &player_ids {
            {
                let cur_player = self.players.get(cur_player_id).unwrap();
                match (cur_player.move_x, cur_player.move_y) {
                    (None, None) => continue,
                    (Some(move_x), Some(move_y)) => {
                        let mut collides = false;
                        for cmp_player_id in &player_ids {
                            if cmp_player_id != cur_player_id {
                                let cmp_player = self.players.get(cmp_player_id).unwrap();
                                if distance_between(cur_player.x + move_x,
                                                    cur_player.y + move_y,
                                                    cmp_player.x,
                                                    cmp_player.y) <
                                   2.0 * PLAYER_RADIUS {
                                    collides = true;
                                    break;
                                }
                            }
                        }
                        if collides {
                            continue;
                        }
                    }
                    _ => panic!("Invariant not met: player moves only in one direction"),
                }
            }

            let mut player = self.players.get_mut(cur_player_id).unwrap();
            player.x = (player.x + player.move_x.unwrap_or(0.0) * PLAYER_SPEED)
                           .max(PLAYER_RADIUS)
                           .min(MAP_WIDTH - PLAYER_RADIUS);
            player.y = (player.y + player.move_y.unwrap_or(0.0) * PLAYER_SPEED)
                           .max(PLAYER_RADIUS)
                           .min(MAP_HEIGHT - PLAYER_RADIUS);
        }

        let mut destroyed_bullets = Vec::new();
        let mut destroyed_players = Vec::new();

        for (_, bullet) in &mut self.bullets {
            bullet.x += bullet.move_x.unwrap_or(0.0) * BULLET_SPEED;
            bullet.y += bullet.move_y.unwrap_or(0.0) * BULLET_SPEED;

            if bullet.x < 0.0 || bullet.x > MAP_WIDTH || bullet.y < 0.0 || bullet.y > MAP_HEIGHT {
                destroyed_bullets.push(bullet.id);
            }
        }

        // Check for collisions
        for (_, bullet) in &mut self.bullets {
            for (_, player) in &mut self.players {
                if distance_between(bullet.x, bullet.y, player.x, player.y) <
                   BULLET_RADIUS + PLAYER_RADIUS {
                    destroyed_bullets.push(bullet.id);
                    destroyed_players.push(player.id);
                }
            }
        }

        // Process destroy requests
        for bullet_id in destroyed_bullets {
            let _ = self.bullets.remove(&bullet_id);
        }

        let mut rng = thread_rng();
        for player_id in destroyed_players {
            let (new_x, new_y) = self.random_free_spot(&mut rng);
            let dead_player = self.players.get_mut(&player_id).unwrap();
            dead_player.x = new_x;
            dead_player.y = new_y;
        }
    }

    /// Send the current state to each client.
    pub fn send_state_updates(&self) {
        let value = self.serialize();

        for (_, client) in &self.clients {
            // Always ignore if the send fails.
            // We will eventually get a disconnect WebSocketMessage where we will cleanly do the disconnect.
            let _ = client.send(value.clone());
        }
    }

    /// Process a web socket event.
    fn process_websocket_event(&mut self, message: WebSocketEvent) {
        match message {
            WebSocketEvent::ClientCreated { client } => {
                let welcome_message = message::Message::Welcome {
                    id: client.id,
                    speed: PLAYER_SPEED,
                    size: PLAYER_RADIUS,
                    bullet_speed: BULLET_SPEED,
                    bullet_size: BULLET_RADIUS,
                };

                let _ = client.send(welcome_message.to_string());
                let _ = client.send(self.serialize());

                let (x, y) = self.random_free_spot(&mut thread_rng());
                let _ = self.players
                            .insert(client.id, message::Player::not_moving(client.id, x, y));

                let _ = self.clients.insert(client.id, client);
            }
            WebSocketEvent::ClientClosed { client_id } => {
                let _ = self.players.remove(&client_id);
                let _ = self.clients.remove(&client_id);
            }
            WebSocketEvent::ClientMessage { client_id, message } => {
                self.process_client_message(client_id, message);
            }
        }
    }

    /// Serialize the entire game state into one json string.
    fn serialize(&self) -> String {
        let players: Vec<_> = self.players
                                  .values()
                                  .cloned()
                                  .collect();
        let bullets: Vec<_> = self.bullets
                                  .values()
                                  .cloned()
                                  .collect();
        let state = message::Message::WorldState {
            player_count: players.len() as u32,
            alive_players: players,
            alive_bullets: bullets,
        };
        state.to_string()
    }

    /// Process a simple string message from the client.
    fn process_client_message(&mut self, client_id: u32, message: message::Message) {
        match message {
            message::Message::StartMoving { move_x, move_y } => {
                let player = self.players.get_mut(&client_id).unwrap();
                player.move_x = Some(move_x);
                player.move_y = Some(move_y);
            }
            message::Message::StopMoving => {
                let player = self.players.get_mut(&client_id).unwrap();
                player.move_x = None;
                player.move_y = None;
            }
            message::Message::Fire { move_x, move_y } => {
                let player = self.players.get(&client_id).unwrap();

                // Have to move the bullet out of the way of the player to avoid an instant collision.
                let start_x = player.x + move_x * (BULLET_RADIUS + PLAYER_RADIUS + 1.0);
                let start_y = player.y + move_y * (BULLET_RADIUS + PLAYER_RADIUS + 1.0);

                let _ = self.bullets.insert(self.next_bullet_id,
                                            message::Bullet::moving(self.next_bullet_id,
                                                                    start_x,
                                                                    start_y,
                                                                    move_x,
                                                                    move_y));

                self.next_bullet_id += 1;
            }
            _ => panic!("Unprocessed message! {}", message.to_string()),
        }
    }

    fn random_free_spot<R: Rng>(&self, rng: &mut R) -> (f32, f32) {
        static MAX_ITERATIONS: u32 = 100;

        let min_vial_x = PLAYER_RADIUS;
        let min_vial_y = PLAYER_RADIUS;
        let max_vial_x = MAP_WIDTH - PLAYER_RADIUS;
        let max_vial_y = MAP_HEIGHT - PLAYER_RADIUS;

        for _ in 1..MAX_ITERATIONS {
            let x: f32 = rng.gen_range(min_vial_x, max_vial_x);
            let y: f32 = rng.gen_range(min_vial_y, max_vial_y);

            let mut collides = false;

            for (_, player) in &self.players {
                if distance_between(x, y, player.x, player.y) < 2.0 * PLAYER_RADIUS {
                    collides = true;
                    break;
                }
            }

            for (_, bullet) in &self.bullets {
                if distance_between(x, y, bullet.x, bullet.y) < PLAYER_RADIUS + BULLET_RADIUS {
                    collides = true;
                    break;
                }
            }

            if !collides {
                return (x, y);
            }
        }
        println!("Failed to find a random empty spot for player after {} iterations",
                 MAX_ITERATIONS);

        (rng.gen_range(0.0, MAP_WIDTH), rng.gen_range(0.0, MAP_HEIGHT))
    }
}
<commit_msg>Send partial updates in reaction to incoming client commands<commit_after>//! The state of the game itself.

use message;

use std::collections::HashMap;
use std::sync::mpsc;

use math::distance_between;
use rand::{thread_rng, Rng};

use self::super::Client;
use self::super::WebSocketEvent;

static BULLET_RADIUS: f32 = 5.0;
static PLAYER_RADIUS: f32 = 10.0;
static BULLET_SPEED: f32 = 3.0;
static PLAYER_SPEED: f32 = 2.0;
static MAP_HEIGHT: f32 = 500.0;
static MAP_WIDTH: f32 = 500.0;
static TICKS_BETWEEN_FULL_UPDATES: u32 = 600; // 10s @ 60FPS

/// The `GameState` contains the whole state of the game.
///
/// It consists of both players, and all the clients which are currently connected.
#[derive(Debug)]
pub struct GameState {
    players: HashMap<u32, message::Player>,
    bullets: HashMap<u32, message::Bullet>,
    clients: HashMap<u32, Client>,
    next_bullet_id: u32,
    ticks_since_last_full_update: u32,
}

impl GameState {
    /// Create a new game state.
    pub fn new() -> GameState {
        GameState {
            players: HashMap::new(),
            bullets: HashMap::new(),
            clients: HashMap::new(),
            next_bullet_id: 0,
            ticks_since_last_full_update: 0,
        }
    }

    /// Tries to process every available websocket event without blocking.
    pub fn process_websocket_events(&mut self, game_messages: &mpsc::Receiver<WebSocketEvent>) {
        loop {
            match game_messages.try_recv() {
                Ok(message) => self.process_websocket_event(message),
                Err(mpsc::TryRecvError::Empty) => return,
                Err(mpsc::TryRecvError::Disconnected) => panic!("Now I am disconnected?"),
            }
        }
    }

    /// Updates the game state in one tick.
    pub fn process_game_update(&mut self) {
        // Do a normal position update
        let player_ids: Vec<_> = self.players.keys().map(|i| *i).collect();
        for cur_player_id in &player_ids {
            {
                let cur_player = self.players.get(cur_player_id).unwrap();
                match (cur_player.move_x, cur_player.move_y) {
                    (None, None) => continue,
                    (Some(move_x), Some(move_y)) => {
                        let mut collides = false;
                        for cmp_player_id in &player_ids {
                            if cmp_player_id != cur_player_id {
                                let cmp_player = self.players.get(cmp_player_id).unwrap();
                                if distance_between(cur_player.x + move_x,
                                                    cur_player.y + move_y,
                                                    cmp_player.x,
                                                    cmp_player.y) <
                                   2.0 * PLAYER_RADIUS {
                                    collides = true;
                                    break;
                                }
                            }
                        }
                        if collides {
                            continue;
                        }
                    }
                    _ => panic!("Invariant not met: player moves only in one direction"),
                }
            }

            let mut player = self.players.get_mut(cur_player_id).unwrap();
            player.x = (player.x + player.move_x.unwrap_or(0.0) * PLAYER_SPEED)
                           .max(PLAYER_RADIUS)
                           .min(MAP_WIDTH - PLAYER_RADIUS);
            player.y = (player.y + player.move_y.unwrap_or(0.0) * PLAYER_SPEED)
                           .max(PLAYER_RADIUS)
                           .min(MAP_HEIGHT - PLAYER_RADIUS);
        }

        let mut destroyed_bullets = Vec::new();
        let mut destroyed_players = Vec::new();

        for (_, bullet) in &mut self.bullets {
            bullet.x += bullet.move_x.unwrap_or(0.0) * BULLET_SPEED;
            bullet.y += bullet.move_y.unwrap_or(0.0) * BULLET_SPEED;

            if bullet.x < 0.0 || bullet.x > MAP_WIDTH || bullet.y < 0.0 || bullet.y > MAP_HEIGHT {
                destroyed_bullets.push(bullet.id);
            }
        }

        // Check for collisions
        for (_, bullet) in &mut self.bullets {
            for (_, player) in &mut self.players {
                if distance_between(bullet.x, bullet.y, player.x, player.y) <
                   BULLET_RADIUS + PLAYER_RADIUS {
                    destroyed_bullets.push(bullet.id);
                    destroyed_players.push(player.id);
                }
            }
        }

        // Process destroy requests
        for bullet_id in destroyed_bullets {
            let _ = self.bullets.remove(&bullet_id);
        }

        let mut rng = thread_rng();
        for player_id in destroyed_players {
            self.send_to_everybody(message::Message::PlayerDestroyed {
                id: player_id,
                killer_id: None,
                bullet_id: None,
            });

            let (new_x, new_y) = self.random_free_spot(&mut rng);

            {
                let dead_player = self.players.get_mut(&player_id).unwrap();
                dead_player.x = new_x;
                dead_player.y = new_y;
            }

            self.send_to_everybody(message::Message::PlayerSpawned {
                id: player_id,
                x: new_x,
                y: new_y,
            });
        }
    }

    /// Send the current state to each client.
    pub fn send_state_updates(&mut self) {
        if self.ticks_since_last_full_update == TICKS_BETWEEN_FULL_UPDATES {
            self.ticks_since_last_full_update = 0;
            self.send_to_everybody(self.serialize());
        } else {
            self.ticks_since_last_full_update += 1;
        }
    }

    /// Process a web socket event.
    fn process_websocket_event(&mut self, message: WebSocketEvent) {
        match message {
            WebSocketEvent::ClientCreated { client } => {
                let welcome_message = message::Message::Welcome {
                    id: client.id,
                    speed: PLAYER_SPEED,
                    size: PLAYER_RADIUS,
                    bullet_speed: BULLET_SPEED,
                    bullet_size: BULLET_RADIUS,
                };

                let _ = client.send(welcome_message.to_string());
                self.send_to_everybody(message::Message::PlayerJoined { id: client.id });

                let (x, y) = self.random_free_spot(&mut thread_rng());
                let _ = self.players
                            .insert(client.id, message::Player::not_moving(client.id, x, y));
                self.send_to_everybody(message::Message::PlayerSpawned {
                    id: client.id,
                    x: x,
                    y: y,
                });

                let _ = client.send(self.serialize().to_string());

                let _ = self.clients.insert(client.id, client);
            }
            WebSocketEvent::ClientClosed { client_id } => {
                let _ = self.players.remove(&client_id);
                let _ = self.clients.remove(&client_id);

                self.send_to_everybody(message::Message::PlayerLeft { id: client_id });
            }
            WebSocketEvent::ClientMessage { client_id, message } => {
                self.process_client_message(client_id, message);
            }
        }
    }

    /// Serialize the entire game state into one json string.
    fn serialize(&self) -> message::Message {
        let players: Vec<_> = self.players
                                  .values()
                                  .cloned()
                                  .collect();
        let bullets: Vec<_> = self.bullets
                                  .values()
                                  .cloned()
                                  .collect();
        message::Message::WorldState {
            player_count: players.len() as u32,
            alive_players: players,
            alive_bullets: bullets,
        }
    }

    /// Process a simple string message from the client.
    fn process_client_message(&mut self, client_id: u32, message: message::Message) {
        match message {
            message::Message::StartMoving { move_x, move_y } => {
                let resp = {
                    let player = self.players.get_mut(&client_id).unwrap();
                    player.move_x = Some(move_x);
                    player.move_y = Some(move_y);

                    message::Message::PlayerMoving {
                        id: player.id,
                        x: player.x,
                        y: player.y,
                        move_x: move_x,
                        move_y: move_y,
                    }
                };
                self.send_to_everybody(resp);
            }
            message::Message::StopMoving => {
                let resp = {
                    let player = self.players.get_mut(&client_id).unwrap();
                    player.move_x = None;
                    player.move_y = None;

                    message::Message::PlayerStopped {
                        id: player.id,
                        x: player.x,
                        y: player.y,
                    }
                };
                self.send_to_everybody(resp);
            }
            message::Message::Fire { move_x, move_y } => {
                let player = self.players.get(&client_id).unwrap();

                // Have to move the bullet out of the way of the player to avoid an instant collision.
                let start_x = player.x + move_x * (BULLET_RADIUS + PLAYER_RADIUS + 1.0);
                let start_y = player.y + move_y * (BULLET_RADIUS + PLAYER_RADIUS + 1.0);

                let _ = self.bullets.insert(self.next_bullet_id,
                                            message::Bullet::moving(self.next_bullet_id,
                                                                    start_x,
                                                                    start_y,
                                                                    move_x,
                                                                    move_y));

                let resp = message::Message::ShotsFired {
                    id: player.id,
                    bullet_id: self.next_bullet_id,
                    x: start_x,
                    y: start_y,
                    aim_x: move_x,
                    aim_y: move_y,
                };
                self.send_to_everybody(resp);

                self.next_bullet_id += 1;
            }
            _ => panic!("Unprocessed message! {}", message.to_string()),
        }
    }

    fn random_free_spot<R: Rng>(&self, rng: &mut R) -> (f32, f32) {
        static MAX_ITERATIONS: u32 = 100;

        let min_vial_x = PLAYER_RADIUS;
        let min_vial_y = PLAYER_RADIUS;
        let max_vial_x = MAP_WIDTH - PLAYER_RADIUS;
        let max_vial_y = MAP_HEIGHT - PLAYER_RADIUS;

        for _ in 1..MAX_ITERATIONS {
            let x: f32 = rng.gen_range(min_vial_x, max_vial_x);
            let y: f32 = rng.gen_range(min_vial_y, max_vial_y);

            let mut collides = false;

            for (_, player) in &self.players {
                if distance_between(x, y, player.x, player.y) < 2.0 * PLAYER_RADIUS {
                    collides = true;
                    break;
                }
            }

            for (_, bullet) in &self.bullets {
                if distance_between(x, y, bullet.x, bullet.y) < PLAYER_RADIUS + BULLET_RADIUS {
                    collides = true;
                    break;
                }
            }

            if !collides {
                return (x, y);
            }
        }
        println!("Failed to find a random empty spot for player after {} iterations",
                 MAX_ITERATIONS);

        (rng.gen_range(0.0, MAP_WIDTH), rng.gen_range(0.0, MAP_HEIGHT))
    }

    fn send_to_everybody(&self, what: message::Message) {
        let value = what.to_string();
        for (_, client) in &self.clients {
            // Always ignore if the send fails.
            // We will eventually get a disconnect WebSocketMessage where we will cleanly do the disconnect.
            let _ = client.send(value.clone());
        }
    }
}
<|endoftext|>


<commit_before>#![allow(non_snake_case)]
#![allow(dead_code)]
#![allow(unused_attributes)]

extern crate regex;
extern crate time;
extern crate rustc_serialize;

mod util;

use util::command;
use util::fs;
use std::env;
use std::path::Path;

fn main() {
  let worlds = vec![
    "VR",
    "utes",
    "Sara",
    "SaraLite",
    "Sara_dbe1",
    "Chernarus",
    "Chernarus_Summer",
    "Porto",
    "Intro",
    "IsolaDiCapraia",
    "Mountains_ACR",
    "Takistan",
    "Zargabad",
    "Woodland_ACR",
    "Bootcamp_ACR",
    "Desert_E",
    "ProvingGrounds_PMC",
    "Shapur_BAF",
    "fata",
    "Stratis",
    "anim_helvantis_v2",
    "Altis",
    "pja310"
  ];

  let mission_competition = "FP_ComfyCompetition";
  let ending = ".VR";
  let cwd: String = format!("{}\\", get_cwd());

  prepate_comfyDowntime(cwd.as_ref());

  // Mission Generation for all Maps
  for map in worlds {
    println!("Generating missions for Map: {}", map);
    compile_comfyDowntime(cwd.as_ref(), map);
  }
}

fn prepate_comfyDowntime(path: &str) {
  let mission_downtime = "FP_ComfyDowntime";
  let dirpath: String = format!("{}{}.VR", path, mission_downtime);
  let mission_str: &str = "COMFY_USE_B = false; COMFY_USE_O = false; COMFY_USE_I = false;";

  // Create Folders for ComfyDowntime Faction Variations
  command::run("robocopy", &[dirpath.as_ref(), format!("{}{}_Blufor", path, mission_downtime).as_ref(), "/E"]);
  command::run("robocopy", &[dirpath.as_ref(), format!("{}{}_Opfor", path, mission_downtime).as_ref(), "/E"]);
  command::run("robocopy", &[dirpath.as_ref(), format!("{}{}_Indfor", path, mission_downtime).as_ref(), "/E"]);

  // Edit mission.sqm files to adjust Spawns
  let mission_path: String = format!("{}.VR\\mission.sqm", mission_downtime);
  let mission_path_slice: &str = mission_path.as_ref();
  let mission_path_struct = Path::new(mission_path_slice);
  let mission_file: String = fs::read(&mission_path_struct);

  let mission_blufor: String = mission_file.replace(mission_str, "COMFY_USE_B = true; COMFY_USE_O = false; COMFY_USE_I = false;");
  let mut mission_opfor: String = mission_file.replace(mission_str, "COMFY_USE_B = false; COMFY_USE_O = true; COMFY_USE_I = false;");
  let mut mission_indfor: String = mission_file.replace(mission_str, "COMFY_USE_B = false; COMFY_USE_O = false; COMFY_USE_I = true;");

  // Now replace the Units in the mission to fit the Faction. We don't require to replace blufor units, since blufor classes are already default
  mission_opfor = mission_opfor.replace("b_soldier_unarmed_f", "o_soldier_unarmed_f");
  mission_indfor = mission_indfor.replace("b_soldier_unarmed_f", "i_soldier_unarmed_f");

  fs::write(format!("{}_Blufor.\\mission.sqm", mission_downtime).as_ref(), mission_blufor.as_ref());
  fs::write(format!("{}_Opfor.\\mission.sqm", mission_downtime).as_ref(), mission_opfor.as_ref());
  fs::write(format!("{}_Indfor.\\mission.sqm", mission_downtime).as_ref(), mission_indfor.as_ref());
}

fn compile_comfyDowntime(path: &str, map: &str) {
  let mission_downtime = "FP_ComfyDowntime";
  let dirpath_blufor: String = format!("{}FP_ComfyDowntime_Blufor", path);
  let newpath_blufor: String = format!("{}FP_ComfyDowntime_Blufor.{}.pbo", path, map);

  let dirpath_opfor: String = format!("{}FP_ComfyDowntime_Opfor", path);
  let newpath_opfor: String = format!("{}FP_ComfyDowntime_Opfor.{}.pbo", path, map);

  let dirpath_indfor: String = format!("{}FP_ComfyDowntime_Indfor", path);
  let newpath_indfor: String = format!("{}FP_ComfyDowntime_Indfor.{}.pbo", path, map);

  command::run("PBOConsole", &["-pack", dirpath_blufor.as_ref(), newpath_blufor.as_ref()]);
  command::run("PBOConsole", &["-pack", dirpath_opfor.as_ref(), newpath_opfor.as_ref()]);
  command::run("PBOConsole", &["-pack", dirpath_indfor.as_ref(), newpath_indfor.as_ref()]);
}

//
fn get_cwd() -> String {
  let pathbuf = env::current_dir().unwrap();
  let path: &str = pathbuf.to_str().unwrap();
  path.to_string()
}<commit_msg>Added ComfyCompetition<commit_after>#![allow(non_snake_case)]
#![allow(dead_code)]
#![allow(unused_attributes)]

extern crate regex;
extern crate time;
extern crate rustc_serialize;

mod util;

use util::command;
use util::fs;
use std::env;
use std::path::Path;

fn main() {
  let worlds = vec![
    "VR",
    "utes",
    "Sara",
    "SaraLite",
    "Sara_dbe1",
    "Chernarus",
    "Chernarus_Summer",
    "Porto",
    "Intro",
    "IsolaDiCapraia",
    "Mountains_ACR",
    "Takistan",
    "Zargabad",
    "Woodland_ACR",
    "Bootcamp_ACR",
    "Desert_E",
    "ProvingGrounds_PMC",
    "Shapur_BAF",
    "fata",
    "Stratis",
    "anim_helvantis_v2",
    "Altis",
    "pja310"
  ];

  let cwd: String = format!("{}\\", get_cwd());

  prepate_comfyDowntime(cwd.as_ref());

  // Mission Generation for all Maps
  for map in worlds {
    println!("Generating missions for Map: {}", map);
    compile_comfyDowntime(cwd.as_ref(), map);
    compile_comfyCompetition(cwd.as_ref(), map);
  }
}

fn prepate_comfyDowntime(path: &str) {
  let mission_downtime = "FP_ComfyDowntime";
  let dirpath: String = format!("{}{}.VR", path, mission_downtime);
  let mission_str: &str = "COMFY_USE_B = false; COMFY_USE_O = false; COMFY_USE_I = false;";

  // Create Folders for ComfyDowntime Faction Variations
  command::run("robocopy", &[dirpath.as_ref(), format!("{}{}_Blufor", path, mission_downtime).as_ref(), "/E"]);
  command::run("robocopy", &[dirpath.as_ref(), format!("{}{}_Opfor", path, mission_downtime).as_ref(), "/E"]);
  command::run("robocopy", &[dirpath.as_ref(), format!("{}{}_Indfor", path, mission_downtime).as_ref(), "/E"]);

  // Edit mission.sqm files to adjust Spawns
  let mission_path: String = format!("{}.VR\\mission.sqm", mission_downtime);
  let mission_path_slice: &str = mission_path.as_ref();
  let mission_path_struct = Path::new(mission_path_slice);
  let mission_file: String = fs::read(&mission_path_struct);

  let mission_blufor: String = mission_file.replace(mission_str, "COMFY_USE_B = true; COMFY_USE_O = false; COMFY_USE_I = false;");
  let mut mission_opfor: String = mission_file.replace(mission_str, "COMFY_USE_B = false; COMFY_USE_O = true; COMFY_USE_I = false;");
  let mut mission_indfor: String = mission_file.replace(mission_str, "COMFY_USE_B = false; COMFY_USE_O = false; COMFY_USE_I = true;");

  // Now replace the Units in the mission to fit the Faction. We don't require to replace blufor units, since blufor classes are already default
  mission_opfor = mission_opfor.replace("b_soldier_unarmed_f", "o_soldier_unarmed_f");
  mission_indfor = mission_indfor.replace("b_soldier_unarmed_f", "i_soldier_unarmed_f");

  fs::write(format!("{}_Blufor.\\mission.sqm", mission_downtime).as_ref(), mission_blufor.as_ref());
  fs::write(format!("{}_Opfor.\\mission.sqm", mission_downtime).as_ref(), mission_opfor.as_ref());
  fs::write(format!("{}_Indfor.\\mission.sqm", mission_downtime).as_ref(), mission_indfor.as_ref());
}

fn compile_comfyDowntime(path: &str, map: &str) {
  let dirpath_blufor: String = format!("{}FP_ComfyDowntime_Blufor", path);
  let newpath_blufor: String = format!("{}FP_ComfyDowntime_Blufor.{}.pbo", path, map);

  let dirpath_opfor: String = format!("{}FP_ComfyDowntime_Opfor", path);
  let newpath_opfor: String = format!("{}FP_ComfyDowntime_Opfor.{}.pbo", path, map);

  let dirpath_indfor: String = format!("{}FP_ComfyDowntime_Indfor", path);
  let newpath_indfor: String = format!("{}FP_ComfyDowntime_Indfor.{}.pbo", path, map);

  command::run("PBOConsole", &["-pack", dirpath_blufor.as_ref(), newpath_blufor.as_ref()]);
  command::run("PBOConsole", &["-pack", dirpath_opfor.as_ref(), newpath_opfor.as_ref()]);
  command::run("PBOConsole", &["-pack", dirpath_indfor.as_ref(), newpath_indfor.as_ref()]);
}

fn compile_comfyCompetition(path: &str, map: &str) {
  let dirpath: String = format!("{}FP_ComfyCompetition.VR", path);
  let newpath: String = format!("{}FP_ComfyCompetition.{}.pbo", path, map);
  command::run("PBOConsole", &["-pack", dirpath.as_ref(), newpath.as_ref()]);
}

//
fn get_cwd() -> String {
  let pathbuf = env::current_dir().unwrap();
  let path: &str = pathbuf.to_str().unwrap();
  path.to_string()
}<|endoftext|>


<commit_before>extern crate gradr_backend;

// TODO: there must be a better way to not import these for testing
#[cfg(not(test))]
use std::comm::sync_channel;
#[cfg(not(test))]
use std::io::timer;
#[cfg(not(test))]
use std::sync::Arc;
#[cfg(not(test))]
use std::time::Duration;

#[cfg(not(test))]
use gradr_backend::database::testing::TestDatabase;
#[cfg(not(test))]
use gradr_backend::notification_listener::NotificationSource;
#[cfg(not(test))]
use gradr_backend::notification_listener::testing::TestNotificationSource;
#[cfg(not(test))]
use gradr_backend::worker::worker_loop_step;

#[cfg(not(test))]
fn main() {
    let (notification_sender, notification_recv) = sync_channel(10);

    let db = Arc::new(TestDatabase::new());
    let c1 = db.clone();
    let c2 = db.clone();

    spawn(proc() {
        let source = TestNotificationSource::new(notification_recv);
        loop {
            source.notification_event_loop_step(&*c1);
        }
    });

    spawn(proc() {
        loop {
            worker_loop_step(&*c2);
        }
    });

    notification_sender.send("test/testing_parsing_nonempty_success".to_string());

    loop {
        timer::sleep(Duration::seconds(1));
        println!("{}", db.results.read().find_equiv(
            &"test/testing_parsing_nonempty_success".to_string()));
    }
}
<commit_msg>Removed testing code from main.<commit_after>extern crate gradr_backend;

#[cfg(not(test))]
fn main() {
    println!("Hello, world!");
}
<|endoftext|>