<commit_before>use super::{
    heap::ObjectPointer,
    use_provider::UseProvider,
    value::{Closure, Value},
    Vm,
};
use crate::{builtin_functions::BuiltinFunction, compiler::lir::Instruction, input::Input};
use itertools::Itertools;
use log;

macro_rules! destructure {
    ($args:expr, $enum:pat, $body:block) => {{
        if let $enum = &$args[..] {
            $body
        } else {
            Err(format!("a builtin function received invalid arguments"))
        }
    }};
}

impl Vm {
    pub(super) fn run_builtin_function<U: UseProvider>(
        &mut self,
        use_provider: &U,
        builtin_function: &BuiltinFunction,
        args: &[ObjectPointer],
    ) {
        log::trace!("run_builtin_function: builtin{builtin_function:?}");

        let args = args.iter().map(|it| self.heap.export(*it)).collect_vec();

        let return_value_or_panic_message = match &builtin_function {
            BuiltinFunction::Add => self.add(args),
            BuiltinFunction::Equals => self.equals(args),
            BuiltinFunction::GetArgumentCount => self.get_argument_count(args),
            BuiltinFunction::IfElse => match self.if_else(use_provider, args) {
                // If successful, IfElse doesn't return a value, but diverges
                // the control flow.
                Ok(()) => return,
                Err(message) => Err(message),
            },
            BuiltinFunction::Panic => self.panic_builtin(args).map(|_| panic!()),
            BuiltinFunction::Print => self.print(args),
            BuiltinFunction::StructGet => self.struct_get(args),
            BuiltinFunction::StructGetKeys => self.struct_get_keys(args),
            BuiltinFunction::StructHasKey => self.struct_has_key(args),
            BuiltinFunction::TypeOf => self.type_of(args),
            BuiltinFunction::UseAsset => self.use_asset(use_provider, args),
            BuiltinFunction::UseLocalModule => {
                // If successful, UseLocalModule doesn't return a value, but
                // diverges the control flow.
                match self.use_local_module(use_provider, args) {
                    Ok(()) => return,
                    Err(message) => Err(message),
                }
            }
        };
        let return_value = match return_value_or_panic_message {
            Ok(value) => value,
            Err(panic_message) => self.panic(panic_message),
        };

        let return_object = self.heap.import(return_value);
        self.data_stack.push(return_object);
    }

    fn add(&mut self, args: Vec<Value>) -> Result<Value, String> {
        destructure!(args, [Value::Int(a), Value::Int(b)], { Ok((a + b).into()) })
    }

    fn equals(&mut self, args: Vec<Value>) -> Result<Value, String> {
        destructure!(args, [a, b], { Ok((a == b).into()) })
    }

    fn get_argument_count(&mut self, args: Vec<Value>) -> Result<Value, String> {
        destructure!(args, [Value::Closure(Closure { num_args, .. })], {
            Ok((*num_args as u64).into())
        })
    }

    fn if_else<U: UseProvider>(
        &mut self,
        use_provider: &U,
        args: Vec<Value>,
    ) -> Result<(), String> {
        destructure!(
            args,
            [
                Value::Symbol(condition),
                Value::Closure(then_closure),
                Value::Closure(else_closure)
            ],
            {
                if then_closure.num_args > 0 {
                    return Err(format!("IfElse expects a closure without arguments as the then, got one with {} arguments.", then_closure.num_args));
                }
                if else_closure.num_args > 0 {
                    return Err(format!("IfElse expects a closure without arguments as the else, got one with {} arguments.", else_closure.num_args));
                }
                let condition = match condition.as_str() {
                    "True" => true,
                    "False" => false,
                    _ => {
                        return Err(format!(
                            "IfElse expected True or False as a condition, but got {condition}.",
                        ));
                    }
                };

                let closure_object = self.heap.import(if condition {
                    Value::Closure(then_closure.clone())
                } else {
                    Value::Closure(else_closure.clone())
                });
                log::debug!(
                    "IfElse executing the closure: {:?}",
                    self.heap.export_without_dropping(closure_object)
                );
                self.data_stack.push(closure_object);
                self.run_instruction(use_provider, Instruction::Call { num_args: 0 });
                Ok(())
            }
        )
    }

    fn panic_builtin(&mut self, args: Vec<Value>) -> Result<!, String> {
        destructure!(args, [Value::Text(message)], { Err(message.to_string()) })
    }

    fn print(&mut self, args: Vec<Value>) -> Result<Value, String> {
        destructure!(args, [Value::Text(message)], {
            println!("{message:?}");
            Ok(Value::nothing())
        })
    }

    fn struct_get(&mut self, args: Vec<Value>) -> Result<Value, String> {
        destructure!(args, [Value::Struct(struct_), key], {
            match struct_.get(&key) {
                Some(value) => Ok(value.clone().into()),
                None => Err(format!("Struct does not contain key {key:?}.")),
            }
        })
    }

    fn struct_get_keys(&mut self, args: Vec<Value>) -> Result<Value, String> {
        destructure!(args, [Value::Struct(struct_)], {
            Ok(Value::list(struct_.keys().cloned().collect()))
        })
    }

    fn struct_has_key(&mut self, args: Vec<Value>) -> Result<Value, String> {
        destructure!(args, [Value::Struct(struct_), key], {
            Ok((struct_.contains_key(key)).into())
        })
    }

    fn type_of(&mut self, args: Vec<Value>) -> Result<Value, String> {
        destructure!(args, [value], {
            Ok(Value::Symbol(
                match &value {
                    Value::Int(_) => "Int",
                    Value::Text(_) => "Text",
                    Value::Symbol(_) => "Symbol",
                    Value::Struct(_) => "Struct",
                    Value::Closure { .. } => "Function",
                    Value::Builtin { .. } => "Builtin",
                }
                .to_owned(),
            ))
        })
    }

    fn use_asset<U: UseProvider>(
        &mut self,
        use_provider: &U,
        args: Vec<Value>,
    ) -> Result<Value, String> {
        let (current_path, target) = Self::parse_current_path_and_target(args)?;
        let target = UseAssetTarget::parse(&target)?;
        let input = target.resolve_asset(&current_path)?;
        let content = use_provider.use_asset(input)?;
        Ok(Value::list(
            content
                .iter()
                .map(|byte| Value::Int(*byte as u64))
                .collect_vec(),
        ))
    }

    fn use_local_module<U: UseProvider>(
        &mut self,
        use_provider: &U,
        args: Vec<Value>,
    ) -> Result<(), String> {
        let (current_path, target) = Self::parse_current_path_and_target(args)?;
        let target = UseAssetTarget::parse(&target)?;
        let possible_inputs = target.resolve_local_module(&current_path)?;
        let (input, lir) = 'find_existing_input: {
            for input in possible_inputs {
                if let Some(lir) = use_provider.use_local_module(input.clone()) {
                    break 'find_existing_input (input, lir);
                }
            }
            return Err("couldn't import module".to_string());
        };

        let module_closure = Value::Closure(Closure::of_lir(input.clone(), lir));
        let address = self.heap.import(module_closure);
        self.data_stack.push(address);
        self.run_instruction(use_provider, Instruction::Call { num_args: 0 });
        Ok(())
    }

    fn parse_current_path_and_target(args: Vec<Value>) -> Result<(Vec<String>, String), String> {
        destructure!(
            args,
            [Value::Struct(current_path_struct), Value::Text(target)],
            {
                // `current_path_struct` is set by us and not users, hence we don't have to validate it that strictly.
                let mut current_path = vec![];
                let mut index = 0;
                while let Some(component) = current_path_struct.get(&Value::Int(index)) {
                    current_path.push(component.clone().try_into_text().unwrap());
                    index += 1;
                }
                Ok((current_path, target.to_string()))
            }
        )
    }
}

struct UseAssetTarget {
    parent_navigations: usize,
    path: String,
}
impl UseAssetTarget {
    const PARENT_NAVIGATION_CHAR: char = '.';

    fn parse(mut target: &str) -> Result<Self, String> {
        let parent_navigations = {
            let mut navigations = 0;
            while target.chars().next() == Some(UseAssetTarget::PARENT_NAVIGATION_CHAR) {
                navigations += 1;
                target = &target[UseAssetTarget::PARENT_NAVIGATION_CHAR.len_utf8()..];
            }
            match navigations {
                0 => return Err("the target must start with at least one dot".to_string()),
                i => i - 1, // two dots means one parent navigation
            }
        };
        let path = {
            if !target
                .chars()
                .all(|c| c.is_ascii_alphanumeric() || c == '.')
            {
                return Err("the target name can only contain letters".to_string());
            }
            target.to_string()
        };
        Ok(UseAssetTarget {
            parent_navigations,
            path,
        })
    }

    fn resolve_asset(&self, current_path: &[String]) -> Result<Input, String> {
        let mut path = current_path.to_owned();
        for _ in 0..self.parent_navigations {
            if path.pop() == None {
                return Err("too many parent navigations".to_string());
            }
        }
        if path
            .last()
            .map(|it| it.ends_with(".candy"))
            .unwrap_or(false)
        {
            return Err(
                "importing child files (starting with a single dot) only works from `.candy` files"
                    .to_string(),
            );
        }
        path.push(self.path.to_string());
        Ok(Input::File(path.clone()))
    }

    fn resolve_local_module(&self, current_path: &[String]) -> Result<Vec<Input>, String> {
        if self.path.contains('.') {
            return Err("the target name contains a file ending".to_string());
        }

        let mut path = current_path.to_owned();
        for _ in 0..self.parent_navigations {
            if path.pop() == None {
                return Err("too many parent navigations".to_string());
            }
        }
        let possible_paths = vec![
            path.clone()
                .into_iter()
                .chain([format!("{}.candy", self.path)])
                .collect_vec(),
            path.clone()
                .into_iter()
                .chain([self.path.to_string(), ".candy".to_string()])
                .collect_vec(),
        ];
        Ok(possible_paths
            .into_iter()
            .map(|path| Input::File(path))
            .collect_vec())
    }
}
<commit_msg>Make builtinPrint not actually print, but log::info instead<commit_after>use super::{
    heap::ObjectPointer,
    use_provider::UseProvider,
    value::{Closure, Value},
    Vm,
};
use crate::{builtin_functions::BuiltinFunction, compiler::lir::Instruction, input::Input};
use itertools::Itertools;
use log;

macro_rules! destructure {
    ($args:expr, $enum:pat, $body:block) => {{
        if let $enum = &$args[..] {
            $body
        } else {
            Err(format!("a builtin function received invalid arguments"))
        }
    }};
}

impl Vm {
    pub(super) fn run_builtin_function<U: UseProvider>(
        &mut self,
        use_provider: &U,
        builtin_function: &BuiltinFunction,
        args: &[ObjectPointer],
    ) {
        log::trace!("run_builtin_function: builtin{builtin_function:?}");

        let args = args.iter().map(|it| self.heap.export(*it)).collect_vec();

        let return_value_or_panic_message = match &builtin_function {
            BuiltinFunction::Add => self.add(args),
            BuiltinFunction::Equals => self.equals(args),
            BuiltinFunction::GetArgumentCount => self.get_argument_count(args),
            BuiltinFunction::IfElse => match self.if_else(use_provider, args) {
                // If successful, IfElse doesn't return a value, but diverges
                // the control flow.
                Ok(()) => return,
                Err(message) => Err(message),
            },
            BuiltinFunction::Panic => self.panic_builtin(args).map(|_| panic!()),
            BuiltinFunction::Print => self.print(args),
            BuiltinFunction::StructGet => self.struct_get(args),
            BuiltinFunction::StructGetKeys => self.struct_get_keys(args),
            BuiltinFunction::StructHasKey => self.struct_has_key(args),
            BuiltinFunction::TypeOf => self.type_of(args),
            BuiltinFunction::UseAsset => self.use_asset(use_provider, args),
            BuiltinFunction::UseLocalModule => {
                // If successful, UseLocalModule doesn't return a value, but
                // diverges the control flow.
                match self.use_local_module(use_provider, args) {
                    Ok(()) => return,
                    Err(message) => Err(message),
                }
            }
        };
        let return_value = match return_value_or_panic_message {
            Ok(value) => value,
            Err(panic_message) => self.panic(panic_message),
        };

        let return_object = self.heap.import(return_value);
        self.data_stack.push(return_object);
    }

    fn add(&mut self, args: Vec<Value>) -> Result<Value, String> {
        destructure!(args, [Value::Int(a), Value::Int(b)], { Ok((a + b).into()) })
    }

    fn equals(&mut self, args: Vec<Value>) -> Result<Value, String> {
        destructure!(args, [a, b], { Ok((a == b).into()) })
    }

    fn get_argument_count(&mut self, args: Vec<Value>) -> Result<Value, String> {
        destructure!(args, [Value::Closure(Closure { num_args, .. })], {
            Ok((*num_args as u64).into())
        })
    }

    fn if_else<U: UseProvider>(
        &mut self,
        use_provider: &U,
        args: Vec<Value>,
    ) -> Result<(), String> {
        destructure!(
            args,
            [
                Value::Symbol(condition),
                Value::Closure(then_closure),
                Value::Closure(else_closure)
            ],
            {
                if then_closure.num_args > 0 {
                    return Err(format!("IfElse expects a closure without arguments as the then, got one with {} arguments.", then_closure.num_args));
                }
                if else_closure.num_args > 0 {
                    return Err(format!("IfElse expects a closure without arguments as the else, got one with {} arguments.", else_closure.num_args));
                }
                let condition = match condition.as_str() {
                    "True" => true,
                    "False" => false,
                    _ => {
                        return Err(format!(
                            "IfElse expected True or False as a condition, but got {condition}.",
                        ));
                    }
                };

                let closure_object = self.heap.import(if condition {
                    Value::Closure(then_closure.clone())
                } else {
                    Value::Closure(else_closure.clone())
                });
                log::debug!(
                    "IfElse executing the closure: {:?}",
                    self.heap.export_without_dropping(closure_object)
                );
                self.data_stack.push(closure_object);
                self.run_instruction(use_provider, Instruction::Call { num_args: 0 });
                Ok(())
            }
        )
    }

    fn panic_builtin(&mut self, args: Vec<Value>) -> Result<!, String> {
        destructure!(args, [Value::Text(message)], { Err(message.to_string()) })
    }

    fn print(&mut self, args: Vec<Value>) -> Result<Value, String> {
        destructure!(args, [Value::Text(message)], {
            log::info!("{message:?}");
            Ok(Value::nothing())
        })
    }

    fn struct_get(&mut self, args: Vec<Value>) -> Result<Value, String> {
        destructure!(args, [Value::Struct(struct_), key], {
            match struct_.get(&key) {
                Some(value) => Ok(value.clone().into()),
                None => Err(format!("Struct does not contain key {key:?}.")),
            }
        })
    }

    fn struct_get_keys(&mut self, args: Vec<Value>) -> Result<Value, String> {
        destructure!(args, [Value::Struct(struct_)], {
            Ok(Value::list(struct_.keys().cloned().collect()))
        })
    }

    fn struct_has_key(&mut self, args: Vec<Value>) -> Result<Value, String> {
        destructure!(args, [Value::Struct(struct_), key], {
            Ok((struct_.contains_key(key)).into())
        })
    }

    fn type_of(&mut self, args: Vec<Value>) -> Result<Value, String> {
        destructure!(args, [value], {
            Ok(Value::Symbol(
                match &value {
                    Value::Int(_) => "Int",
                    Value::Text(_) => "Text",
                    Value::Symbol(_) => "Symbol",
                    Value::Struct(_) => "Struct",
                    Value::Closure { .. } => "Function",
                    Value::Builtin { .. } => "Builtin",
                }
                .to_owned(),
            ))
        })
    }

    fn use_asset<U: UseProvider>(
        &mut self,
        use_provider: &U,
        args: Vec<Value>,
    ) -> Result<Value, String> {
        let (current_path, target) = Self::parse_current_path_and_target(args)?;
        let target = UseAssetTarget::parse(&target)?;
        let input = target.resolve_asset(&current_path)?;
        let content = use_provider.use_asset(input)?;
        Ok(Value::list(
            content
                .iter()
                .map(|byte| Value::Int(*byte as u64))
                .collect_vec(),
        ))
    }

    fn use_local_module<U: UseProvider>(
        &mut self,
        use_provider: &U,
        args: Vec<Value>,
    ) -> Result<(), String> {
        let (current_path, target) = Self::parse_current_path_and_target(args)?;
        let target = UseAssetTarget::parse(&target)?;
        let possible_inputs = target.resolve_local_module(&current_path)?;
        let (input, lir) = 'find_existing_input: {
            for input in possible_inputs {
                if let Some(lir) = use_provider.use_local_module(input.clone()) {
                    break 'find_existing_input (input, lir);
                }
            }
            return Err("couldn't import module".to_string());
        };

        let module_closure = Value::Closure(Closure::of_lir(input.clone(), lir));
        let address = self.heap.import(module_closure);
        self.data_stack.push(address);
        self.run_instruction(use_provider, Instruction::Call { num_args: 0 });
        Ok(())
    }

    fn parse_current_path_and_target(args: Vec<Value>) -> Result<(Vec<String>, String), String> {
        destructure!(
            args,
            [Value::Struct(current_path_struct), Value::Text(target)],
            {
                // `current_path_struct` is set by us and not users, hence we don't have to validate it that strictly.
                let mut current_path = vec![];
                let mut index = 0;
                while let Some(component) = current_path_struct.get(&Value::Int(index)) {
                    current_path.push(component.clone().try_into_text().unwrap());
                    index += 1;
                }
                Ok((current_path, target.to_string()))
            }
        )
    }
}

struct UseAssetTarget {
    parent_navigations: usize,
    path: String,
}
impl UseAssetTarget {
    const PARENT_NAVIGATION_CHAR: char = '.';

    fn parse(mut target: &str) -> Result<Self, String> {
        let parent_navigations = {
            let mut navigations = 0;
            while target.chars().next() == Some(UseAssetTarget::PARENT_NAVIGATION_CHAR) {
                navigations += 1;
                target = &target[UseAssetTarget::PARENT_NAVIGATION_CHAR.len_utf8()..];
            }
            match navigations {
                0 => return Err("the target must start with at least one dot".to_string()),
                i => i - 1, // two dots means one parent navigation
            }
        };
        let path = {
            if !target
                .chars()
                .all(|c| c.is_ascii_alphanumeric() || c == '.')
            {
                return Err("the target name can only contain letters".to_string());
            }
            target.to_string()
        };
        Ok(UseAssetTarget {
            parent_navigations,
            path,
        })
    }

    fn resolve_asset(&self, current_path: &[String]) -> Result<Input, String> {
        let mut path = current_path.to_owned();
        for _ in 0..self.parent_navigations {
            if path.pop() == None {
                return Err("too many parent navigations".to_string());
            }
        }
        if path
            .last()
            .map(|it| it.ends_with(".candy"))
            .unwrap_or(false)
        {
            return Err(
                "importing child files (starting with a single dot) only works from `.candy` files"
                    .to_string(),
            );
        }
        path.push(self.path.to_string());
        Ok(Input::File(path.clone()))
    }

    fn resolve_local_module(&self, current_path: &[String]) -> Result<Vec<Input>, String> {
        if self.path.contains('.') {
            return Err("the target name contains a file ending".to_string());
        }

        let mut path = current_path.to_owned();
        for _ in 0..self.parent_navigations {
            if path.pop() == None {
                return Err("too many parent navigations".to_string());
            }
        }
        let possible_paths = vec![
            path.clone()
                .into_iter()
                .chain([format!("{}.candy", self.path)])
                .collect_vec(),
            path.clone()
                .into_iter()
                .chain([self.path.to_string(), ".candy".to_string()])
                .collect_vec(),
        ];
        Ok(possible_paths
            .into_iter()
            .map(|path| Input::File(path))
            .collect_vec())
    }
}
<|endoftext|>


<commit_before>use gl;
use std::default::Default;
use std::os::raw::c_void;
use std::sync::mpsc;
use std::thread;

pub use glfw::{self, Action, Context, CursorMode, Key, MouseButton, Window};

#[derive(Clone, Copy, Debug)]
pub enum LuminanceBackend {
  GL33
}

impl Default for LuminanceBackend {
  fn default() -> Self {
    LuminanceBackend::GL33
  }
}

#[derive(Clone, Copy, Debug)]
pub enum WindowDim {
  Windowed(u32, u32),
  FullScreen,
  FullScreenRestricted(u32, u32)
}

pub type Keyboard = mpsc::Receiver<(Key, Action)>;
pub type Mouse = mpsc::Receiver<(MouseButton, Action)>;
pub type MouseMove = mpsc::Receiver<[f64; 2]>;
pub type Scroll = mpsc::Receiver<[f64; 2]>;

pub fn bootstrap<App: FnOnce(u32, u32, Keyboard, Mouse, MouseMove, Scroll, Window)>(dim: WindowDim, title: &'static str, backend: LuminanceBackend, app: App) {
  let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();

  match backend {
    LuminanceBackend::GL33 => {
      // OpenGL hints; implements luminance-gl’s core contexts creation
      glfw.window_hint(glfw::WindowHint::OpenGlProfile(glfw::OpenGlProfileHint::Core));
      glfw.window_hint(glfw::WindowHint::ContextVersionMajor(3));
      glfw.window_hint(glfw::WindowHint::ContextVersionMinor(3));
    }
  }

  // open a window in windowed or fullscreen mode
  let (mut window, events, w, h) = match dim {
    WindowDim::Windowed(w, h) => {
      let (window, events) = glfw.create_window(w, h, title, glfw::WindowMode::Windowed).expect("Failed to create GLFW window.");
      (window, events, w, h)
    },
    WindowDim::FullScreen => {
      glfw.with_primary_monitor(|glfw, monitor| {
        let monitor = monitor.unwrap();
        let vmode = monitor.get_video_mode().expect("primary monitor’s video mode");
        let (w, h) = (vmode.width, vmode.height);

        let (window, events) = glfw.create_window(w, h, title, glfw::WindowMode::FullScreen(monitor)).expect("Failed to create GLFW window.");
        (window, events, w, h)
      })
    },
    WindowDim::FullScreenRestricted(w, h) => {
      glfw.with_primary_monitor(|glfw, monitor| {
        let monitor = monitor.unwrap();

        let (window, events) = glfw.create_window(w, h, title, glfw::WindowMode::FullScreen(monitor)).expect("Failed to create GLFW window.");
        (window, events, w, h)
      })
    }
  };

  window.make_current();

  if cfg!(feature = "release") {
    window.set_cursor_mode(CursorMode::Disabled);
  }

  window.set_key_polling(true);
  window.set_cursor_pos_polling(true);
  window.set_mouse_button_polling(true);
  window.set_scroll_polling(true);

  // init OpenGL
  gl::load_with(|s| window.get_proc_address(s) as *const c_void);

  info!("using resolution {}x{}", w, h);

  // create channels to stream keyboard and mouse events
  let (kbd_snd, kbd_rcv) = mpsc::channel();
  let (mouse_snd, mouse_rcv) = mpsc::channel();
  let (mouse_move_snd, mouse_move_rcv) = mpsc::channel();
  let (scroll_snd, scroll_rcv) = mpsc::channel();

  // start the event threads
  let _ = thread::spawn(move || {
    loop {
      glfw.wait_events();

      for (_, event) in glfw::flush_messages(&events) {
        match event {
            glfw::WindowEvent::Key(key, _, action, _) => {
              let _ = kbd_snd.send((key, action));
            },
            glfw::WindowEvent::MouseButton(button, action, _) => {
              let _ = mouse_snd.send((button, action));
            },
            glfw::WindowEvent::CursorPos(x, y) => {
              let _ = mouse_move_snd.send([x, y]);
            },
            glfw::WindowEvent::Scroll(x, y) => {
              let _ = scroll_snd.send([x, y]);
            },
            _ => {},
        }
      }
    }
  });

  app(w, h, kbd_rcv, mouse_rcv, mouse_move_rcv, scroll_rcv, window);
}
<commit_msg>Added more output message in bootstrapping.<commit_after>use gl;
use std::default::Default;
use std::os::raw::c_void;
use std::sync::mpsc;
use std::thread;

pub use glfw::{self, Action, Context, CursorMode, Key, MouseButton, Window};

#[derive(Clone, Copy, Debug)]
pub enum LuminanceBackend {
  GL33
}

impl Default for LuminanceBackend {
  fn default() -> Self {
    LuminanceBackend::GL33
  }
}

#[derive(Clone, Copy, Debug)]
pub enum WindowDim {
  Windowed(u32, u32),
  FullScreen,
  FullScreenRestricted(u32, u32)
}

pub type Keyboard = mpsc::Receiver<(Key, Action)>;
pub type Mouse = mpsc::Receiver<(MouseButton, Action)>;
pub type MouseMove = mpsc::Receiver<[f64; 2]>;
pub type Scroll = mpsc::Receiver<[f64; 2]>;

pub fn bootstrap<App: FnOnce(u32, u32, Keyboard, Mouse, MouseMove, Scroll, Window)>(dim: WindowDim, title: &'static str, backend: LuminanceBackend, app: App) {
  info!("{} starting\nwindow mode: {:?}\nluminance backend: {:?}", title, dim, backend);

  let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();

  match backend {
    LuminanceBackend::GL33 => {
      // OpenGL hints; implements luminance-gl’s core contexts creation
      glfw.window_hint(glfw::WindowHint::OpenGlProfile(glfw::OpenGlProfileHint::Core));
      glfw.window_hint(glfw::WindowHint::ContextVersionMajor(3));
      glfw.window_hint(glfw::WindowHint::ContextVersionMinor(3));
    }
  }

  // open a window in windowed or fullscreen mode
  let (mut window, events, w, h) = match dim {
    WindowDim::Windowed(w, h) => {
      let (window, events) = glfw.create_window(w, h, title, glfw::WindowMode::Windowed).expect("Failed to create GLFW window.");
      (window, events, w, h)
    },
    WindowDim::FullScreen => {
      glfw.with_primary_monitor(|glfw, monitor| {
        let monitor = monitor.unwrap();
        let vmode = monitor.get_video_mode().expect("primary monitor’s video mode");
        let (w, h) = (vmode.width, vmode.height);

        let (window, events) = glfw.create_window(w, h, title, glfw::WindowMode::FullScreen(monitor)).expect("Failed to create GLFW window.");
        (window, events, w, h)
      })
    },
    WindowDim::FullScreenRestricted(w, h) => {
      glfw.with_primary_monitor(|glfw, monitor| {
        let monitor = monitor.unwrap();

        let (window, events) = glfw.create_window(w, h, title, glfw::WindowMode::FullScreen(monitor)).expect("Failed to create GLFW window.");
        (window, events, w, h)
      })
    }
  };

  deb!("window opened");

  window.make_current();

  if cfg!(feature = "release") {
    deb!("hiding cursor");
    window.set_cursor_mode(CursorMode::Disabled);
  }

  window.set_key_polling(true);
  window.set_cursor_pos_polling(true);
  window.set_mouse_button_polling(true);
  window.set_scroll_polling(true);

  deb!("initializing OpenGL pointers");

  // init OpenGL
  gl::load_with(|s| window.get_proc_address(s) as *const c_void);

  // create channels to stream keyboard and mouse events
  let (kbd_snd, kbd_rcv) = mpsc::channel();
  let (mouse_snd, mouse_rcv) = mpsc::channel();
  let (mouse_move_snd, mouse_move_rcv) = mpsc::channel();
  let (scroll_snd, scroll_rcv) = mpsc::channel();

  deb!("spawing the event thread");
  let _ = thread::spawn(move || {
    loop {
      glfw.wait_events();

      for (_, event) in glfw::flush_messages(&events) {
        match event {
            glfw::WindowEvent::Key(key, _, action, _) => {
              let _ = kbd_snd.send((key, action));
            },
            glfw::WindowEvent::MouseButton(button, action, _) => {
              let _ = mouse_snd.send((button, action));
            },
            glfw::WindowEvent::CursorPos(x, y) => {
              let _ = mouse_move_snd.send([x, y]);
            },
            glfw::WindowEvent::Scroll(x, y) => {
              let _ = scroll_snd.send([x, y]);
            },
            _ => {},
        }
      }
    }
  });

  deb!("bootstrapping finished");
  app(w, h, kbd_rcv, mouse_rcv, mouse_move_rcv, scroll_rcv, window);
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::path::Path;
use super::{Result,time_adjusted,calculate_time_difference};
use error::ProbeError;

pub type DiskStats = HashMap<String, DiskStat>;

#[derive(Debug,PartialEq)]
pub struct DiskStatsMeasurement {
    pub precise_time_ns: u64,
    pub stats: DiskStats
}

impl DiskStatsMeasurement {
    /// Calculate the disk stats per minute based on this measurement and a measurement in the
    /// future. It is advisable to make the next measurement roughly a minute from this one for the
    /// most reliable result.
    pub fn calculate_per_minute(&self, next_measurement: &DiskStatsMeasurement) -> Result<DiskStatsPerMinute> {
        let time_difference = try!(calculate_time_difference(self.precise_time_ns, next_measurement.precise_time_ns));

        let mut stats = HashMap::new();

        for (name, stat) in self.stats.iter() {
            let next_stat = match next_measurement.stats.get(name) {
                Some(stat) => stat,
                None => return Err(ProbeError::UnexpectedContent(format!("{} is not present in the next measurement", name)))
            };
            stats.insert(
                name.to_owned(),
                DiskStat {
                    reads_completed_successfully: try!(time_adjusted(next_stat.reads_completed_successfully, stat.reads_completed_successfully, time_difference)),
                    reads_merged: try!(time_adjusted(next_stat.reads_merged, stat.reads_merged, time_difference)),
                    sectors_read: try!(time_adjusted(next_stat.sectors_read, stat.sectors_read, time_difference)),
                    time_spent_reading_ms: try!(time_adjusted(next_stat.time_spent_reading_ms, stat.time_spent_reading_ms, time_difference)),
                    writes_completed: try!(time_adjusted(next_stat.writes_completed, stat.writes_completed, time_difference)),
                    writes_merged: try!(time_adjusted(next_stat.writes_merged, stat.writes_merged, time_difference)),
                    sectors_written: try!(time_adjusted(next_stat.sectors_written, stat.sectors_written, time_difference)),
                    time_spent_writing_ms: try!(time_adjusted(next_stat.time_spent_writing_ms, stat.time_spent_writing_ms, time_difference)),
                    ios_currently_in_progress: try!(time_adjusted(next_stat.ios_currently_in_progress, stat.ios_currently_in_progress, time_difference)),
                    time_spent_doing_ios_ms: try!(time_adjusted(next_stat.time_spent_doing_ios_ms, stat.time_spent_doing_ios_ms, time_difference)),
                    weighted_time_spent_doing_ios_ms: try!(time_adjusted(next_stat.weighted_time_spent_doing_ios_ms, stat.weighted_time_spent_doing_ios_ms, time_difference))
                }
            );
        }

        Ok(DiskStatsPerMinute {
            stats: stats
        })
    }
}

#[derive(Debug,PartialEq)]
pub struct DiskStat {
    pub reads_completed_successfully: u64,
    pub reads_merged: u64,
    pub sectors_read: u64,
    pub time_spent_reading_ms: u64,
    pub writes_completed: u64,
    pub writes_merged: u64,
    pub sectors_written: u64,
    pub time_spent_writing_ms: u64,
    pub ios_currently_in_progress: u64,
    pub time_spent_doing_ios_ms: u64,
    pub weighted_time_spent_doing_ios_ms: u64
}

#[derive(Debug,PartialEq)]
pub struct DiskStatsPerMinute {
   pub stats: DiskStats
}

#[cfg(target_os = "linux")]
pub fn read() -> Result<DiskStatsMeasurement> {
    os::read_and_parse_proc_diskstats(&Path::new("/proc/diskstats"))
}

#[cfg(target_os = "linux")]
mod os {
    use std::io::BufRead;
    use std::path::Path;
    use std::collections::HashMap;
    use time;
    use super::{DiskStatsMeasurement,DiskStat};
    use super::super::{file_to_buf_reader,parse_u64,Result,ProbeError};

    #[inline]
    pub fn read_and_parse_proc_diskstats(path: &Path) -> Result<DiskStatsMeasurement> {
        let reader = try!(file_to_buf_reader(path));

        let mut out = DiskStatsMeasurement {
            precise_time_ns: time::precise_time_ns(),
            stats: HashMap::new()
        };

        for line_result in reader.lines() {
            let line = try!(line_result);
            let segments: Vec<&str> = line.split_whitespace().collect();

            if segments.len() != 14 {
                return Err(ProbeError::UnexpectedContent("Incorrect number of segments".to_owned()))
            }

            let disk_stat = DiskStat {
                reads_completed_successfully: try!(parse_u64(segments[3])),
                reads_merged: try!(parse_u64(segments[4])),
                sectors_read: try!(parse_u64(segments[5])),
                time_spent_reading_ms: try!(parse_u64(segments[6])),
                writes_completed: try!(parse_u64(segments[7])),
                writes_merged: try!(parse_u64(segments[8])),
                sectors_written: try!(parse_u64(segments[9])),
                time_spent_writing_ms: try!(parse_u64(segments[10])),
                ios_currently_in_progress: try!(parse_u64(segments[11])),
                time_spent_doing_ios_ms: try!(parse_u64(segments[12])),
                weighted_time_spent_doing_ios_ms: try!(parse_u64(segments[13]))
            };
            out.stats.insert(segments[2].to_owned(), disk_stat);
        }

        Ok(out)
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use std::path::Path;
    use error::ProbeError;
    use super::DiskStatsMeasurement;
    use super::os::read_and_parse_proc_diskstats;

    #[test]
    fn test_read_disk_stats() {
        assert!(super::read().is_ok());
    }

    #[test]
    fn test_read_and_parse_proc_diskstats() {
        let measurement = read_and_parse_proc_diskstats(&Path::new("fixtures/linux/disk_stats/proc_diskstats")).unwrap();

        assert!(measurement.precise_time_ns > 0);

        assert_eq!(2, measurement.stats.len());

        let sda = measurement.stats.get("sda").unwrap();
        assert_eq!(6185, sda.reads_completed_successfully);
        assert_eq!(9367, sda.reads_merged);
        assert_eq!(403272, sda.sectors_read);
        assert_eq!(22160, sda.time_spent_reading_ms);
        assert_eq!(2591, sda.writes_completed);
        assert_eq!(8251, sda.writes_merged);
        assert_eq!(84452, sda.sectors_written);
        assert_eq!(2860, sda.time_spent_writing_ms);
        assert_eq!(0, sda.ios_currently_in_progress);
        assert_eq!(8960, sda.time_spent_doing_ios_ms);
        assert_eq!(24990, sda.weighted_time_spent_doing_ios_ms);

        let sda1 = measurement.stats.get("sda1").unwrap();
        assert_eq!(483, sda1.reads_completed_successfully);
        assert_eq!(4782, sda1.reads_merged);
        assert_eq!(41466, sda1.sectors_read);
        assert_eq!(1100, sda1.time_spent_reading_ms);
        assert_eq!(7, sda1.writes_completed);
        assert_eq!(1, sda1.writes_merged);
        assert_eq!(28, sda1.sectors_written);
        assert_eq!(40, sda1.time_spent_writing_ms);
        assert_eq!(0, sda1.ios_currently_in_progress);
        assert_eq!(930, sda1.time_spent_doing_ios_ms);
        assert_eq!(1140, sda1.weighted_time_spent_doing_ios_ms);
    }

    #[test]
    fn test_read_and_parse_proc_diskstats_incomplete() {
        match read_and_parse_proc_diskstats(&Path::new("fixtures/linux/disk_stats/proc_diskstats_incomplete")) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_read_and_parse_proc_diskstats_garbage() {
        match read_and_parse_proc_diskstats(&Path::new("fixtures/linux/disk_stats/proc_diskstats_garbage")) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_calculate_per_minute_full_minute() {
        let mut stats1 = HashMap::new();
        stats1.insert("sda1".to_owned(), helpers::disk_stat(0));
        let measurement1 = DiskStatsMeasurement {
            precise_time_ns: 60_000_000,
            stats: stats1
        };
        let mut stats2 = HashMap::new();
        stats2.insert("sda1".to_owned(), helpers::disk_stat(120));
        let measurement2 = DiskStatsMeasurement {
            precise_time_ns: 120_000_000,
            stats: stats2
        };

        let per_minute = measurement1.calculate_per_minute(&measurement2).unwrap();
        let sda1 = per_minute.stats.get("sda1").unwrap();
        assert_eq!(sda1.reads_completed_successfully, 120);
        assert_eq!(sda1.reads_merged, 120);
        assert_eq!(sda1.sectors_read, 120);
        assert_eq!(sda1.time_spent_reading_ms, 120);
        assert_eq!(sda1.writes_completed, 120);
        assert_eq!(sda1.writes_merged, 120);
        assert_eq!(sda1.sectors_written, 120);
        assert_eq!(sda1.time_spent_writing_ms, 120);
        assert_eq!(sda1.ios_currently_in_progress, 120);
        assert_eq!(sda1.time_spent_doing_ios_ms, 120);
        assert_eq!(sda1.weighted_time_spent_doing_ios_ms, 120);
    }

    #[test]
    fn test_calculate_per_minute_partial_minute() {
        let mut stats1 = HashMap::new();
        stats1.insert("sda1".to_owned(), helpers::disk_stat(0));
        let measurement1 = DiskStatsMeasurement {
            precise_time_ns: 60_000_000,
            stats: stats1
        };
        let mut stats2 = HashMap::new();
        stats2.insert("sda1".to_owned(), helpers::disk_stat(120));
        let measurement2 = DiskStatsMeasurement {
            precise_time_ns: 90_000_000,
            stats: stats2
        };

        let per_minute = measurement1.calculate_per_minute(&measurement2).unwrap();
        let sda1 = per_minute.stats.get("sda1").unwrap();
        assert_eq!(sda1.reads_completed_successfully, 60);
        assert_eq!(sda1.reads_merged, 60);
        assert_eq!(sda1.sectors_read, 60);
        assert_eq!(sda1.time_spent_reading_ms, 60);
        assert_eq!(sda1.writes_completed, 60);
        assert_eq!(sda1.writes_merged, 60);
        assert_eq!(sda1.sectors_written, 60);
        assert_eq!(sda1.time_spent_writing_ms, 60);
        assert_eq!(sda1.ios_currently_in_progress, 60);
        assert_eq!(sda1.time_spent_doing_ios_ms, 60);
        assert_eq!(sda1.weighted_time_spent_doing_ios_ms, 60);
    }

    #[test]
    fn test_calculate_per_minute_wrong_times() {
        let measurement1 = DiskStatsMeasurement {
            precise_time_ns: 500,
            stats: HashMap::new()
        };
        let measurement2 = DiskStatsMeasurement {
            precise_time_ns: 300,
            stats: HashMap::new()
        };

        match measurement1.calculate_per_minute(&measurement2) {
            Err(ProbeError::InvalidInput(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_calculate_per_minute_values_lower() {
        let mut stats1 = HashMap::new();
        stats1.insert("sda1".to_owned(), helpers::disk_stat(500));
        let measurement1 = DiskStatsMeasurement {
            precise_time_ns: 500,
            stats: stats1
        };
        let mut stats2 = HashMap::new();
        stats2.insert("sda1".to_owned(), helpers::disk_stat(400));
        let measurement2 = DiskStatsMeasurement {
            precise_time_ns: 600,
            stats: stats2
        };

        match measurement1.calculate_per_minute(&measurement2) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_calculate_per_minute_different_disks() {
        let mut stats1 = HashMap::new();
        stats1.insert("sda1".to_owned(), helpers::disk_stat(500));
        let measurement1 = DiskStatsMeasurement {
            precise_time_ns: 500,
            stats: stats1
        };
        let mut stats2 = HashMap::new();
        stats2.insert("sda2".to_owned(), helpers::disk_stat(600));
        let measurement2 = DiskStatsMeasurement {
            precise_time_ns: 600,
            stats: stats2
        };

        match measurement1.calculate_per_minute(&measurement2) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    mod helpers {
        use super::super::DiskStat;

        pub fn disk_stat(value: u64) -> DiskStat {
            DiskStat {
                reads_completed_successfully: value,
                reads_merged: value,
                sectors_read: value,
                time_spent_reading_ms: value,
                writes_completed: value,
                writes_merged: value,
                sectors_written: value,
                time_spent_writing_ms: value,
                ios_currently_in_progress: value,
                time_spent_doing_ios_ms: value,
                weighted_time_spent_doing_ios_ms: value
            }
        }
    }
}
<commit_msg>Add bytes read and written to disk io<commit_after>use std::collections::HashMap;
use std::path::Path;
use super::{Result,time_adjusted,calculate_time_difference};
use error::ProbeError;

pub type DiskStats = HashMap<String, DiskStat>;

#[derive(Debug,PartialEq)]
pub struct DiskStatsMeasurement {
    pub precise_time_ns: u64,
    pub stats: DiskStats
}

impl DiskStatsMeasurement {
    /// Calculate the disk stats per minute based on this measurement and a measurement in the
    /// future. It is advisable to make the next measurement roughly a minute from this one for the
    /// most reliable result.
    pub fn calculate_per_minute(&self, next_measurement: &DiskStatsMeasurement) -> Result<DiskStatsPerMinute> {
        let time_difference = try!(calculate_time_difference(self.precise_time_ns, next_measurement.precise_time_ns));

        let mut stats = HashMap::new();

        for (name, stat) in self.stats.iter() {
            let next_stat = match next_measurement.stats.get(name) {
                Some(stat) => stat,
                None => return Err(ProbeError::UnexpectedContent(format!("{} is not present in the next measurement", name)))
            };
            stats.insert(
                name.to_owned(),
                DiskStat {
                    reads_completed_successfully: try!(time_adjusted(next_stat.reads_completed_successfully, stat.reads_completed_successfully, time_difference)),
                    reads_merged: try!(time_adjusted(next_stat.reads_merged, stat.reads_merged, time_difference)),
                    sectors_read: try!(time_adjusted(next_stat.sectors_read, stat.sectors_read, time_difference)),
                    time_spent_reading_ms: try!(time_adjusted(next_stat.time_spent_reading_ms, stat.time_spent_reading_ms, time_difference)),
                    writes_completed: try!(time_adjusted(next_stat.writes_completed, stat.writes_completed, time_difference)),
                    writes_merged: try!(time_adjusted(next_stat.writes_merged, stat.writes_merged, time_difference)),
                    sectors_written: try!(time_adjusted(next_stat.sectors_written, stat.sectors_written, time_difference)),
                    time_spent_writing_ms: try!(time_adjusted(next_stat.time_spent_writing_ms, stat.time_spent_writing_ms, time_difference)),
                    ios_currently_in_progress: try!(time_adjusted(next_stat.ios_currently_in_progress, stat.ios_currently_in_progress, time_difference)),
                    time_spent_doing_ios_ms: try!(time_adjusted(next_stat.time_spent_doing_ios_ms, stat.time_spent_doing_ios_ms, time_difference)),
                    weighted_time_spent_doing_ios_ms: try!(time_adjusted(next_stat.weighted_time_spent_doing_ios_ms, stat.weighted_time_spent_doing_ios_ms, time_difference))
                }
            );
        }

        Ok(DiskStatsPerMinute {
            stats: stats
        })
    }
}

#[derive(Debug,PartialEq)]
pub struct DiskStat {
    pub reads_completed_successfully: u64,
    pub reads_merged: u64,
    pub sectors_read: u64,
    pub time_spent_reading_ms: u64,
    pub writes_completed: u64,
    pub writes_merged: u64,
    pub sectors_written: u64,
    pub time_spent_writing_ms: u64,
    pub ios_currently_in_progress: u64,
    pub time_spent_doing_ios_ms: u64,
    pub weighted_time_spent_doing_ios_ms: u64
}

impl DiskStat {
    pub fn bytes_read(&self) -> u64 {
        self.sectors_read * 512
    }

    pub fn bytes_written(&self) -> u64 {
        self.sectors_written * 512
    }
}

#[derive(Debug,PartialEq)]
pub struct DiskStatsPerMinute {
   pub stats: DiskStats
}

#[cfg(target_os = "linux")]
pub fn read() -> Result<DiskStatsMeasurement> {
    os::read_and_parse_proc_diskstats(&Path::new("/proc/diskstats"))
}

#[cfg(target_os = "linux")]
mod os {
    use std::io::BufRead;
    use std::path::Path;
    use std::collections::HashMap;
    use time;
    use super::{DiskStatsMeasurement,DiskStat};
    use super::super::{file_to_buf_reader,parse_u64,Result,ProbeError};

    #[inline]
    pub fn read_and_parse_proc_diskstats(path: &Path) -> Result<DiskStatsMeasurement> {
        let reader = try!(file_to_buf_reader(path));

        let mut out = DiskStatsMeasurement {
            precise_time_ns: time::precise_time_ns(),
            stats: HashMap::new()
        };

        for line_result in reader.lines() {
            let line = try!(line_result);
            let segments: Vec<&str> = line.split_whitespace().collect();

            if segments.len() != 14 {
                return Err(ProbeError::UnexpectedContent("Incorrect number of segments".to_owned()))
            }

            let disk_stat = DiskStat {
                reads_completed_successfully: try!(parse_u64(segments[3])),
                reads_merged: try!(parse_u64(segments[4])),
                sectors_read: try!(parse_u64(segments[5])),
                time_spent_reading_ms: try!(parse_u64(segments[6])),
                writes_completed: try!(parse_u64(segments[7])),
                writes_merged: try!(parse_u64(segments[8])),
                sectors_written: try!(parse_u64(segments[9])),
                time_spent_writing_ms: try!(parse_u64(segments[10])),
                ios_currently_in_progress: try!(parse_u64(segments[11])),
                time_spent_doing_ios_ms: try!(parse_u64(segments[12])),
                weighted_time_spent_doing_ios_ms: try!(parse_u64(segments[13]))
            };
            out.stats.insert(segments[2].to_owned(), disk_stat);
        }

        Ok(out)
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use std::path::Path;
    use error::ProbeError;
    use super::DiskStatsMeasurement;
    use super::os::read_and_parse_proc_diskstats;

    #[test]
    fn test_read_disk_stats() {
        assert!(super::read().is_ok());
    }

    #[test]
    fn test_read_and_parse_proc_diskstats() {
        let measurement = read_and_parse_proc_diskstats(&Path::new("fixtures/linux/disk_stats/proc_diskstats")).unwrap();

        assert!(measurement.precise_time_ns > 0);

        assert_eq!(2, measurement.stats.len());

        let sda = measurement.stats.get("sda").unwrap();
        assert_eq!(6185, sda.reads_completed_successfully);
        assert_eq!(9367, sda.reads_merged);
        assert_eq!(403272, sda.sectors_read);
        assert_eq!(206475264, sda.bytes_read());
        assert_eq!(22160, sda.time_spent_reading_ms);
        assert_eq!(2591, sda.writes_completed);
        assert_eq!(8251, sda.writes_merged);
        assert_eq!(84452, sda.sectors_written);
        assert_eq!(43239424, sda.bytes_written());
        assert_eq!(2860, sda.time_spent_writing_ms);
        assert_eq!(0, sda.ios_currently_in_progress);
        assert_eq!(8960, sda.time_spent_doing_ios_ms);
        assert_eq!(24990, sda.weighted_time_spent_doing_ios_ms);

        let sda1 = measurement.stats.get("sda1").unwrap();
        assert_eq!(483, sda1.reads_completed_successfully);
        assert_eq!(4782, sda1.reads_merged);
        assert_eq!(41466, sda1.sectors_read);
        assert_eq!(21230592, sda1.bytes_read());
        assert_eq!(1100, sda1.time_spent_reading_ms);
        assert_eq!(7, sda1.writes_completed);
        assert_eq!(1, sda1.writes_merged);
        assert_eq!(28, sda1.sectors_written);
        assert_eq!(14336, sda1.bytes_written());
        assert_eq!(40, sda1.time_spent_writing_ms);
        assert_eq!(0, sda1.ios_currently_in_progress);
        assert_eq!(930, sda1.time_spent_doing_ios_ms);
        assert_eq!(1140, sda1.weighted_time_spent_doing_ios_ms);
    }

    #[test]
    fn test_read_and_parse_proc_diskstats_incomplete() {
        match read_and_parse_proc_diskstats(&Path::new("fixtures/linux/disk_stats/proc_diskstats_incomplete")) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_read_and_parse_proc_diskstats_garbage() {
        match read_and_parse_proc_diskstats(&Path::new("fixtures/linux/disk_stats/proc_diskstats_garbage")) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_calculate_per_minute_full_minute() {
        let mut stats1 = HashMap::new();
        stats1.insert("sda1".to_owned(), helpers::disk_stat(0));
        let measurement1 = DiskStatsMeasurement {
            precise_time_ns: 60_000_000,
            stats: stats1
        };
        let mut stats2 = HashMap::new();
        stats2.insert("sda1".to_owned(), helpers::disk_stat(120));
        let measurement2 = DiskStatsMeasurement {
            precise_time_ns: 120_000_000,
            stats: stats2
        };

        let per_minute = measurement1.calculate_per_minute(&measurement2).unwrap();
        let sda1 = per_minute.stats.get("sda1").unwrap();
        assert_eq!(sda1.reads_completed_successfully, 120);
        assert_eq!(sda1.reads_merged, 120);
        assert_eq!(sda1.sectors_read, 120);
        assert_eq!(sda1.time_spent_reading_ms, 120);
        assert_eq!(sda1.writes_completed, 120);
        assert_eq!(sda1.writes_merged, 120);
        assert_eq!(sda1.sectors_written, 120);
        assert_eq!(sda1.time_spent_writing_ms, 120);
        assert_eq!(sda1.ios_currently_in_progress, 120);
        assert_eq!(sda1.time_spent_doing_ios_ms, 120);
        assert_eq!(sda1.weighted_time_spent_doing_ios_ms, 120);
    }

    #[test]
    fn test_calculate_per_minute_partial_minute() {
        let mut stats1 = HashMap::new();
        stats1.insert("sda1".to_owned(), helpers::disk_stat(0));
        let measurement1 = DiskStatsMeasurement {
            precise_time_ns: 60_000_000,
            stats: stats1
        };
        let mut stats2 = HashMap::new();
        stats2.insert("sda1".to_owned(), helpers::disk_stat(120));
        let measurement2 = DiskStatsMeasurement {
            precise_time_ns: 90_000_000,
            stats: stats2
        };

        let per_minute = measurement1.calculate_per_minute(&measurement2).unwrap();
        let sda1 = per_minute.stats.get("sda1").unwrap();
        assert_eq!(sda1.reads_completed_successfully, 60);
        assert_eq!(sda1.reads_merged, 60);
        assert_eq!(sda1.sectors_read, 60);
        assert_eq!(sda1.time_spent_reading_ms, 60);
        assert_eq!(sda1.writes_completed, 60);
        assert_eq!(sda1.writes_merged, 60);
        assert_eq!(sda1.sectors_written, 60);
        assert_eq!(sda1.time_spent_writing_ms, 60);
        assert_eq!(sda1.ios_currently_in_progress, 60);
        assert_eq!(sda1.time_spent_doing_ios_ms, 60);
        assert_eq!(sda1.weighted_time_spent_doing_ios_ms, 60);
    }

    #[test]
    fn test_calculate_per_minute_wrong_times() {
        let measurement1 = DiskStatsMeasurement {
            precise_time_ns: 500,
            stats: HashMap::new()
        };
        let measurement2 = DiskStatsMeasurement {
            precise_time_ns: 300,
            stats: HashMap::new()
        };

        match measurement1.calculate_per_minute(&measurement2) {
            Err(ProbeError::InvalidInput(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_calculate_per_minute_values_lower() {
        let mut stats1 = HashMap::new();
        stats1.insert("sda1".to_owned(), helpers::disk_stat(500));
        let measurement1 = DiskStatsMeasurement {
            precise_time_ns: 500,
            stats: stats1
        };
        let mut stats2 = HashMap::new();
        stats2.insert("sda1".to_owned(), helpers::disk_stat(400));
        let measurement2 = DiskStatsMeasurement {
            precise_time_ns: 600,
            stats: stats2
        };

        match measurement1.calculate_per_minute(&measurement2) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    #[test]
    fn test_calculate_per_minute_different_disks() {
        let mut stats1 = HashMap::new();
        stats1.insert("sda1".to_owned(), helpers::disk_stat(500));
        let measurement1 = DiskStatsMeasurement {
            precise_time_ns: 500,
            stats: stats1
        };
        let mut stats2 = HashMap::new();
        stats2.insert("sda2".to_owned(), helpers::disk_stat(600));
        let measurement2 = DiskStatsMeasurement {
            precise_time_ns: 600,
            stats: stats2
        };

        match measurement1.calculate_per_minute(&measurement2) {
            Err(ProbeError::UnexpectedContent(_)) => (),
            r => panic!("Unexpected result: {:?}", r)
        }
    }

    mod helpers {
        use super::super::DiskStat;

        pub fn disk_stat(value: u64) -> DiskStat {
            DiskStat {
                reads_completed_successfully: value,
                reads_merged: value,
                sectors_read: value,
                time_spent_reading_ms: value,
                writes_completed: value,
                writes_merged: value,
                sectors_written: value,
                time_spent_writing_ms: value,
                ios_currently_in_progress: value,
                time_spent_doing_ios_ms: value,
                weighted_time_spent_doing_ios_ms: value
            }
        }
    }
}
<|endoftext|>


<commit_before>/*================================================================================================*/
// Copyright 2016 Kyle Finlay
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*================================================================================================*/

use ::util::{Logger, Version};
use ::engine::PluginManager;

use std::boxed::Box;

/*================================================================================================*/
/*------STATIC VARIABLES--------------------------------------------------------------------------*/
/*================================================================================================*/

static mut APP_POINTER: Option <*mut App> = None;

/*================================================================================================*/
/*------APP STRUCT--------------------------------------------------------------------------------*/
/*================================================================================================*/

/// The app
///
/// This is the main control center of ionCore.
/// It is in charge of initialization, updating, and shutdown of all modules,
/// as well as the handing of any inter-module communication.
#[derive (Clone)]
pub struct App {

    // Public
    /// Manages all plugins.
    pub plugin_manager: PluginManager,

    // Private
    _name: String,
    _developer: String,
    _version: Version
}

/*================================================================================================*/
/*------APPVERSION PUBLIC METHODS-----------------------------------------------------------------*/
/*================================================================================================*/

impl App {

    /// Returns the name of the application.
    ///
    /// # Return value
    /// An immutable reference to the app name.
    pub fn get_name (&self) -> &str {
        &self._name
    }

    /// Returns the application developer
    ///
    /// # Return value
    /// An immutable reference to the app developer
    pub fn get_developer (&self) -> &str {
        &self._developer
    }

    /// Returns the version of the application.
    ///
    /// # Return value
    /// An immutable reference to the app version.
    pub fn get_version (&self) -> &Version {
        &self._version
    }

/*================================================================================================*/
/*------APPVERSION PUBLIC STATIC METHODS----------------------------------------------------------*/
/*================================================================================================*/

    /// Returns a new instance of the app builder
    ///
    /// # Return value
    /// A new instance of the app builder.
    pub fn builder () -> AppBuilder {

        AppBuilder {_name: "Ion App".to_string (),
                    _developer: String::new (),
                    _version: Version {major: 0, minor: 1, patch: 0}}
    }

    /// Gets a reference to the current app instance.
    pub fn instance () -> &'static App {
        unsafe {&*APP_POINTER.unwrap ()}
    }

    /// Releases the app instance.
    pub fn release () {

        info! ("Shutting down ion Core.
        Using 'App' after this point will result in a panic.");

        unsafe {

            Box::from_raw (APP_POINTER.unwrap ());
            APP_POINTER = None;
        };
    }
}

/*================================================================================================*/
/*------APPBUILDER STRUCT-------------------------------------------------------------------------*/
/*================================================================================================*/

/// The app builder
///
/// The app builder provides an easy and straightforward way of creating and
/// configuring a new app instance.
#[derive (Clone)]
pub struct AppBuilder {

    // Private
    _name: String,
    _developer: String,
    _version: Version
}

/*================================================================================================*/
/*------APPBUILDER PUBLIC METHODS-----------------------------------------------------------------*/
/*================================================================================================*/

impl AppBuilder {

    /// Builds the application
    ///
    /// # Return value
    /// A new app instance.
    pub fn build (&self) -> &'static App  {

        Logger::init ("./ionCore.log", true).unwrap ();
        info! ("Initializing ionCore | Version: {}", env! ("CARGO_PKG_VERSION"));

        let app_box = Box::new (App {plugin_manager: PluginManager::new (),
                                     _name: self._name.clone (),
                                     _developer: self._developer.clone (),
                                     _version: self._version});

        unsafe {APP_POINTER = Some (Box::into_raw (app_box))};
        App::instance ()
    }

    /// Sets the application name.
    ///
    /// # Arguments
    /// * `name` - The desired name of the application.
    ///
    /// # Return value
    /// A mutable reference to the current AppBuilder instance.
    pub fn name (&mut self, name: &str) -> &mut AppBuilder {

        self._name = name.to_string ();
        self
    }

    /// Sets the application developer.
    ///
    /// # Arguments
    /// * `developer` - The name of the developer.
    ///
    /// # Return value
    /// A mutable reference to the current AppBuilder instance.
    pub fn developer (&mut self, developer: &str) -> &mut AppBuilder {

        self._developer = developer.to_string ();
        self
    }

    /// Sets the version of the app.
    ///
    /// # Arguments
    /// * 'major' - The major version of the app.
    /// * `minor` - The minor version of the app.
    /// * `patch` - The patch version of the app.
    ///
    /// # Return value
    /// A mutable reference to the current AppBuilder instance.
    pub fn version (&mut self, major: i32, minor: i32, patch: i32) -> &mut AppBuilder {

        self._version.major = major;
        self._version.minor = minor;
        self._version.patch = patch;

        self
    }

    /// Sets the major version of the app.
    ///
    /// # Arguments
    /// * `major` - The major version of the app.
    ///
    /// # Return value
    /// A mutable reference to the current AppBuilder instance.
    pub fn version_major (&mut self, major: i32) -> &mut AppBuilder {

        self._version.major = major;
        self
    }

    /// Sets the minor version of the app.
    ///
    /// # Arguments
    /// * `minor` - The minor version of the app.
    ///
    /// # Return value
    /// A mutable reference to the current AppBuilder instance.
    pub fn version_minor (&mut self, minor: i32) -> &mut AppBuilder {

        self._version.minor = minor;
        self
    }

    /// Sets the patch version of the app.
    ///
    /// # Arguments
    /// * `patch` - The patch version of the app.
    ///
    /// # Return value
    /// A mutable reference to the current AppBuilder instance.
    pub fn version_patch (&mut self, patch: i32) -> &mut AppBuilder {

        self._version.patch = patch;
        self
    }
}
<commit_msg>Moving app to be a purely static struct<commit_after>/*===============================================================================================*/
// Copyright 2016 Kyle Finlay
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*===============================================================================================*/

use ::engine::PluginManager;

use std::boxed::Box;

/*===============================================================================================*/
/*------STATIC VARIABLES-------------------------------------------------------------------------*/
/*===============================================================================================*/

static mut APP_POINTER: Option <*mut App> = None;

/*===============================================================================================*/
/*------APP STRUCT-------------------------------------------------------------------------------*/
/*===============================================================================================*/

/// The app
///
/// This is the main control center of ionCore.
/// It is in charge of initialization, updating, and shutdown of all modules,
/// as well as the handing of any inter-module communication.
#[derive (Clone)]
pub struct App {

    // Public
    /// Manages all plugins.
    pub plugin_manager: PluginManager,
}

/*===============================================================================================*/
/*------APPVERSION PUBLIC STATIC METHODS---------------------------------------------------------*/
/*===============================================================================================*/

impl App {

    /// Checks if the app has been initialized.
    ///
    /// # Return value
    /// A bool returning whether the app has been initialized.
    pub fn is_initialized () -> bool {

        unsafe {

            match APP_POINTER {

                Some (pointer) => !pointer.is_null (),
                None => false
            }
        }
    }

/*-----------------------------------------------------------------------------------------------*/

    /// Releases the app instance.
    pub fn release () {

        if App::is_initialized () {

            info! ("Shutting down ion Core.");

            unsafe {

                Box::from_raw (APP_POINTER.unwrap ());
                APP_POINTER = None;
            };
        }
    }
}
<|endoftext|>


<commit_before>// automatically generated by rust-bindgen
#[allow(dead_code)]
const HAVE_F77_UNDERSCORE: u8 = 1;
#[allow(dead_code)]
const IEEE_754: u8 = 1;
#[allow(dead_code)]
const HAVE_VISIBILITY_ATTRIBUTE: u8 = 1;
#[allow(dead_code)]
const SUPPORT_UTF8: u8 = 1;
#[allow(dead_code)]
const SUPPORT_MBCS: u8 = 1;
#[allow(dead_code)]
const ENABLE_NLS: u8 = 1;
#[allow(dead_code)]
const SUPPORT_OPENMP: u8 = 1;
#[allow(dead_code)]
const SIZEOF_SIZE_T: u8 = 8;
#[allow(dead_code)]
const HAVE_ALLOCA_H: u8 = 1;
<commit_msg>Add comments for config.rs as Rconfig.h<commit_after>//! R_RCONFIG_H

// HAVE_F77_UNDERSCORE 1 /* all R platforms have this */
pub const HAVE_F77_UNDERSCORE: i32 = 1;

// IEEE_754 1 /* #undef WORDS_BIGENDIAN */
pub const IEEE_754: i32 = 1;

// R_INLINE inline

// HAVE_VISIBILITY_ATTRIBUTE 1 /* all R platforms have the next two */
pub const HAVE_VISIBILITY_ATTRIBUTE: i32 = 1;

// SUPPORT_UTF8 1
pub const SUPPORT_UTF8: i32 = 1;

// SUPPORT_MBCS 1
pub const SUPPORT_MBCS: i32 = 1;

// ENABLE_NLS 1 /* #undef HAVE_AQUA */
pub const ENABLE_NLS: i32 = 1;

// SUPPORT_OPENMP 1
pub const SUPPORT_OPENMP: i32 = 1;

// SIZEOF_SIZE_T 8
pub const SIZEOF_SIZE_T: i32 = 8;

// HAVE_ALLOCA_H 1
pub const HAVE_ALLOCA_H: i32 = 1;
<|endoftext|>


<commit_before>#![feature(core)]
#![feature(test)]

extern crate test;
extern crate wheel_timer;

use wheel_timer::WheelTimer;

#[test]
fn wheel_timer_schedule_test() {
  let mut timer = WheelTimer::new(10);
  timer.schedule(3, "tick");

  timer.tick();
  timer.tick();
  timer.tick();

  let list = timer.tick();
  assert_eq!(list.len(), 1);

  let val = list[0];
  assert_eq!(val, "tick");
}

#[test]
fn wheel_timer_tick_test() {
  let mut timer = WheelTimer::new(10);

  for i in range(0, 10) {
    timer.schedule(i, i)
  }

  for i in range(0, 10) {
    let list = timer.tick();
    assert_eq!(list.len(), 1);

    let val = list[0];
    assert_eq!(val, i);
  }
}

#[test]
fn wheel_timer_size_test() {
  let mut timer = Box::new(WheelTimer::new(10));

  for i in range(0, 10) {
    timer.schedule(i, i)
  }

  assert_eq!(timer.size(), 10);

  for _ in range(0, 10) {
    timer.tick();
  }

  assert_eq!(timer.size(), 0);
}
<commit_msg>Fixing tests for rust 1.0<commit_after>extern crate wheel_timer;

use wheel_timer::WheelTimer;

#[test]
fn wheel_timer_schedule_test() {
  let mut timer = WheelTimer::new(10);
  timer.schedule(3, "tick");

  timer.tick();
  timer.tick();
  timer.tick();

  let list = timer.tick();
  assert_eq!(list.len(), 1);

  let val = list[0];
  assert_eq!(val, "tick");
}

#[test]
fn wheel_timer_tick_test() {
  let mut timer = WheelTimer::new(10);

  for i in 0..10 {
    timer.schedule(i, i)
  }

  for i in 0..10 {
    let list = timer.tick();
    assert_eq!(list.len(), 1);

    let val = list[0];
    assert_eq!(val, i);
  }
}

#[test]
fn wheel_timer_size_test() {
  let mut timer = Box::new(WheelTimer::new(10));

  for i in 0..10 {
    timer.schedule(i, i)
  }

  assert_eq!(timer.size(), 10);

  for _ in 0..10 {
    timer.tick();
  }

  assert_eq!(timer.size(), 0);
}
<|endoftext|>


<commit_before>use std::ops::BitOr;
use super::{chtype, COLOR_PAIR};
use attributes::{Attribute, Attributes};

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct ColorPair(pub u8);

impl From<ColorPair> for chtype {
    fn from(color_pair: ColorPair) -> chtype {
        COLOR_PAIR(color_pair.0 as chtype)
    }
}

/// Implement the | operator for setting a color pair on an Attributes object
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
/// use pancurses::colorpair::ColorPair;
///
/// let mut attributes = Attributes::new();
/// assert!(attributes.color_pair().0 == 0);
/// attributes = attributes | ColorPair(1);
/// assert!(attributes.color_pair().0 == 1);
/// ```
impl BitOr<ColorPair> for Attributes {
    type Output = Attributes;

    fn bitor(mut self, rhs: ColorPair) -> Attributes {
        self.set_color_pair(rhs);
        self
    }
}

/// Implement the | operator for combining a ColorPair and an Attribute to produce Attributes
///
/// # Example
///
/// ```
/// use pancurses::Attribute;
/// use pancurses::colorpair::ColorPair;
///
/// let attributes = ColorPair(5) | Attribute::Blink;
/// assert!(attributes.color_pair().0 == 5);
/// assert!(!attributes.is_bold());
/// assert!(attributes.is_blink());
/// ```
impl BitOr<Attribute> for ColorPair {
    type Output = Attributes;

    fn bitor(self, rhs: Attribute) -> Attributes {
        Attributes::new() | self | rhs
    }
}

/// Implement the | operator for combining an Attribute and a  ColorPair to produce Attributes
///
/// # Example
///
/// ```
/// use pancurses::Attribute;
/// use pancurses::colorpair::ColorPair;
///
/// let attributes = Attribute::Blink | ColorPair(2);
/// assert!(attributes.color_pair().0 == 2);
/// assert!(!attributes.is_bold());
/// assert!(attributes.is_blink());
/// ```
impl BitOr<ColorPair> for Attribute {
    type Output = Attributes;

    fn bitor(self, rhs: ColorPair) -> Attributes {
        Attributes::new() | self | rhs
    }
}
<commit_msg>Make sure conversion from ColorPair to chtype is lossless<commit_after>use std::ops::BitOr;
use super::{chtype, COLOR_PAIR};
use attributes::{Attribute, Attributes};

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct ColorPair(pub u8);

impl From<ColorPair> for chtype {
    fn from(color_pair: ColorPair) -> chtype {
        COLOR_PAIR(chtype::from(color_pair.0))
    }
}

/// Implement the | operator for setting a color pair on an Attributes object
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
/// use pancurses::colorpair::ColorPair;
///
/// let mut attributes = Attributes::new();
/// assert!(attributes.color_pair().0 == 0);
/// attributes = attributes | ColorPair(1);
/// assert!(attributes.color_pair().0 == 1);
/// ```
impl BitOr<ColorPair> for Attributes {
    type Output = Attributes;

    fn bitor(mut self, rhs: ColorPair) -> Attributes {
        self.set_color_pair(rhs);
        self
    }
}

/// Implement the | operator for combining a ColorPair and an Attribute to produce Attributes
///
/// # Example
///
/// ```
/// use pancurses::Attribute;
/// use pancurses::colorpair::ColorPair;
///
/// let attributes = ColorPair(5) | Attribute::Blink;
/// assert!(attributes.color_pair().0 == 5);
/// assert!(!attributes.is_bold());
/// assert!(attributes.is_blink());
/// ```
impl BitOr<Attribute> for ColorPair {
    type Output = Attributes;

    fn bitor(self, rhs: Attribute) -> Attributes {
        Attributes::new() | self | rhs
    }
}

/// Implement the | operator for combining an Attribute and a  ColorPair to produce Attributes
///
/// # Example
///
/// ```
/// use pancurses::Attribute;
/// use pancurses::colorpair::ColorPair;
///
/// let attributes = Attribute::Blink | ColorPair(2);
/// assert!(attributes.color_pair().0 == 2);
/// assert!(!attributes.is_bold());
/// assert!(attributes.is_blink());
/// ```
impl BitOr<ColorPair> for Attribute {
    type Output = Attributes;

    fn bitor(self, rhs: ColorPair) -> Attributes {
        Attributes::new() | self | rhs
    }
}
<|endoftext|>


<commit_before>extern crate crypto;
extern crate reqwest;

use std::io::{self, Read, Write};
use std::path::Path;
use std::fs;
use std::fs::File;

use crypto::md5::Md5;
use crypto::digest::Digest;

use reqwest::Client;
use reqwest::StatusCode;

#[derive(Hash, Eq, PartialEq)]
struct LoLVersion {
    region: &'static str,
    version: &'static str,
    description: &'static str
}

static VERSIONS: [LoLVersion; 8] = [
    LoLVersion {
        version: "2016_11_10",
        region: "EUW",
        description: "EU West"
    },
    LoLVersion {
        region: "NA",
        version: "2016_05_13",
        description: "North America"
    },
    LoLVersion {
        region: "EUNE",
        version: "2016_11_10",
        description: "EU Nordic & East"
    },
    LoLVersion {
        region: "OC1",
        version: "2016_05_13",
        description: "Oceania"
    },
    LoLVersion {
        region: "RU",
        version: "2016_05_13",
        description: "Russia"
    },
    LoLVersion {
        region: "LA1",
        version: "2016_05_26",
        description: "Latin America North"
    },
    LoLVersion {
        region: "LA2",
        version: "2016_05_27",
        description: "Latin America South"
    },
    LoLVersion {
        region: "BR",
        version: "2016_05_13",
        description: "Brazil"
    }
];

fn main() {
    let http_client = Client::new().expect("Couldn't create client");
    let mut regions_found = vec!();
    let mut hash = Md5::new();
    let cache_folder = Path::new("filecache");
    if !cache_folder.exists() {
        fs::create_dir(cache_folder).expect("Failed to create installer cache folder");
    }

    for version in &VERSIONS {
        let download_url = get_url(&version);
        println!("Calculating hash for url: {}", download_url);
        // Check if file exists in cache
        let executable = cache_folder.join(format!("LeagueofLegends_{}_Installer_{}.exe", version.region, version.version));
        if !executable.exists() {
            if !download(&http_client, &download_url, &executable) {
                println!("Warning: {} returned 404", download_url);
                continue;
            }
        }

        let mut file = File::open(&executable).expect(format!("Could not find installer: {:?}", executable).as_ref());

        let mut bytes = vec!();
        file.read_to_end(&mut bytes).expect("Failed to read remote content");
        hash.input(&bytes);
        let hash_result = hash.result_str();
        println!("{}", &hash_result);
        regions_found.push((version, hash_result));
        hash.reset();
    }

    let hashes_map: Vec<String> = regions_found.iter()
        .map(|&(ref version, ref hash)| format!("[\"{}\"]=\"{}\"", version.description, hash))
        .collect();
    let url_map: Vec<String> = regions_found.iter()
        .map(|&(ref version, _)| format!("[\"{}\"]=\"{}\"", version.description, get_url(&version)))
        .collect();
    let region_map: Vec<String> = regions_found.iter()
        .map(|&(ref version, _)| format!("[\"{}\"]=\"{}\"", version.description, version.region))
        .collect();
    let description_map: Vec<String> = regions_found.iter()
        .map(|&(ref version, _)| version.description.to_owned())
        .collect();
    println!("declare -A HASHES=( {} )", hashes_map.join(" "));
    println!("declare -A URLS=( {} )", url_map.join(" "));
    println!("declare -A REGIONS=( {} )", region_map.join(" "));
    println!("REGION_DESC=\"{}\"", description_map.join("|"));
}

fn download(http_client: &Client, download_url: &String, path: &Path) -> bool {
    let mut result = http_client.get(download_url).send().expect("Failed to download file");
    if *(result.status()) != StatusCode::Ok {
        return false;
    }
    let mut out_file = File::create(path).expect("Could not create file.");
    io::copy(&mut result, &mut out_file).expect("Failed to write to file.");
    true
}

fn get_url(version: &LoLVersion) -> String {
    format!("https://riotgamespatcher-a.akamaihd.net/ShellInstaller/{region}/LeagueofLegends_{region}_Installer_{version}.exe",
            region = version.region, version = version.version)
}
<commit_msg>Clean up things and use less allocations.<commit_after>extern crate crypto;
extern crate reqwest;

use std::fs::{self, File};
use std::io::prelude::*;
use std::path::Path;

use crypto::md5::Md5;
use crypto::digest::Digest;

use reqwest::{Client, StatusCode};

#[derive(Debug)]
struct LoLVersion {
    region: &'static str,
    version: &'static str,
    desc: &'static str,
}

static VERSIONS: &'static [LoLVersion] = &[
    LoLVersion { region: "EUW", version: "2016_11_10", desc: "EU West" },
    LoLVersion { region: "NA", version: "2016_05_13", desc: "North America" },
    LoLVersion { region: "EUNE", version: "2016_11_10", desc: "EU Nordic & East" },
    LoLVersion { region: "OC1", version: "2016_05_13", desc: "Oceania" },
    LoLVersion { region: "RU", version: "2016_05_13", desc: "Russia" },
    LoLVersion { region: "LA1", version: "2016_05_26", desc: "Latin America North" },
    LoLVersion { region: "LA2", version: "2016_05_27", desc: "Latin America South" },
    LoLVersion { region: "BR", version: "2016_05_13", desc: "Brazil" },
];

fn main() {
    let cache_path = Path::new("filecache");

    if !cache_path.exists() {
        fs::create_dir(cache_path).expect("Could not create cache directory");
    }

    let mut found = Vec::new();
    let mut hash = Md5::new();
    let http_client = Client::new().expect("Could not create http client");

    for v in VERSIONS {
        let url = format!("https://riotgamespatcher-a.akamaihd.net/ShellInstaller/{region}/LeagueofLegends_{region}_Installer_{version}.exe",
            region = v.region, version = v.version);

        println!("Calculating hash for url: {}", url);

        let exec = cache_path.join(format!("LeagueofLegends_{}_Installer_{}.exe", v.region, v.version));

        // If the installer doesn't exist try to download it.
        if !exec.exists() {
            let mut res = http_client.get(&url).send().expect("Could not download file");

            if *res.status() != StatusCode::Ok {
                println!("Warning: Couldn't download file {}", url);
                continue;
            }

            let mut buf = vec![];
            res.read_to_end(&mut buf).expect("Could not read from response");

            let mut file = File::create(&exec).expect(&format!("Could not create installer at: {:?}", exec));
            file.write_all(&buf).expect("Could not write to file {}");
        }

        let mut file = File::open(&exec).expect(&format!("Could not find installer: {:?}", exec));

        let mut buf = vec![];
        file.read_to_end(&mut buf).expect("Could not read installer data");
        hash.input(&buf);

        let hash_str = hash.result_str();
        println!("{}", hash_str);

        hash.reset();

        found.push((hash_str, url, v));
    }

    // Create string outputs for hashes, urls, regions, and descriptions using 4 string allocations.
    let (mut hashes, mut urls, mut regions, mut desc) = found.iter()
        .fold((String::new(), String::new(), String::new(), String::new()), |s, &(ref hash, ref url, v)| {
            ((s.0 + "[\"" + v.desc + "\"]=\"" + hash + "\" "),
             (s.1 + "[\"" + v.desc + "\"]=\"" + url + "\" "),
             (s.2 + "[\"" + v.desc + "\"]=\"" + v.region + "\" "),
             (s.3 + v.desc + "|"))
        });

    // For now we just pop the extra last character off of each string because of the fold.
    hashes.pop(); urls.pop(); regions.pop(); desc.pop();

    println!("declare -A HASHES=( {} )", hashes);
    println!("declare -A URLS=( {} )", urls);
    println!("declare -A REGIONS=( {} )", regions);
    println!("REGION_DESC=\"{}\"", desc);
}<|endoftext|>


<commit_before>// Copyright (C) 2020 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use mockito::{mock, Mock};
use regex::Regex;
use serde_json::json;
use std::{env, path::PathBuf};

pub enum FakeServer {
    NoUpdate,
    HasUpdate(String),
}

pub enum Polling {
    Enable,
    Disable,
}

pub enum Server {
    Custom(String),
    Standard,
}

pub struct Settings {
    polling: bool,
    listen_socket: String,
    server_address: String,
    download_dir: Option<PathBuf>,
    config_file: Option<PathBuf>,
    timeout: Option<u64>,
}

impl Default for Settings {
    fn default() -> Self {
        Settings {
            polling: false,
            listen_socket: String::from("localhost:8080"),
            server_address: mockito::server_url(),
            download_dir: None,
            config_file: None,
            timeout: None,
        }
    }
}

impl Settings {
    pub fn init_server(self) -> (rexpect::session::PtySession, updatehub::tests::TestEnvironment) {
        let setup = updatehub::tests::TestEnvironment::build()
            .listen_socket(self.listen_socket)
            .server_address(self.server_address)
            .add_echo_binary("reboot");

        let mut setup = if !self.polling { setup.disable_polling() } else { setup }.finish();

        if let Some(download_dir) = self.download_dir {
            setup.settings.data.update.download_dir = download_dir;

            let content = toml::ser::to_string_pretty(&setup.settings.data.0)
                .expect("fail to convert the data to toml");
            std::fs::write(&setup.settings.stored_path, content)
                .expect("fail to write the content on settings file");
        }

        if let Some(config_file) = self.config_file {
            setup.settings.stored_path = config_file;
        }

        let cmd = format!(
            "{} server -v trace -c {}",
            cargo_bin("updatehub").to_string_lossy(),
            setup.settings.stored_path.to_string_lossy()
        );

        let handle = rexpect::spawn(&cmd, self.timeout).expect("fail to spawn server command");

        (handle, setup)
    }

    pub fn timeout(self, t: u64) -> Self {
        Settings { timeout: Some(t), ..self }
    }

    pub fn config_file(self, p: PathBuf) -> Self {
        Settings { config_file: Some(p), ..self }
    }

    pub fn download_dir(self, p: PathBuf) -> Self {
        Settings { download_dir: Some(p), ..self }
    }

    pub fn polling(self) -> Self {
        Settings { polling: true, ..self }
    }

    pub fn listen_socket(self, s: String) -> Self {
        Settings { listen_socket: s, ..self }
    }

    pub fn server_address(self, s: String) -> Self {
        Settings { server_address: s, ..self }
    }
}

pub fn get_output_server(handle: &mut rexpect::session::PtySession, polling: Polling) -> String {
    handle
        .exp_string(match polling {
            Polling::Enable => "TRCE delaying transition for:",
            Polling::Disable => "TRCE stopping transition until awoken",
        })
        .expect("fail to match the required string")
}

pub fn run_client_probe(server: Server) -> String {
    let cmd_string = format!("{} client probe", cargo_bin("updatehub").to_string_lossy());
    let cmd = match server {
        Server::Custom(server_address) => format!("{} --server {}", cmd_string, server_address),
        Server::Standard => cmd_string,
    };
    let mut handle = rexpect::spawn(&cmd, None).expect("fail to spawn probe command");
    handle.exp_eof().expect("fail to match the EOF for client")
}

pub fn run_client_log() -> String {
    let cmd = format!("{} client log", cargo_bin("updatehub").to_string_lossy());
    let mut handle = rexpect::spawn(&cmd, None).expect("fail to spawn log command");
    handle.exp_eof().expect("fail to match the EOF for client")
}

pub fn cargo_bin<S: AsRef<str>>(name: S) -> PathBuf {
    let mut target_dir = env::current_exe().expect("fail to get current binary name");

    target_dir.pop();
    if target_dir.ends_with("deps") {
        target_dir.pop();
    }

    target_dir.join(format!("{}{}", name.as_ref(), env::consts::EXE_SUFFIX))
}

pub fn create_mock_server(server: FakeServer) -> Vec<Mock> {
    use mockito::Matcher;

    let json_update = json!({
        "product": "0123456789",
        "version": "1.2",
        "supported-hardware": ["board"],
        "objects":
        [
            [
                {
                    "mode": "test",
                    "filename": "testfile",
                    "target": "/dev/device1",
                    "sha256sum": "23c3c412177bd37b9b61bf4738b18dc1fe003811c2583a14d2d9952d8b6a75b4",
                    "size": 40960
                }
            ],
            [
                {
                    "mode": "test",
                    "filename": "testfile",
                    "target": "/dev/device2",
                    "sha256sum": "23c3c412177bd37b9b61bf4738b18dc1fe003811c2583a14d2d9952d8b6a75b4",
                    "size": 40960
                }
            ]
        ]
    });

    let request_body = Matcher::Json(json!({
        "product-uid": "229ffd7e08721d716163fc81a2dbaf6c90d449f0a3b009b6a2defe8a0b0d7381",
        "version": "1.1",
        "hardware": "board",
        "device-identity": {
            "id1":"value1",
            "id2":"value2"
        },
        "device-attributes": {
            "attr1":"attrvalue1",
            "attr2":"attrvalue2"
        }
    }));

    match server {
        FakeServer::NoUpdate => vec![
            mock("POST", "/upgrades")
                .match_header("Content-Type", "application/json")
                .match_header("Api-Content-Type", "application/vnd.updatehub-v1+json")
                .match_body(request_body)
                .with_status(404)
                .create(),
        ],
        FakeServer::HasUpdate(product_uid) => vec![
            mock("POST", "/upgrades")
                .match_header("Content-Type", "application/json")
                .match_header("Api-Content-Type", "application/vnd.updatehub-v1+json")
                .match_body(request_body)
                .with_status(200)
                .with_header("UH-Signature", &openssl::base64::encode_block(b"some_signature"))
                .with_body(&json_update.to_string())
                .create(),
            mock(
                "GET",
                format!("/products/{}/packages/7b5078f6e7549ad5a1397d7d95b0ba20cffbb16bb739fec6b6c078cd94707786/objects/23c3c412177bd37b9b61bf4738b18dc1fe003811c2583a14d2d9952d8b6a75b4", product_uid)
                    .as_str(),
            )
            .match_header("Content-Type", "application/json")
            .match_header("Api-Content-Type", "application/vnd.updatehub-v1+json")
            .with_status(200)
                .with_body(std::iter::repeat(0xF).take(40960).collect::<Vec<_>>())
            .create(),
        ],
    }
}

pub fn format_output_server(s: String) -> (String, String) {
    let version_re = Regex::new(r"Agent .*").unwrap();
    let tmpfile_re = Regex::new(r#""/tmp/.tmp.*""#).unwrap();
    let date_re = Regex::new(r"\b(?:Jan|...|Dec) (\d{2}) (\d{2}):(\d{2}):(\d{2}).(\d{3})").unwrap();
    let trce_re = Regex::new(r"<timestamp> TRCE.*").unwrap();
    let debg_re = Regex::new(r"<timestamp> DEBG.*").unwrap();
    let download_re = Regex::new(r"DEBG (\d{2})%").unwrap();

    let s = version_re.replace_all(&s, "Agent <version>");
    let s = tmpfile_re.replace_all(&s, r#""<file>""#);
    let s = date_re.replace_all(&s, "<timestamp>");
    let s = download_re.replace_all(&s, "DEBG <percentage>%");
    let mut iter = s.lines();
    iter.next_back();
    let s_trce = iter.fold(String::default(), |acc, l| acc + l + "\n");

    let s_info = trce_re.replace_all(&s_trce, "");
    let s_info = debg_re.replace_all(&s_info, "");
    let s_info = s_info
        .split('\n')
        .map(|s| s.trim())
        .filter(|s| !s.is_empty())
        .collect::<Vec<_>>()
        .join("\n");

    (s_trce, s_info)
}

pub fn remove_carriage_newline_caracters(s: String) -> String {
    s.replace("\r\n", "\n")
}

pub fn format_output_client_log(s: String) -> String {
    let date_re =
        Regex::new(r"(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2}).(\d{9}) (-|\+)(\d{4})")
            .expect("fail to compile the date regexp");
    let s = date_re.replace_all(&s, "<timestamp>");

    let tmpfile_re = Regex::new(r#"\\"/tmp/.tmp.*""#).expect("fail to compile the tmpfile regexp");
    let s = tmpfile_re.replace_all(&s, r#""<file>""#);

    remove_carriage_newline_caracters(s.to_string())
}
<commit_msg>updatehub: change the get_output_server function<commit_after>// Copyright (C) 2020 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use mockito::{mock, Mock};
use regex::Regex;
use serde_json::json;
use std::{env, path::PathBuf};

pub enum FakeServer {
    NoUpdate,
    HasUpdate(String),
}

pub enum Polling {
    Enable,
    Disable,
}

pub enum Server {
    Custom(String),
    Standard,
}

pub struct Settings {
    polling: bool,
    listen_socket: String,
    server_address: String,
    download_dir: Option<PathBuf>,
    config_file: Option<PathBuf>,
    timeout: Option<u64>,
}

impl Default for Settings {
    fn default() -> Self {
        Settings {
            polling: false,
            listen_socket: String::from("localhost:8080"),
            server_address: mockito::server_url(),
            download_dir: None,
            config_file: None,
            timeout: None,
        }
    }
}

impl Settings {
    pub fn init_server(self) -> (rexpect::session::PtySession, updatehub::tests::TestEnvironment) {
        let setup = updatehub::tests::TestEnvironment::build()
            .listen_socket(self.listen_socket)
            .server_address(self.server_address)
            .add_echo_binary("reboot");

        let mut setup = if !self.polling { setup.disable_polling() } else { setup }.finish();

        if let Some(download_dir) = self.download_dir {
            setup.settings.data.update.download_dir = download_dir;

            let content = toml::ser::to_string_pretty(&setup.settings.data.0)
                .expect("fail to convert the data to toml");
            std::fs::write(&setup.settings.stored_path, content)
                .expect("fail to write the content on settings file");
        }

        if let Some(config_file) = self.config_file {
            setup.settings.stored_path = config_file;
        }

        let cmd = format!(
            "{} server -v trace -c {}",
            cargo_bin("updatehub").to_string_lossy(),
            setup.settings.stored_path.to_string_lossy()
        );

        let handle = rexpect::spawn(&cmd, self.timeout).expect("fail to spawn server command");

        (handle, setup)
    }

    pub fn timeout(self, t: u64) -> Self {
        Settings { timeout: Some(t), ..self }
    }

    pub fn config_file(self, p: PathBuf) -> Self {
        Settings { config_file: Some(p), ..self }
    }

    pub fn download_dir(self, p: PathBuf) -> Self {
        Settings { download_dir: Some(p), ..self }
    }

    pub fn polling(self) -> Self {
        Settings { polling: true, ..self }
    }

    pub fn listen_socket(self, s: String) -> Self {
        Settings { listen_socket: s, ..self }
    }

    pub fn server_address(self, s: String) -> Self {
        Settings { server_address: s, ..self }
    }
}

pub fn get_output_server(handle: &mut rexpect::session::PtySession, polling: Polling) -> String {
    handle
        .exp_regex(match polling {
            Polling::Enable => "\r\n.* TRCE delaying transition for:.*",
            Polling::Disable => "\r\n.* TRCE stopping transition until awoken.*",
        })
        .expect("fail to match the required string")
        .0
}

pub fn run_client_probe(server: Server) -> String {
    let cmd_string = format!("{} client probe", cargo_bin("updatehub").to_string_lossy());
    let cmd = match server {
        Server::Custom(server_address) => format!("{} --server {}", cmd_string, server_address),
        Server::Standard => cmd_string,
    };
    let mut handle = rexpect::spawn(&cmd, None).expect("fail to spawn probe command");
    handle.exp_eof().expect("fail to match the EOF for client")
}

pub fn run_client_log() -> String {
    let cmd = format!("{} client log", cargo_bin("updatehub").to_string_lossy());
    let mut handle = rexpect::spawn(&cmd, None).expect("fail to spawn log command");
    handle.exp_eof().expect("fail to match the EOF for client")
}

pub fn cargo_bin<S: AsRef<str>>(name: S) -> PathBuf {
    let mut target_dir = env::current_exe().expect("fail to get current binary name");

    target_dir.pop();
    if target_dir.ends_with("deps") {
        target_dir.pop();
    }

    target_dir.join(format!("{}{}", name.as_ref(), env::consts::EXE_SUFFIX))
}

pub fn create_mock_server(server: FakeServer) -> Vec<Mock> {
    use mockito::Matcher;

    let json_update = json!({
        "product": "0123456789",
        "version": "1.2",
        "supported-hardware": ["board"],
        "objects":
        [
            [
                {
                    "mode": "test",
                    "filename": "testfile",
                    "target": "/dev/device1",
                    "sha256sum": "23c3c412177bd37b9b61bf4738b18dc1fe003811c2583a14d2d9952d8b6a75b4",
                    "size": 40960
                }
            ],
            [
                {
                    "mode": "test",
                    "filename": "testfile",
                    "target": "/dev/device2",
                    "sha256sum": "23c3c412177bd37b9b61bf4738b18dc1fe003811c2583a14d2d9952d8b6a75b4",
                    "size": 40960
                }
            ]
        ]
    });

    let request_body = Matcher::Json(json!({
        "product-uid": "229ffd7e08721d716163fc81a2dbaf6c90d449f0a3b009b6a2defe8a0b0d7381",
        "version": "1.1",
        "hardware": "board",
        "device-identity": {
            "id1":"value1",
            "id2":"value2"
        },
        "device-attributes": {
            "attr1":"attrvalue1",
            "attr2":"attrvalue2"
        }
    }));

    match server {
        FakeServer::NoUpdate => vec![
            mock("POST", "/upgrades")
                .match_header("Content-Type", "application/json")
                .match_header("Api-Content-Type", "application/vnd.updatehub-v1+json")
                .match_body(request_body)
                .with_status(404)
                .create(),
        ],
        FakeServer::HasUpdate(product_uid) => vec![
            mock("POST", "/upgrades")
                .match_header("Content-Type", "application/json")
                .match_header("Api-Content-Type", "application/vnd.updatehub-v1+json")
                .match_body(request_body)
                .with_status(200)
                .with_header("UH-Signature", &openssl::base64::encode_block(b"some_signature"))
                .with_body(&json_update.to_string())
                .create(),
            mock(
                "GET",
                format!("/products/{}/packages/7b5078f6e7549ad5a1397d7d95b0ba20cffbb16bb739fec6b6c078cd94707786/objects/23c3c412177bd37b9b61bf4738b18dc1fe003811c2583a14d2d9952d8b6a75b4", product_uid)
                    .as_str(),
            )
            .match_header("Content-Type", "application/json")
            .match_header("Api-Content-Type", "application/vnd.updatehub-v1+json")
            .with_status(200)
                .with_body(std::iter::repeat(0xF).take(40960).collect::<Vec<_>>())
            .create(),
        ],
    }
}

pub fn format_output_server(s: String) -> (String, String) {
    let version_re = Regex::new(r"Agent .*").unwrap();
    let tmpfile_re = Regex::new(r#""/tmp/.tmp.*""#).unwrap();
    let date_re = Regex::new(r"\b(?:Jan|...|Dec) (\d{2}) (\d{2}):(\d{2}):(\d{2}).(\d{3})").unwrap();
    let trce_re = Regex::new(r"<timestamp> TRCE.*").unwrap();
    let debg_re = Regex::new(r"<timestamp> DEBG.*").unwrap();
    let download_re = Regex::new(r"DEBG (\d{2})%").unwrap();

    let s = version_re.replace_all(&s, "Agent <version>");
    let s = tmpfile_re.replace_all(&s, r#""<file>""#);
    let s = date_re.replace_all(&s, "<timestamp>");
    let s = download_re.replace_all(&s, "DEBG <percentage>%");
    let s_trce = s.replace("\r\n", "\n");

    let s_info = trce_re.replace_all(&s_trce, "");
    let s_info = debg_re.replace_all(&s_info, "");
    let s_info = s_info
        .split('\n')
        .map(|s| s.trim())
        .filter(|s| !s.is_empty())
        .collect::<Vec<_>>()
        .join("\n");

    (s_trce, s_info)
}

pub fn remove_carriage_newline_caracters(s: String) -> String {
    s.replace("\r\n", "\n")
}

pub fn format_output_client_log(s: String) -> String {
    let date_re =
        Regex::new(r"(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2}).(\d{9}) (-|\+)(\d{4})")
            .expect("fail to compile the date regexp");
    let s = date_re.replace_all(&s, "<timestamp>");

    let tmpfile_re = Regex::new(r#"\\"/tmp/.tmp.*""#).expect("fail to compile the tmpfile regexp");
    let s = tmpfile_re.replace_all(&s, r#""<file>""#);

    remove_carriage_newline_caracters(s.to_string())
}
<|endoftext|>