<commit_before>use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::fmt::{Debug, Error, Formatter};
use std::rc::Rc;

use super::super::target::bytecode::layout::{Instruction, Reg};

use super::loader::{BytecodeFunction, LoadedFunction, LoadedModule};
use super::value::Value;

struct InnerClosure {
    locals: HashMap<String, Option<Value>>,
    parent: Option<Closure>,
    /// If this closure is the static closure environment for a module.
    is_static: bool,
}

#[derive(Clone)]
pub struct Closure(Rc<RefCell<InnerClosure>>);

impl Closure {
    pub fn new(bindings: Option<HashSet<String>>, parent: Option<Closure>) -> Self {
        let mut locals = HashMap::<String, Option<Value>>::new();
        if let Some(bindings) = bindings {
            for binding in bindings.iter() {
                locals.insert(binding.clone(), None);
            }
        };
        Self(Rc::new(RefCell::new(InnerClosure {
            locals,
            parent,
            is_static: false,
        })))
    }

    pub fn new_static() -> Self {
        Self(Rc::new(RefCell::new(InnerClosure {
            locals: HashMap::new(),
            parent: None,
            is_static: true,
        })))
    }

    fn get(&self, name: &String) -> Option<Value> {
        let inner = &self.0.borrow();
        if let Some(value) = inner.locals.get(name) {
            return match value {
                initialized @ Some(_) => initialized.clone(),
                None => unreachable!("ERROR: Uninitialized closure variable: {}", name),
            };
        }
        if let Some(parent) = &inner.parent {
            return parent.get(name);
        }
        unreachable!("ERROR: Couldn't find closure variable: {}", name)
    }

    /// Returns true if it found a closure in which to set the variable,
    /// false if not.
    pub fn try_set(&self, name: String, value: Value) -> bool {
        let inner = &mut self.0.borrow_mut();
        if let Some(exists) = inner.locals.get_mut(&name) {
            *exists = Some(value);
            return true;
        }
        // If it's static then we can create new locals at will.
        if inner.is_static {
            inner.locals.insert(name, Some(value));
            return true;
        }
        if let Some(parent) = &inner.parent {
            // If we found a parent with this name and were able to set the
            // value then all is good.
            if parent.set_as_parent(name.clone(), value) {
                return true;
            }
        }
        return false;
    }

    // Recursive call to set in parent closures. Returns true if it found
    // a local and set, false if not.
    fn set_as_parent(&self, name: String, value: Value) -> bool {
        let inner = &mut self.0.borrow_mut();
        if inner.locals.contains_key(&name) {
            inner.locals.insert(name.clone(), Some(value));
            return true;
        }
        if let Some(parent) = &inner.parent {
            parent.set_as_parent(name, value)
        } else {
            false
        }
    }
}

// An action for the VM to do.
pub enum Action {
    Call(Frame),
    Return(Value),
}

pub enum Frame {
    Bytecode(BytecodeFrame),
}

impl FrameApi for Frame {
    fn run(&mut self) -> Action {
        match self {
            Frame::Bytecode(frame) => frame.run(),
        }
    }

    fn get_lexical(&self, name: &String) -> Value {
        match self {
            Frame::Bytecode(frame) => frame.get_lexical(name),
        }
    }

    fn set_lexical(&mut self, name: &String, value: Value) {
        match self {
            Frame::Bytecode(frame) => frame.set_lexical(name, value),
        }
    }

    fn receive_return(&mut self, value: Value) {
        match self {
            Frame::Bytecode(frame) => frame.receive_return(value),
        }
    }
}

pub trait FrameApi {
    // Run the frame's fetch-execute loop. Will be different depending on if
    // it's a bytecode or native frame.
    fn run(&mut self) -> Action;

    fn get_lexical(&self, name: &String) -> Value;

    fn set_lexical(&mut self, name: &String, value: Value);

    /// Called before the frame resumes execution after the higher frame has
    /// returned a value.
    ///
    /// [0].run() -> Call
    ///   [1].run() -> Return(value)
    /// [0].receive_return(value)
    /// [0].run() -> ...
    fn receive_return(&mut self, value: Value);
}

// Frame evaluating a bytecode function.
//
// The first three fields should *not* be changed after the frame
// is initialized.
pub struct BytecodeFrame {
    // TODO: Replace `LoadedFunction` with an abstraction that can support
    //   specialized instruction sequences.
    function: LoadedFunction,
    bytecode: BytecodeFunction,
    /// The slots for the local variables (not bound to a closure).
    locals: Vec<Value>,
    closure: Option<Closure>,
    /// The register for the returned value to be stored in.
    return_register: Option<Reg>,
    registers: Vec<Value>,
    current_address: usize,
}

impl BytecodeFrame {
    pub fn new(function: LoadedFunction, closure: Option<Closure>) -> Self {
        let bytecode = function.bytecode();
        let registers = bytecode.registers();
        let locals = bytecode.locals();
        Self {
            function,
            bytecode,
            locals: vec![Value::Null; locals as usize],
            closure,
            return_register: None,
            registers: vec![Value::Null; registers as usize],
            current_address: 0,
        }
    }

    pub fn unit(&self) -> LoadedModule {
        self.function.module()
    }

    #[inline]
    pub fn current(&self) -> Instruction {
        self.bytecode.instruction(self.current_address)
    }

    #[inline]
    pub fn advance(&mut self) {
        self.current_address += 1;
    }

    #[inline]
    fn offset_register(index: Reg) -> usize {
        (index as usize) - 1
    }

    pub fn read_register(&self, index: Reg) -> Value {
        self.registers[BytecodeFrame::offset_register(index)].clone()
    }

    fn write_register(&mut self, index: Reg, value: Value) {
        if index == 0 {
            return;
        }
        self.registers[BytecodeFrame::offset_register(index)] = value;
    }

    pub fn get_constant<N: AsRef<str>>(&self, name: N) -> Value {
        self.function.module().get_constant(name.as_ref())
    }

    pub fn get_local(&self, index: u8) -> Value {
        self.locals[index as usize].clone()
    }

    pub fn set_local(&mut self, index: u8, value: Value) {
        self.locals[index as usize] = value;
    }

    fn get_index_of_local(&self, name: &String) -> Option<usize> {
        self.bytecode
            .locals_names()
            .iter()
            .position(|local| local == name)
    }
}

impl FrameApi for BytecodeFrame {
    fn run(&mut self) -> Action {
        loop {
            let instruction = self.current();

            match &instruction {
                Instruction::GetConstant(lval, name) => {
                    self.write_register(*lval, self.get_constant(name));
                    self.advance();
                }
                Instruction::GetLocal(lval, index) => {
                    self.write_register(*lval, self.get_local(*index));
                    self.advance();
                }
                Instruction::GetLocalLexical(lval, name) => {
                    self.write_register(*lval, self.get_lexical(name));
                    self.advance();
                }
                Instruction::SetLocal(index, rval) => {
                    self.set_local(*index, self.read_register(*rval));
                    self.advance();
                }
                Instruction::SetLocalLexical(name, rval) => {
                    self.set_lexical(name, self.read_register(*rval));
                    self.advance();
                }
                Instruction::MakeFunction(lval, id) => {
                    let function = self.unit().function(*id);
                    let closure = if function.binds_on_create() {
                        self.closure.clone()
                    } else {
                        None
                    };
                    let value = Value::from_dynamic_function(function, closure);
                    self.write_register(*lval, value);
                    self.advance();
                }
                Instruction::MakeInteger(lval, value) => {
                    self.write_register(*lval, Value::Integer(*value));
                    self.advance();
                }
                Instruction::Branch(destination) => {
                    self.current_address = *destination as usize;
                }
                Instruction::Call(lval, target, arguments) => {
                    let target = self.read_register(*target);
                    let arguments = arguments
                        .iter()
                        .map(|argument| self.read_register(*argument))
                        .collect::<Vec<Value>>();
                    match target {
                        Value::DynamicFunction(dynamic_function) => {
                            // Save the return register for when the VM calls `receive_return`.
                            self.return_register = Some(*lval);
                            // TODO: Make `CallTarget` able to do specialization.
                            let function = dynamic_function.call_target.function;
                            let parent_closure = dynamic_function.closure;
                            let closure = if function.binds_on_call() {
                                let bindings = function.bindings();
                                let maybe_bindings = if !bindings.is_empty() {
                                    Some(bindings)
                                } else {
                                    None
                                };
                                Some(Closure::new(maybe_bindings, parent_closure))
                            } else {
                                None
                            };
                            let frame = Frame::Bytecode(BytecodeFrame::new(function, closure));
                            // Be at the next instruction when control flow returns to us.
                            self.advance();
                            return Action::Call(frame);
                        }
                        Value::NativeFunction(native_function) => {
                            let result = native_function.call(arguments);
                            self.write_register(*lval, result);
                            self.advance();
                        }
                        _ => panic!("Cannot call"),
                    }
                }
                Instruction::Return(rval) => {
                    let value = self.read_register(*rval);
                    return Action::Return(value);
                }
                Instruction::ReturnNull => {
                    return Action::Return(Value::Null);
                }
            }
        }
    }

    fn get_lexical(&self, name: &String) -> Value {
        if let Some(index) = self.get_index_of_local(name) {
            return self.locals[index].clone();
        }
        if let Some(closure) = &self.closure {
            return closure.get(name).expect(&format!("Not found: {}", name));
        }
        panic!("Not found: {}", name)
    }

    fn set_lexical(&mut self, name: &String, value: Value) {
        if let Some(closure) = &self.closure {
            if closure.try_set(name.clone(), value.clone()) {
                return;
            }
        }
        if let Some(index) = self.get_index_of_local(name) {
            self.locals[index] = value;
        } else {
            panic!("Not found: {}\nFrame: {:?}", name, self)
        }
    }

    fn receive_return(&mut self, value: Value) {
        let return_register = self.return_register.expect("Return register not set");
        self.write_register(return_register, value);
        self.return_register = None;
    }
}

impl Debug for BytecodeFrame {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        write!(
            f,
            "BytecodeFrame {{ locals: {:?}, closure: {:?} }}",
            self.locals.len(),
            self.closure
        )
    }
}

impl Debug for Closure {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        let inner = &*self.0.borrow();
        let locals = inner
            .locals
            .keys()
            .map(|k| k.to_owned())
            .collect::<Vec<String>>();
        write!(
            f,
            "Closure {{ locals: {:?}, parent: {:?}, is_static: {:?} }}",
            locals, inner.parent, inner.is_static
        )
    }
}
<commit_msg>Temporarily (maybe permanently) disable setting of unknown locals in static closure<commit_after>use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::fmt::{Debug, Error, Formatter};
use std::rc::Rc;

use super::super::target::bytecode::layout::{Instruction, Reg};

use super::loader::{BytecodeFunction, LoadedFunction, LoadedModule};
use super::value::Value;

struct InnerClosure {
    locals: HashMap<String, Option<Value>>,
    parent: Option<Closure>,
    /// If this closure is the static closure environment for a module.
    is_static: bool,
}

#[derive(Clone)]
pub struct Closure(Rc<RefCell<InnerClosure>>);

impl Closure {
    pub fn new(bindings: Option<HashSet<String>>, parent: Option<Closure>) -> Self {
        let mut locals = HashMap::<String, Option<Value>>::new();
        if let Some(bindings) = bindings {
            for binding in bindings.iter() {
                locals.insert(binding.clone(), None);
            }
        };
        Self(Rc::new(RefCell::new(InnerClosure {
            locals,
            parent,
            is_static: false,
        })))
    }

    pub fn new_static() -> Self {
        Self(Rc::new(RefCell::new(InnerClosure {
            locals: HashMap::new(),
            parent: None,
            is_static: true,
        })))
    }

    fn get(&self, name: &String) -> Option<Value> {
        let inner = &self.0.borrow();
        if let Some(value) = inner.locals.get(name) {
            return match value {
                initialized @ Some(_) => initialized.clone(),
                None => unreachable!("ERROR: Uninitialized closure variable: {}", name),
            };
        }
        if let Some(parent) = &inner.parent {
            return parent.get(name);
        }
        unreachable!("ERROR: Couldn't find closure variable: {}", name)
    }

    /// Returns true if it found a closure in which to set the variable,
    /// false if not.
    pub fn try_set(&self, name: String, value: Value) -> bool {
        let inner = &mut self.0.borrow_mut();
        if let Some(exists) = inner.locals.get_mut(&name) {
            *exists = Some(value);
            return true;
        }
        // If it's static then we can create new locals at will.
        // if inner.is_static {
        //     inner.locals.insert(name, Some(value));
        //     return true;
        // }
        if let Some(parent) = &inner.parent {
            // If we found a parent with this name and were able to set the
            // value then all is good.
            if parent.set_as_parent(name.clone(), value) {
                return true;
            }
        }
        return false;
    }

    // Recursive call to set in parent closures. Returns true if it found
    // a local and set, false if not.
    fn set_as_parent(&self, name: String, value: Value) -> bool {
        let inner = &mut self.0.borrow_mut();
        if inner.locals.contains_key(&name) {
            inner.locals.insert(name.clone(), Some(value));
            return true;
        }
        if let Some(parent) = &inner.parent {
            parent.set_as_parent(name, value)
        } else {
            false
        }
    }
}

// An action for the VM to do.
pub enum Action {
    Call(Frame),
    Return(Value),
}

pub enum Frame {
    Bytecode(BytecodeFrame),
}

impl FrameApi for Frame {
    fn run(&mut self) -> Action {
        match self {
            Frame::Bytecode(frame) => frame.run(),
        }
    }

    fn get_lexical(&self, name: &String) -> Value {
        match self {
            Frame::Bytecode(frame) => frame.get_lexical(name),
        }
    }

    fn set_lexical(&mut self, name: &String, value: Value) {
        match self {
            Frame::Bytecode(frame) => frame.set_lexical(name, value),
        }
    }

    fn receive_return(&mut self, value: Value) {
        match self {
            Frame::Bytecode(frame) => frame.receive_return(value),
        }
    }
}

pub trait FrameApi {
    // Run the frame's fetch-execute loop. Will be different depending on if
    // it's a bytecode or native frame.
    fn run(&mut self) -> Action;

    fn get_lexical(&self, name: &String) -> Value;

    fn set_lexical(&mut self, name: &String, value: Value);

    /// Called before the frame resumes execution after the higher frame has
    /// returned a value.
    ///
    /// [0].run() -> Call
    ///   [1].run() -> Return(value)
    /// [0].receive_return(value)
    /// [0].run() -> ...
    fn receive_return(&mut self, value: Value);
}

// Frame evaluating a bytecode function.
//
// The first three fields should *not* be changed after the frame
// is initialized.
pub struct BytecodeFrame {
    // TODO: Replace `LoadedFunction` with an abstraction that can support
    //   specialized instruction sequences.
    function: LoadedFunction,
    bytecode: BytecodeFunction,
    /// The slots for the local variables (not bound to a closure).
    locals: Vec<Value>,
    closure: Option<Closure>,
    /// The register for the returned value to be stored in.
    return_register: Option<Reg>,
    registers: Vec<Value>,
    current_address: usize,
}

impl BytecodeFrame {
    pub fn new(function: LoadedFunction, closure: Option<Closure>) -> Self {
        let bytecode = function.bytecode();
        let registers = bytecode.registers();
        let locals = bytecode.locals();
        Self {
            function,
            bytecode,
            locals: vec![Value::Null; locals as usize],
            closure,
            return_register: None,
            registers: vec![Value::Null; registers as usize],
            current_address: 0,
        }
    }

    pub fn unit(&self) -> LoadedModule {
        self.function.module()
    }

    #[inline]
    pub fn current(&self) -> Instruction {
        self.bytecode.instruction(self.current_address)
    }

    #[inline]
    pub fn advance(&mut self) {
        self.current_address += 1;
    }

    #[inline]
    fn offset_register(index: Reg) -> usize {
        (index as usize) - 1
    }

    pub fn read_register(&self, index: Reg) -> Value {
        self.registers[BytecodeFrame::offset_register(index)].clone()
    }

    fn write_register(&mut self, index: Reg, value: Value) {
        if index == 0 {
            return;
        }
        self.registers[BytecodeFrame::offset_register(index)] = value;
    }

    pub fn get_constant<N: AsRef<str>>(&self, name: N) -> Value {
        self.function.module().get_constant(name.as_ref())
    }

    pub fn get_local(&self, index: u8) -> Value {
        self.locals[index as usize].clone()
    }

    pub fn set_local(&mut self, index: u8, value: Value) {
        self.locals[index as usize] = value;
    }

    fn get_index_of_local(&self, name: &String) -> Option<usize> {
        self.bytecode
            .locals_names()
            .iter()
            .position(|local| local == name)
    }
}

impl FrameApi for BytecodeFrame {
    fn run(&mut self) -> Action {
        loop {
            let instruction = self.current();

            match &instruction {
                Instruction::GetConstant(lval, name) => {
                    self.write_register(*lval, self.get_constant(name));
                    self.advance();
                }
                Instruction::GetLocal(lval, index) => {
                    self.write_register(*lval, self.get_local(*index));
                    self.advance();
                }
                Instruction::GetLocalLexical(lval, name) => {
                    self.write_register(*lval, self.get_lexical(name));
                    self.advance();
                }
                Instruction::SetLocal(index, rval) => {
                    self.set_local(*index, self.read_register(*rval));
                    self.advance();
                }
                Instruction::SetLocalLexical(name, rval) => {
                    self.set_lexical(name, self.read_register(*rval));
                    self.advance();
                }
                Instruction::MakeFunction(lval, id) => {
                    let function = self.unit().function(*id);
                    let closure = if function.binds_on_create() {
                        self.closure.clone()
                    } else {
                        None
                    };
                    let value = Value::from_dynamic_function(function, closure);
                    self.write_register(*lval, value);
                    self.advance();
                }
                Instruction::MakeInteger(lval, value) => {
                    self.write_register(*lval, Value::Integer(*value));
                    self.advance();
                }
                Instruction::Branch(destination) => {
                    self.current_address = *destination as usize;
                }
                Instruction::Call(lval, target, arguments) => {
                    let target = self.read_register(*target);
                    let arguments = arguments
                        .iter()
                        .map(|argument| self.read_register(*argument))
                        .collect::<Vec<Value>>();
                    match target {
                        Value::DynamicFunction(dynamic_function) => {
                            // Save the return register for when the VM calls `receive_return`.
                            self.return_register = Some(*lval);
                            // TODO: Make `CallTarget` able to do specialization.
                            let function = dynamic_function.call_target.function;
                            let parent_closure = dynamic_function.closure;
                            let closure = if function.binds_on_call() {
                                let bindings = function.bindings();
                                let maybe_bindings = if !bindings.is_empty() {
                                    Some(bindings)
                                } else {
                                    None
                                };
                                Some(Closure::new(maybe_bindings, parent_closure))
                            } else {
                                None
                            };
                            let frame = Frame::Bytecode(BytecodeFrame::new(function, closure));
                            // Be at the next instruction when control flow returns to us.
                            self.advance();
                            return Action::Call(frame);
                        }
                        Value::NativeFunction(native_function) => {
                            let result = native_function.call(arguments);
                            self.write_register(*lval, result);
                            self.advance();
                        }
                        _ => panic!("Cannot call"),
                    }
                }
                Instruction::Return(rval) => {
                    let value = self.read_register(*rval);
                    return Action::Return(value);
                }
                Instruction::ReturnNull => {
                    return Action::Return(Value::Null);
                }
            }
        }
    }

    fn get_lexical(&self, name: &String) -> Value {
        if let Some(index) = self.get_index_of_local(name) {
            return self.locals[index].clone();
        }
        if let Some(closure) = &self.closure {
            return closure.get(name).expect(&format!("Not found: {}", name));
        }
        panic!("Not found: {}", name)
    }

    fn set_lexical(&mut self, name: &String, value: Value) {
        if let Some(closure) = &self.closure {
            if closure.try_set(name.clone(), value.clone()) {
                return;
            }
        }
        if let Some(index) = self.get_index_of_local(name) {
            self.locals[index] = value;
        } else {
            panic!("Not found: {}\nFrame: {:?}", name, self)
        }
    }

    fn receive_return(&mut self, value: Value) {
        let return_register = self.return_register.expect("Return register not set");
        self.write_register(return_register, value);
        self.return_register = None;
    }
}

impl Debug for BytecodeFrame {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        write!(
            f,
            "BytecodeFrame {{ locals: {:?}, closure: {:?} }}",
            self.locals.len(),
            self.closure
        )
    }
}

impl Debug for Closure {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        let inner = &*self.0.borrow();
        let locals = inner
            .locals
            .keys()
            .map(|k| k.to_owned())
            .collect::<Vec<String>>();
        write!(
            f,
            "Closure {{ locals: {:?}, parent: {:?}, is_static: {:?} }}",
            locals, inner.parent, inner.is_static
        )
    }
}
<|endoftext|>


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

#[macro_use]
mod macros;
pub(crate) mod actor;
mod download;
mod error;
mod idle;
pub(crate) mod install;
mod park;
mod poll;
mod prepare_download;
mod probe;
mod reboot;
mod transition;

use self::{
    download::Download,
    error::Error,
    idle::Idle,
    install::Install,
    park::Park,
    poll::Poll,
    prepare_download::PrepareDownload,
    probe::{Probe, ServerAddress},
    reboot::Reboot,
};
use crate::{firmware::Metadata, http_api, runtime_settings::RuntimeSettings, settings::Settings};
use actix::{Actor, System};
use futures::future::Future;

trait StateChangeImpl {
    fn handle(self, shared_state: &mut SharedState) -> Result<StateMachine, failure::Error>;
    fn name(&self) -> &'static str;

    fn handle_download_abort(&self) -> actor::download_abort::Response {
        actor::download_abort::Response::InvalidState
    }

    fn handle_trigger_probe(&self) -> actor::probe::Response {
        actor::probe::Response::InvalidState(self.name().to_owned())
    }
}

trait TransitionCallback: StateChangeImpl + Into<State<Idle>> {}

trait ProgressReporter: TransitionCallback {
    fn package_uid(&self) -> String;
    fn report_enter_state_name(&self) -> &'static str;
    fn report_leave_state_name(&self) -> &'static str;
}

#[derive(Debug, PartialEq)]
struct State<S>(S)
where
    State<S>: StateChangeImpl;

#[derive(Debug, PartialEq)]
struct SharedState {
    settings: Settings,
    runtime_settings: RuntimeSettings,
    firmware: Metadata,
}

#[derive(Debug, PartialEq)]
enum StateMachine {
    Park(State<Park>),
    Idle(State<Idle>),
    Poll(State<Poll>),
    Probe(State<Probe>),
    PrepareDownload(State<PrepareDownload>),
    Download(State<Download>),
    Install(State<Install>),
    Reboot(State<Reboot>),
    Error(State<Error>),
}

impl<S> State<S>
where
    State<S>: TransitionCallback + ProgressReporter,
{
    fn handle_with_callback_and_report_progress(
        self,
        shared_state: &mut SharedState,
    ) -> Result<StateMachine, failure::Error> {
        use transition::{state_change_callback, Transition};

        let transition =
            state_change_callback(&shared_state.settings.firmware.metadata_path, self.name())?;

        match transition {
            Transition::Continue => Ok(self.handle_and_report_progress(shared_state)?),
            Transition::Cancel => Ok(StateMachine::Idle(self.into())),
        }
    }
}

impl<S> State<S>
where
    State<S>: ProgressReporter,
{
    fn handle_and_report_progress(
        self,
        shared_state: &mut SharedState,
    ) -> Result<StateMachine, failure::Error> {
        let server = &shared_state.settings.network.server_address.clone();
        let firmware = &shared_state.firmware.clone();
        let package_uid = &self.package_uid();
        let enter_state = self.report_enter_state_name();
        let leave_state = self.report_leave_state_name();

        let report = |state, previous_state, error_message, current_log| {
            crate::client::Api::new(&server).report(
                state,
                firmware,
                package_uid,
                previous_state,
                error_message,
                current_log,
            )
        };

        report(enter_state, None, None, None)?;
        self.handle(shared_state)
            .and_then(|state| {
                report(leave_state, None, None, None)?;
                Ok(state)
            })
            .or_else(|e| {
                report(
                    "error",
                    Some(enter_state),
                    Some(e.to_string()),
                    Some(crate::logger::buffer().lock().unwrap().to_string()),
                )?;
                Err(e)
            })
    }
}

impl StateMachine {
    fn new() -> Self {
        StateMachine::Idle(State(Idle {}))
    }

    fn move_to_next_state(self, shared_state: &mut SharedState) -> Result<Self, failure::Error> {
        match self {
            StateMachine::Error(s) => Ok(s.handle(shared_state)?),
            StateMachine::Park(s) => Ok(s.handle(shared_state)?),
            StateMachine::Idle(s) => Ok(s.handle(shared_state)?),
            StateMachine::Poll(s) => Ok(s.handle(shared_state)?),
            StateMachine::Probe(s) => Ok(s.handle(shared_state)?),
            StateMachine::PrepareDownload(s) => Ok(s.handle(shared_state)?),
            StateMachine::Download(s) => {
                Ok(s.handle_with_callback_and_report_progress(shared_state)?)
            }
            StateMachine::Install(s) => {
                Ok(s.handle_with_callback_and_report_progress(shared_state)?)
            }
            StateMachine::Reboot(s) => {
                Ok(s.handle_with_callback_and_report_progress(shared_state)?)
            }
        }
    }

    fn for_any_state<F, A>(&self, f: F) -> A
    where
        F: Fn(&dyn StateChangeImpl) -> A,
    {
        match self {
            StateMachine::Error(s) => f(s),
            StateMachine::Park(s) => f(s),
            StateMachine::Idle(s) => f(s),
            StateMachine::Poll(s) => f(s),
            StateMachine::Probe(s) => f(s),
            StateMachine::PrepareDownload(s) => f(s),
            StateMachine::Download(s) => f(s),
            StateMachine::Install(s) => f(s),
            StateMachine::Reboot(s) => f(s),
        }
    }
}

/// Runs the state machine up to completion handling all procing
/// states without extra manual work.
///
/// It supports following states, and transitions, as shown in the
/// below diagram:
///
/// ```text
///           .--------------.
///           |              v
/// Park <- Idle -> Poll -> Probe -> Download -> Install -> Reboot
///           ^      ^        '          '          '
///           '      '        '          '          '
///           '      `--------'          '          '
///           `---------------'          '          '
///           `--------------------------'          '
///           `-------------------------------------'
/// ```
///
/// # Example
/// ```
/// # extern crate failure;
/// # extern crate updatehub;
/// # use failure;
/// # fn run() -> Result<(), failure::Error> {
/// use updatehub;
///
/// updatehub::logger::init(0);
/// let settings = updatehub::Settings::load()?;
/// updatehub::run(settings)?;
/// # Ok(())
/// # }
/// ```
pub fn run(settings: Settings) -> Result<(), failure::Error> {
    let mut runtime_settings = RuntimeSettings::new().load(&settings.storage.runtime_settings)?;
    if !settings.storage.read_only {
        runtime_settings.enable_persistency();
    }

    let firmware = Metadata::from_path(&settings.firmware.metadata_path)?;
    // set_shared_state!(settings, runtime_settings, firmware);

    let agent_machine = actor::Machine::new(
        StateMachine::new(),
        SharedState {
            settings,
            runtime_settings,
            firmware,
        },
    );

    System::run(|| {
        let machine = agent_machine.start();
        let api = machine.clone();
        actix_web::HttpServer::new(move || {
            actix_web::App::new().configure(|cfg| http_api::API::configure(cfg, api.clone()))
        })
        .bind("localhost:8080")
        .unwrap()
        .start();

        // Iterate over the state machine.
        loop {
            machine
                .send(actor::Step)
                .wait()
                .expect("Failed to communicate with actor");
        }
    })?;

    unreachable!("actix System has stopped");
}
<commit_msg>updatehub: Step message sent for arbiter so wait don't block the main thread<commit_after>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

#[macro_use]
mod macros;
pub(crate) mod actor;
mod download;
mod error;
mod idle;
pub(crate) mod install;
mod park;
mod poll;
mod prepare_download;
mod probe;
mod reboot;
mod transition;

use self::{
    download::Download,
    error::Error,
    idle::Idle,
    install::Install,
    park::Park,
    poll::Poll,
    prepare_download::PrepareDownload,
    probe::{Probe, ServerAddress},
    reboot::Reboot,
};
use crate::{firmware::Metadata, http_api, runtime_settings::RuntimeSettings, settings::Settings};
use actix::{Actor, System};
use futures::future::Future;
use slog_scope::error;

trait StateChangeImpl {
    fn handle(self, shared_state: &mut SharedState) -> Result<StateMachine, failure::Error>;
    fn name(&self) -> &'static str;

    fn handle_download_abort(&self) -> actor::download_abort::Response {
        actor::download_abort::Response::InvalidState
    }

    fn handle_trigger_probe(&self) -> actor::probe::Response {
        actor::probe::Response::InvalidState(self.name().to_owned())
    }
}

trait TransitionCallback: StateChangeImpl + Into<State<Idle>> {}

trait ProgressReporter: TransitionCallback {
    fn package_uid(&self) -> String;
    fn report_enter_state_name(&self) -> &'static str;
    fn report_leave_state_name(&self) -> &'static str;
}

#[derive(Debug, PartialEq)]
struct State<S>(S)
where
    State<S>: StateChangeImpl;

#[derive(Debug, PartialEq)]
struct SharedState {
    settings: Settings,
    runtime_settings: RuntimeSettings,
    firmware: Metadata,
}

#[derive(Debug, PartialEq)]
enum StateMachine {
    Park(State<Park>),
    Idle(State<Idle>),
    Poll(State<Poll>),
    Probe(State<Probe>),
    PrepareDownload(State<PrepareDownload>),
    Download(State<Download>),
    Install(State<Install>),
    Reboot(State<Reboot>),
    Error(State<Error>),
}

impl<S> State<S>
where
    State<S>: TransitionCallback + ProgressReporter,
{
    fn handle_with_callback_and_report_progress(
        self,
        shared_state: &mut SharedState,
    ) -> Result<StateMachine, failure::Error> {
        use transition::{state_change_callback, Transition};

        let transition =
            state_change_callback(&shared_state.settings.firmware.metadata_path, self.name())?;

        match transition {
            Transition::Continue => Ok(self.handle_and_report_progress(shared_state)?),
            Transition::Cancel => Ok(StateMachine::Idle(self.into())),
        }
    }
}

impl<S> State<S>
where
    State<S>: ProgressReporter,
{
    fn handle_and_report_progress(
        self,
        shared_state: &mut SharedState,
    ) -> Result<StateMachine, failure::Error> {
        let server = &shared_state.settings.network.server_address.clone();
        let firmware = &shared_state.firmware.clone();
        let package_uid = &self.package_uid();
        let enter_state = self.report_enter_state_name();
        let leave_state = self.report_leave_state_name();

        let report = |state, previous_state, error_message, current_log| {
            crate::client::Api::new(&server).report(
                state,
                firmware,
                package_uid,
                previous_state,
                error_message,
                current_log,
            )
        };

        report(enter_state, None, None, None)?;
        self.handle(shared_state)
            .and_then(|state| {
                report(leave_state, None, None, None)?;
                Ok(state)
            })
            .or_else(|e| {
                report(
                    "error",
                    Some(enter_state),
                    Some(e.to_string()),
                    Some(crate::logger::buffer().lock().unwrap().to_string()),
                )?;
                Err(e)
            })
    }
}

impl StateMachine {
    fn new() -> Self {
        StateMachine::Idle(State(Idle {}))
    }

    fn move_to_next_state(self, shared_state: &mut SharedState) -> Result<Self, failure::Error> {
        match self {
            StateMachine::Error(s) => Ok(s.handle(shared_state)?),
            StateMachine::Park(s) => Ok(s.handle(shared_state)?),
            StateMachine::Idle(s) => Ok(s.handle(shared_state)?),
            StateMachine::Poll(s) => Ok(s.handle(shared_state)?),
            StateMachine::Probe(s) => Ok(s.handle(shared_state)?),
            StateMachine::PrepareDownload(s) => Ok(s.handle(shared_state)?),
            StateMachine::Download(s) => {
                Ok(s.handle_with_callback_and_report_progress(shared_state)?)
            }
            StateMachine::Install(s) => {
                Ok(s.handle_with_callback_and_report_progress(shared_state)?)
            }
            StateMachine::Reboot(s) => {
                Ok(s.handle_with_callback_and_report_progress(shared_state)?)
            }
        }
    }

    fn for_any_state<F, A>(&self, f: F) -> A
    where
        F: Fn(&dyn StateChangeImpl) -> A,
    {
        match self {
            StateMachine::Error(s) => f(s),
            StateMachine::Park(s) => f(s),
            StateMachine::Idle(s) => f(s),
            StateMachine::Poll(s) => f(s),
            StateMachine::Probe(s) => f(s),
            StateMachine::PrepareDownload(s) => f(s),
            StateMachine::Download(s) => f(s),
            StateMachine::Install(s) => f(s),
            StateMachine::Reboot(s) => f(s),
        }
    }
}

/// Runs the state machine up to completion handling all procing
/// states without extra manual work.
///
/// It supports following states, and transitions, as shown in the
/// below diagram:
///
/// ```text
///           .--------------.
///           |              v
/// Park <- Idle -> Poll -> Probe -> Download -> Install -> Reboot
///           ^      ^        '          '          '
///           '      '        '          '          '
///           '      `--------'          '          '
///           `---------------'          '          '
///           `--------------------------'          '
///           `-------------------------------------'
/// ```
///
/// # Example
/// ```
/// # extern crate failure;
/// # extern crate updatehub;
/// # use failure;
/// # fn run() -> Result<(), failure::Error> {
/// use updatehub;
///
/// updatehub::logger::init(0);
/// let settings = updatehub::Settings::load()?;
/// updatehub::run(settings)?;
/// # Ok(())
/// # }
/// ```
pub fn run(settings: Settings) -> Result<(), failure::Error> {
    let mut runtime_settings = RuntimeSettings::new().load(&settings.storage.runtime_settings)?;
    if !settings.storage.read_only {
        runtime_settings.enable_persistency();
    }

    let firmware = Metadata::from_path(&settings.firmware.metadata_path)?;
    // set_shared_state!(settings, runtime_settings, firmware);

    let agent_machine = actor::Machine::new(
        StateMachine::new(),
        SharedState {
            settings,
            runtime_settings,
            firmware,
        },
    );

    System::run(|| {
        let machine = agent_machine.start();
        let api = machine.clone();
        actix_web::HttpServer::new(move || {
            actix_web::App::new().configure(|cfg| http_api::API::configure(cfg, api.clone()))
        })
        .bind("localhost:8080")
        .unwrap()
        .start();

        // Iterate over the state machine on a separated thread.
        actix::Arbiter::new().exec_fn(move || {
            while machine.connected() {
                if let Err(e) = machine.send(actor::Step).wait() {
                    error!("Communication to actor failed: {:?}", e);
                }
            }
        });
    })?;

    unreachable!("actix System has stopped");
}
<|endoftext|>


<commit_before>use std::fmt;
use std::path::Path;
use geometry::Size;
use terminal::config::{ConfigPart, escape_config_string};

pub fn bitmap<T: AsRef<Path>>(origin: Origin, path: T) -> BitmapFont {
	BitmapFont{
		origin: origin,
		path: path.as_ref().to_str().unwrap().to_string(),
		resize: None,
		resize_filter: None,
		resize_mode: None,
		raw_size: None,
		codepage: None,
		align: None,
		spacing: None,
	}
}

pub fn true_type<T: AsRef<Path>>(origin: Origin, path: T, tile_size: Size) {}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum Origin {
	Default,
	Offset(char),
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum RasterizationMode {
	Monochrome,
	Normal,
	Lcd,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum ResizeFilter {
	Nearest,
	Bilinear,
	Bicubic,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum ResizeMode {
	Stretch,
	Fit,
	Crop,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum Align {
	Center,
	TopLeft,
	BottomLeft,
	TopRight,
	BottomRight,
}

// What's the format for this?
// ref: http://foo.wyrd.name/en:bearlibterminal:reference:configuration#font_and_tileset_management
// pub enum Background {
// 	Auto,
// 	Color(Color),
// }

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct BitmapFont {
	origin       : Origin,
	path         : String,
	resize       : Option<Size>,
	resize_filter: Option<ResizeFilter>,
	resize_mode  : Option<ResizeMode>,
	raw_size     : Option<Size>,
	codepage     : Option<String>,
	align        : Option<Align>,
	spacing      : Option<Size>,
//	transparent  : Option<Background>,
}

impl BitmapFont {
	builder!(resize, Size);
	builder!(resize_filter, ResizeFilter);
	builder!(resize_mode, ResizeMode);
	builder!(raw_size, Size);
	builder!(codepage, String);
	builder!(align, Align);
	builder!(spacing, Size);
}

impl ConfigPart for BitmapFont {
	fn to_config_str(&self) -> String {
		format!("{}: {}{}{}{}{}{}{}{};", self.origin, escape_config_string(&self.path),
			match self.resize {
				None             => "".to_string(),
				Some(ref resize) => format!(", resize={}", resize),
			},
			match self.resize_filter {
				None                    => "".to_string(),
				Some(ref resize_filter) => format!(", resize-filter={}", resize_filter),
			},
			match self.resize_mode {
				None                  => "".to_string(),
				Some(ref resize_mode) => format!(", resize-mode={}", resize_mode),
			},
			match self.raw_size {
				None               => "".to_string(),
				Some(ref raw_size) => format!(", raw-size={}", raw_size),
			},
			match self.codepage {
				None               => "".to_string(),
				Some(ref codepage) => format!(", codepage={}", codepage),
			},
			match self.align {
				None            => "".to_string(),
				Some(ref align) => format!(", align={}", align),
			},
			match self.spacing {
				None              => "".to_string(),
				Some(ref spacing) => format!(", spacing={}", spacing),
			}
		)
	}
}

impl fmt::Display for Origin {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		match self {
			&Origin::Default   => formatter.write_str("font"),
			&Origin::Offset(o) => formatter.write_str(&*&format!("0x{:X}", o as i32)),
		}
	}
}

impl fmt::Display for RasterizationMode {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		match self {
			&RasterizationMode::Monochrome => formatter.write_str("monochrome"),
			&RasterizationMode::Normal     => formatter.write_str("normal"),
			&RasterizationMode::Lcd        => formatter.write_str("lcd"),
		}
	}
}

impl fmt::Display for ResizeFilter {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		match self {
			&ResizeFilter::Nearest  => formatter.write_str("nearest"),
			&ResizeFilter::Bilinear => formatter.write_str("bilinear"),
			&ResizeFilter::Bicubic  => formatter.write_str("bicubic"),
		}
	}
}

impl fmt::Display for ResizeMode {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		match self {
			&ResizeMode::Stretch => formatter.write_str("stretch"),
			&ResizeMode::Fit     => formatter.write_str("fit"),
			&ResizeMode::Crop    => formatter.write_str("crop"),
		}
	}
}

impl fmt::Display for Align {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		match self {
			&Align::Center      => formatter.write_str("center"),
			&Align::TopLeft     => formatter.write_str("top-left"),
			&Align::BottomLeft  => formatter.write_str("bottom-left"),
			&Align::TopRight    => formatter.write_str("top-right"),
			&Align::BottomRight => formatter.write_str("bottom-right"),
		}
	}
}

macro_rules! builder {
	($name:ident, $argt:ty) => {
		pub fn $name(mut self, $name: $argt) -> Self {
			self.$name = Some($name);
			self
		}
	};
}
<commit_msg>Implement TrueType fonts. Add docs for Font properties<commit_after>use std::fmt;
use std::path::Path;
use geometry::Size;
use terminal::config::{ConfigPart, escape_config_string};

pub fn bitmap<T: AsRef<Path>>(origin: Origin, path: T) -> BitmapFont {
	BitmapFont{
		origin: origin,
		path: path.as_ref().to_str().unwrap().to_string(),
		size: None,
		resize: None,
		resize_filter: None,
		resize_mode: None,
		raw_size: None,
		codepage: None,
		align: None,
		spacing: None,
	}
}

pub fn true_type<T: AsRef<Path>>(origin: Origin, path: T, tile_size: Size) -> TrueTypeFont {
	TrueTypeFont{
		origin: origin,
		path: path.as_ref().to_str().unwrap().to_string(),
		size: tile_size,
		size_reference: None,
		mode: None,
		codepage: None,
		align: None,
		spacing: None,
	}
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum Origin {
	Default,
	Offset(char),
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum RasterizationMode {
	Monochrome,
	Normal,
	Lcd,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum ResizeFilter {
	Nearest,
	Bilinear,
	Bicubic,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum ResizeMode {
	Stretch,
	Fit,
	Crop,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum Align {
	Center,
	TopLeft,
	BottomLeft,
	TopRight,
	BottomRight,
}

// What's the format for this?
// ref: http://foo.wyrd.name/en:bearlibterminal:reference:configuration#font_and_tileset_management
// pub enum Background {
// 	Auto,
// 	Color(Color),
// }

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct BitmapFont {
	origin       : Origin,
	path         : String,
	size         : Option<Size>,
	resize       : Option<Size>,
	resize_filter: Option<ResizeFilter>,
	resize_mode  : Option<ResizeMode>,
	raw_size     : Option<Size>,
	codepage     : Option<String>,
	align        : Option<Align>,
	spacing      : Option<Size>,
//	transparent  : Option<Background>,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct TrueTypeFont {
	origin        : Origin,
	path          : String,
	size          : Size,
	size_reference: Option<char>,
	mode          : Option<RasterizationMode>,
	codepage      : Option<String>,
	align         : Option<Align>,
	spacing       : Option<Size>,
}

/// For all functions consult the corresponding attributes in
/// [the official docs](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#font_and_tileset_management).
impl BitmapFont {
	/// The size of a single tile in the tileset.
	pub fn size         (mut self, size: Size)                  -> Self {self.size          = Some(size)         ; self}
	/// The size to resize the image to.
	pub fn resize       (mut self, resize: Size)                -> Self {self.resize        = Some(resize)       ; self}
	/// How to resize the image.
	///
	/// Default: `ResizeFilter::Bilinear`.
	pub fn resize_filter(mut self, resize_filter: ResizeFilter) -> Self {self.resize_filter = Some(resize_filter); self}
	/// Resize aspect method.
	///
	/// Default: `ResizeMode::Stretch`.
	pub fn resize_mode  (mut self, resize_mode: ResizeMode)     -> Self {self.resize_mode   = Some(resize_mode)  ; self}
	/// Raw memory size, `size` if not specified.
	pub fn raw_size     (mut self, raw_size: Size)              -> Self {self.raw_size      = Some(raw_size)     ; self}
	/// Tileset's codepage.
	///
	/// Default: `"ascii"`.
	pub fn codepage     (mut self, codepage: String)            -> Self {self.codepage      = Some(codepage)     ; self}
	/// How tiles are to be aligned.
	///
	/// Default: `Align::Center`.
	pub fn align        (mut self, align: Align)                -> Self {self.align         = Some(align)        ; self}
	/// Tile alignment area \[cells\].
	///
	/// Default: `1x1`.
	pub fn spacing      (mut self, spacing: Size)               -> Self {self.spacing       = Some(spacing)      ; self}
}

/// For all functions consult the corresponding attributes in
/// [the official docs](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#font_and_tileset_management).
impl TrueTypeFont {
	/// Character used for size probing.
	///
	/// Default: `'@'`.
	pub fn size_reference(mut self, size_reference: char)    -> Self {self.size_reference = Some(size_reference); self}
	/// Rasterization mode. Note: `RasterizationMode::Lcd` forces opaque black background.
	///
	/// Default: `RasterizationMode::Normal`.
	pub fn mode          (mut self, mode: RasterizationMode) -> Self {self.mode           = Some(mode)          ; self}
	/// Reverse codepage for loading symbols.
	pub fn codepage      (mut self, codepage: String)        -> Self {self.codepage       = Some(codepage)      ; self}
	/// How tiles are to be aligned.
	///
	/// Default: `Align::Center`.
	pub fn align         (mut self, align: Align)            -> Self {self.align          = Some(align)         ; self}
	/// Tile alignment area \[cells\].
	///
	/// Default: `1x1`.
	pub fn spacing       (mut self, spacing: Size)           -> Self {self.spacing        = Some(spacing)       ; self}
}

impl ConfigPart for BitmapFont {
	fn to_config_str(&self) -> String {
		format!("{}: {}{}{}{}{}{}{}{};", self.origin, escape_config_string(&self.path),
			match self.resize {
				None             => "".to_string(),
				Some(ref resize) => format!(", resize={}", resize),
			},
			match self.resize_filter {
				None                    => "".to_string(),
				Some(ref resize_filter) => format!(", resize-filter={}", resize_filter),
			},
			match self.resize_mode {
				None                  => "".to_string(),
				Some(ref resize_mode) => format!(", resize-mode={}", resize_mode),
			},
			match self.raw_size {
				None               => "".to_string(),
				Some(ref raw_size) => format!(", raw-size={}", raw_size),
			},
			match self.codepage {
				None               => "".to_string(),
				Some(ref codepage) => format!(", codepage={}", escape_config_string(codepage)),
			},
			match self.align {
				None            => "".to_string(),
				Some(ref align) => format!(", align={}", align),
			},
			match self.spacing {
				None              => "".to_string(),
				Some(ref spacing) => format!(", spacing={}", spacing),
			}
		)
	}
}

impl ConfigPart for TrueTypeFont {
	fn to_config_str(&self) -> String {
		format!("{}: {}, size={}{}{}{}{}{};", self.origin, escape_config_string(&self.path), self.size,
			match self.size_reference {
				None                     => "".to_string(),
				Some(ref size_reference) => format!(", size-reference=0x{:X}", *size_reference as i32),
			},
			match self.mode {
				None           => "".to_string(),
				Some(ref mode) => format!(", mode={}", mode),
			},
			match self.codepage {
				None               => "".to_string(),
				Some(ref codepage) => format!(", codepage={}", escape_config_string(codepage)),
			},
			match self.align {
				None            => "".to_string(),
				Some(ref align) => format!(", align={}", align),
			},
			match self.spacing {
				None              => "".to_string(),
				Some(ref spacing) => format!(", spacing={}", spacing),
			}
		)
	}
}

impl fmt::Display for Origin {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		match self {
			&Origin::Default   => formatter.write_str("font"),
			&Origin::Offset(o) => formatter.write_str(&*&format!("0x{:X}", o as i32)),
		}
	}
}

impl fmt::Display for RasterizationMode {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		match self {
			&RasterizationMode::Monochrome => formatter.write_str("monochrome"),
			&RasterizationMode::Normal     => formatter.write_str("normal"),
			&RasterizationMode::Lcd        => formatter.write_str("lcd"),
		}
	}
}

impl fmt::Display for ResizeFilter {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		match self {
			&ResizeFilter::Nearest  => formatter.write_str("nearest"),
			&ResizeFilter::Bilinear => formatter.write_str("bilinear"),
			&ResizeFilter::Bicubic  => formatter.write_str("bicubic"),
		}
	}
}

impl fmt::Display for ResizeMode {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		match self {
			&ResizeMode::Stretch => formatter.write_str("stretch"),
			&ResizeMode::Fit     => formatter.write_str("fit"),
			&ResizeMode::Crop    => formatter.write_str("crop"),
		}
	}
}

impl fmt::Display for Align {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		match self {
			&Align::Center      => formatter.write_str("center"),
			&Align::TopLeft     => formatter.write_str("top-left"),
			&Align::BottomLeft  => formatter.write_str("bottom-left"),
			&Align::TopRight    => formatter.write_str("top-right"),
			&Align::BottomRight => formatter.write_str("bottom-right"),
		}
	}
}
<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    data_notification::{
        DataClientRequest, DataPayload, EpochEndingLedgerInfosRequest, PendingClientResponse,
    },
    data_stream::{DataStream, DataStreamListener},
    streaming_client::{GetAllEpochEndingLedgerInfosRequest, StreamRequest},
    tests::utils::{
        create_data_client_response, create_ledger_info, MockDiemDataClient, MAX_ADVERTISED_EPOCH,
        MAX_NOTIFICATION_TIMEOUT_SECS, MIN_ADVERTISED_EPOCH,
    },
};
use claim::{assert_ge, assert_none};
use diem_data_client::{
    AdvertisedData, DataClientPayload, DataClientResponse, GlobalDataSummary, OptimalChunkSizes,
};
use diem_infallible::Mutex;
use diem_types::ledger_info::LedgerInfoWithSignatures;
use futures::{FutureExt, StreamExt};
use std::{
    cmp::min,
    sync::{atomic::AtomicU64, Arc},
    time::Duration,
};
use storage_service_types::CompleteDataRange;
use tokio::time::timeout;

#[tokio::test]
async fn test_epoch_ending_requests() {
    // Create an epoch ending data stream
    let (mut data_stream, _) = create_epoch_ending_stream(MIN_ADVERTISED_EPOCH);

    // Initialize the data stream
    let epoch_chunk_size = 15;
    data_stream
        .initialize_data_requests(create_global_data_summary(epoch_chunk_size))
        .unwrap();

    // Verify valid client requests have been created
    let (sent_requests, _) = data_stream.get_sent_requests_and_notifications();
    for i in 0..sent_requests.as_ref().unwrap().len() {
        let sent_request = sent_requests.as_ref().unwrap().get(i).unwrap();
        let i = i as u64;

        // Verify the start and end epochs of the request
        assert_eq!(
            sent_request.lock().client_request,
            DataClientRequest::EpochEndingLedgerInfos(EpochEndingLedgerInfosRequest {
                start_epoch: MIN_ADVERTISED_EPOCH + (i * epoch_chunk_size),
                end_epoch: min(
                    MAX_ADVERTISED_EPOCH - 1,
                    MIN_ADVERTISED_EPOCH + ((i + 1) * epoch_chunk_size) - 1
                ),
            })
        );
    }
}

#[tokio::test]
async fn test_epoch_ending_notifications() {
    // Create an epoch ending data stream
    let (mut data_stream, mut stream_listener) = create_epoch_ending_stream(MIN_ADVERTISED_EPOCH);

    // Initialize the data stream
    let epoch_chunk_size = 100;
    data_stream
        .initialize_data_requests(create_global_data_summary(epoch_chunk_size))
        .unwrap();

    // Clear the pending queue and insert a response
    let pending_response = PendingClientResponse {
        client_request: DataClientRequest::EpochEndingLedgerInfos(EpochEndingLedgerInfosRequest {
            start_epoch: MIN_ADVERTISED_EPOCH,
            end_epoch: MIN_ADVERTISED_EPOCH + 1,
        }),
        client_response: Some(Ok(create_data_client_response(
            DataClientPayload::EpochEndingLedgerInfos(vec![create_ledger_info(
                0,
                MIN_ADVERTISED_EPOCH,
                true,
            )]),
        ))),
    };
    insert_response_into_pending_queue(&mut data_stream, pending_response);

    // Process the response and verify a notification is sent to the client
    data_stream
        .process_data_responses(create_global_data_summary(epoch_chunk_size))
        .unwrap();
    verify_epoch_ending_notification(
        &mut stream_listener,
        create_ledger_info(0, MIN_ADVERTISED_EPOCH, true),
    )
    .await;
}

#[tokio::test]
async fn test_stream_initialization() {
    // Create an epoch ending data stream
    let (mut data_stream, _) = create_epoch_ending_stream(MIN_ADVERTISED_EPOCH);

    // Verify the data stream is not initialized
    assert!(!data_stream.data_requests_initialized());

    // Initialize the data stream
    data_stream
        .initialize_data_requests(create_global_data_summary(100))
        .unwrap();

    // Verify the data stream is now initialized
    assert!(data_stream.data_requests_initialized());

    // Verify that client requests have been made
    let (sent_requests, _) = data_stream.get_sent_requests_and_notifications();
    assert_ne!(sent_requests.as_ref().unwrap().len(), 0);
}

#[tokio::test]
async fn test_stream_data_error() {
    // Create an epoch ending data stream
    let (mut data_stream, mut stream_listener) = create_epoch_ending_stream(MIN_ADVERTISED_EPOCH);

    // Initialize the data stream
    let global_data_summary = create_global_data_summary(100);
    data_stream
        .initialize_data_requests(global_data_summary.clone())
        .unwrap();

    // Clear the pending queue and insert an error response
    let client_request = DataClientRequest::EpochEndingLedgerInfos(EpochEndingLedgerInfosRequest {
        start_epoch: MIN_ADVERTISED_EPOCH,
        end_epoch: MIN_ADVERTISED_EPOCH + 1,
    });
    let pending_response = PendingClientResponse {
        client_request: client_request.clone(),
        client_response: Some(Err(diem_data_client::Error::DataIsUnavailable(
            "Missing data!".into(),
        ))),
    };
    insert_response_into_pending_queue(&mut data_stream, pending_response);

    // Process the responses and verify the data client request was resent to the network
    data_stream
        .process_data_responses(global_data_summary)
        .unwrap();
    assert_none!(stream_listener.select_next_some().now_or_never());
    verify_client_request_resubmitted(&mut data_stream, client_request);
}

#[tokio::test]
async fn test_stream_invalid_response() {
    // Create an epoch ending data stream
    let (mut data_stream, mut stream_listener) = create_epoch_ending_stream(MIN_ADVERTISED_EPOCH);

    // Initialize the data stream
    let global_data_summary = create_global_data_summary(100);
    data_stream
        .initialize_data_requests(global_data_summary.clone())
        .unwrap();

    // Clear the pending queue and insert a response with an invalid type
    let client_request = DataClientRequest::EpochEndingLedgerInfos(EpochEndingLedgerInfosRequest {
        start_epoch: MIN_ADVERTISED_EPOCH,
        end_epoch: MIN_ADVERTISED_EPOCH + 1,
    });
    let pending_response = PendingClientResponse {
        client_request: client_request.clone(),
        client_response: Some(Ok(DataClientResponse {
            response_id: 0,
            response_payload: DataClientPayload::NumberOfAccountStates(10),
        })),
    };
    insert_response_into_pending_queue(&mut data_stream, pending_response);

    // Process the responses and verify the data client request was resent to the network
    data_stream
        .process_data_responses(global_data_summary)
        .unwrap();
    assert_none!(stream_listener.select_next_some().now_or_never());
    verify_client_request_resubmitted(&mut data_stream, client_request);
}

#[tokio::test]
async fn test_stream_out_of_order_responses() {
    // Create an epoch ending data stream
    let (mut data_stream, mut stream_listener) = create_epoch_ending_stream(MIN_ADVERTISED_EPOCH);

    // Initialize the data stream
    let global_data_summary = create_global_data_summary(1);
    data_stream
        .initialize_data_requests(global_data_summary.clone())
        .unwrap();

    // Verify at least three requests have been made
    let (sent_requests, _) = data_stream.get_sent_requests_and_notifications();
    assert_ge!(sent_requests.as_ref().unwrap().len(), 3);

    // Set a response for the second request and verify no notifications
    set_epoch_ending_response_in_queue(&mut data_stream, 1);
    data_stream
        .process_data_responses(global_data_summary.clone())
        .unwrap();
    assert_none!(stream_listener.select_next_some().now_or_never());

    // Set a response for the first request and verify two notifications
    set_epoch_ending_response_in_queue(&mut data_stream, 0);
    data_stream
        .process_data_responses(global_data_summary.clone())
        .unwrap();
    for _ in 0..2 {
        verify_epoch_ending_notification(
            &mut stream_listener,
            create_ledger_info(0, MIN_ADVERTISED_EPOCH, true),
        )
        .await;
    }
    assert_none!(stream_listener.select_next_some().now_or_never());

    // Set the response for the first and third request and verify one notification sent
    set_epoch_ending_response_in_queue(&mut data_stream, 0);
    set_epoch_ending_response_in_queue(&mut data_stream, 2);
    data_stream
        .process_data_responses(global_data_summary.clone())
        .unwrap();
    verify_epoch_ending_notification(
        &mut stream_listener,
        create_ledger_info(0, MIN_ADVERTISED_EPOCH, true),
    )
    .await;
    assert_none!(stream_listener.select_next_some().now_or_never());

    // Set the response for the first and third request and verify three notifications sent
    set_epoch_ending_response_in_queue(&mut data_stream, 0);
    set_epoch_ending_response_in_queue(&mut data_stream, 2);
    data_stream
        .process_data_responses(global_data_summary.clone())
        .unwrap();
    for _ in 0..3 {
        verify_epoch_ending_notification(
            &mut stream_listener,
            create_ledger_info(0, MIN_ADVERTISED_EPOCH, true),
        )
        .await;
    }
    assert_none!(stream_listener.select_next_some().now_or_never());
}

/// Creates an epoch ending stream starting at `start_epoch`
fn create_epoch_ending_stream(
    start_epoch: u64,
) -> (DataStream<MockDiemDataClient>, DataStreamListener) {
    // Create an epoch ending stream request
    let stream_request =
        StreamRequest::GetAllEpochEndingLedgerInfos(GetAllEpochEndingLedgerInfosRequest {
            start_epoch,
        });

    // Create an advertised data containing only epoch ending ledger infos
    let advertised_data = AdvertisedData {
        account_states: vec![],
        epoch_ending_ledger_infos: vec![CompleteDataRange::new(
            MIN_ADVERTISED_EPOCH,
            MAX_ADVERTISED_EPOCH,
        )],
        synced_ledger_infos: vec![],
        transactions: vec![],
        transaction_outputs: vec![],
    };

    // Create a diem data client mock and notification generator
    let diem_data_client = MockDiemDataClient::new();
    let notification_generator = Arc::new(AtomicU64::new(0));

    // Return the data stream and listener pair
    DataStream::new(
        &stream_request,
        diem_data_client,
        notification_generator,
        &advertised_data,
    )
    .unwrap()
}

fn create_global_data_summary(chunk_sizes: u64) -> GlobalDataSummary {
    let mut global_data_summary = GlobalDataSummary::empty();
    global_data_summary.optimal_chunk_sizes = create_optimal_chunk_sizes(chunk_sizes);
    global_data_summary
}

fn create_optimal_chunk_sizes(chunk_sizes: u64) -> OptimalChunkSizes {
    OptimalChunkSizes {
        account_states_chunk_size: chunk_sizes,
        epoch_chunk_size: chunk_sizes,
        transaction_chunk_size: chunk_sizes,
        transaction_output_chunk_size: chunk_sizes,
    }
}

/// Sets the client response at the index in the pending queue to contain an
/// epoch ending data response.
fn set_epoch_ending_response_in_queue(
    data_stream: &mut DataStream<MockDiemDataClient>,
    index: usize,
) {
    // Set the response at the specified index
    let (sent_requests, _) = data_stream.get_sent_requests_and_notifications();
    let pending_response = sent_requests.as_mut().unwrap().get_mut(index).unwrap();
    let client_response = Some(Ok(create_data_client_response(
        DataClientPayload::EpochEndingLedgerInfos(vec![create_ledger_info(
            0,
            MIN_ADVERTISED_EPOCH,
            true,
        )]),
    )));
    pending_response.lock().client_response = client_response;
}

/// Clears the pending queue of the given data stream and inserts a single
/// response into the head of the queue.
fn insert_response_into_pending_queue(
    data_stream: &mut DataStream<MockDiemDataClient>,
    pending_response: PendingClientResponse,
) {
    // Clear the queue
    let (sent_requests, _) = data_stream.get_sent_requests_and_notifications();
    sent_requests.as_mut().unwrap().clear();

    // Insert the pending response
    let pending_response = Arc::new(Mutex::new(Box::new(pending_response)));
    sent_requests.as_mut().unwrap().push_front(pending_response);
}

/// Verifies that a client request was resubmitted (i.e., pushed to the head of the
/// sent request queue)
fn verify_client_request_resubmitted(
    data_stream: &mut DataStream<MockDiemDataClient>,
    client_request: DataClientRequest,
) {
    let (sent_requests, _) = data_stream.get_sent_requests_and_notifications();
    let pending_response = sent_requests.as_mut().unwrap().pop_front().unwrap();
    assert_eq!(pending_response.lock().client_request, client_request);
    assert_none!(pending_response.lock().client_response.as_ref());
}

/// Verifies that a single epoch ending notification is received by the
/// data listener and that it contains the `expected_ledger_info`.
async fn verify_epoch_ending_notification(
    stream_listener: &mut DataStreamListener,
    expected_ledger_info: LedgerInfoWithSignatures,
) {
    if let Ok(data_notification) = timeout(
        Duration::from_secs(MAX_NOTIFICATION_TIMEOUT_SECS),
        stream_listener.select_next_some(),
    )
    .await
    {
        if let DataPayload::EpochEndingLedgerInfos(ledger_infos) = data_notification.data_payload {
            assert_eq!(ledger_infos[0], expected_ledger_info);
        } else {
            panic!(
                "Expected an epoch ending ledger info payload, but got: {:?}",
                data_notification
            );
        }
    } else {
        panic!("Timed out waiting for a data notification!");
    }
}
<commit_msg>[State Sync] Remove duplicate unit tests from data streaming service.<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    data_notification::{
        DataClientRequest, DataPayload, EpochEndingLedgerInfosRequest, PendingClientResponse,
    },
    data_stream::{DataStream, DataStreamListener},
    streaming_client::{GetAllEpochEndingLedgerInfosRequest, StreamRequest},
    tests::utils::{
        create_data_client_response, create_ledger_info, MockDiemDataClient, MAX_ADVERTISED_EPOCH,
        MAX_NOTIFICATION_TIMEOUT_SECS, MIN_ADVERTISED_EPOCH,
    },
};
use claim::{assert_ge, assert_none};
use diem_data_client::{
    AdvertisedData, DataClientPayload, DataClientResponse, GlobalDataSummary, OptimalChunkSizes,
};
use diem_infallible::Mutex;
use diem_types::ledger_info::LedgerInfoWithSignatures;
use futures::{FutureExt, StreamExt};
use std::{
    sync::{atomic::AtomicU64, Arc},
    time::Duration,
};
use storage_service_types::CompleteDataRange;
use tokio::time::timeout;

#[tokio::test]
async fn test_stream_initialization() {
    // Create an epoch ending data stream
    let (mut data_stream, _) = create_epoch_ending_stream(MIN_ADVERTISED_EPOCH);

    // Verify the data stream is not initialized
    assert!(!data_stream.data_requests_initialized());

    // Initialize the data stream
    data_stream
        .initialize_data_requests(create_global_data_summary(100))
        .unwrap();

    // Verify the data stream is now initialized
    assert!(data_stream.data_requests_initialized());

    // Verify that client requests have been made
    let (sent_requests, _) = data_stream.get_sent_requests_and_notifications();
    assert_ne!(sent_requests.as_ref().unwrap().len(), 0);
}

#[tokio::test]
async fn test_stream_data_error() {
    // Create an epoch ending data stream
    let (mut data_stream, mut stream_listener) = create_epoch_ending_stream(MIN_ADVERTISED_EPOCH);

    // Initialize the data stream
    let global_data_summary = create_global_data_summary(100);
    data_stream
        .initialize_data_requests(global_data_summary.clone())
        .unwrap();

    // Clear the pending queue and insert an error response
    let client_request = DataClientRequest::EpochEndingLedgerInfos(EpochEndingLedgerInfosRequest {
        start_epoch: MIN_ADVERTISED_EPOCH,
        end_epoch: MIN_ADVERTISED_EPOCH + 1,
    });
    let pending_response = PendingClientResponse {
        client_request: client_request.clone(),
        client_response: Some(Err(diem_data_client::Error::DataIsUnavailable(
            "Missing data!".into(),
        ))),
    };
    insert_response_into_pending_queue(&mut data_stream, pending_response);

    // Process the responses and verify the data client request was resent to the network
    data_stream
        .process_data_responses(global_data_summary)
        .unwrap();
    assert_none!(stream_listener.select_next_some().now_or_never());
    verify_client_request_resubmitted(&mut data_stream, client_request);
}

#[tokio::test]
async fn test_stream_invalid_response() {
    // Create an epoch ending data stream
    let (mut data_stream, mut stream_listener) = create_epoch_ending_stream(MIN_ADVERTISED_EPOCH);

    // Initialize the data stream
    let global_data_summary = create_global_data_summary(100);
    data_stream
        .initialize_data_requests(global_data_summary.clone())
        .unwrap();

    // Clear the pending queue and insert a response with an invalid type
    let client_request = DataClientRequest::EpochEndingLedgerInfos(EpochEndingLedgerInfosRequest {
        start_epoch: MIN_ADVERTISED_EPOCH,
        end_epoch: MIN_ADVERTISED_EPOCH + 1,
    });
    let pending_response = PendingClientResponse {
        client_request: client_request.clone(),
        client_response: Some(Ok(DataClientResponse {
            response_id: 0,
            response_payload: DataClientPayload::NumberOfAccountStates(10),
        })),
    };
    insert_response_into_pending_queue(&mut data_stream, pending_response);

    // Process the responses and verify the data client request was resent to the network
    data_stream
        .process_data_responses(global_data_summary)
        .unwrap();
    assert_none!(stream_listener.select_next_some().now_or_never());
    verify_client_request_resubmitted(&mut data_stream, client_request);
}

#[tokio::test]
async fn test_stream_out_of_order_responses() {
    // Create an epoch ending data stream
    let (mut data_stream, mut stream_listener) = create_epoch_ending_stream(MIN_ADVERTISED_EPOCH);

    // Initialize the data stream
    let global_data_summary = create_global_data_summary(1);
    data_stream
        .initialize_data_requests(global_data_summary.clone())
        .unwrap();

    // Verify at least three requests have been made
    let (sent_requests, _) = data_stream.get_sent_requests_and_notifications();
    assert_ge!(sent_requests.as_ref().unwrap().len(), 3);

    // Set a response for the second request and verify no notifications
    set_epoch_ending_response_in_queue(&mut data_stream, 1);
    data_stream
        .process_data_responses(global_data_summary.clone())
        .unwrap();
    assert_none!(stream_listener.select_next_some().now_or_never());

    // Set a response for the first request and verify two notifications
    set_epoch_ending_response_in_queue(&mut data_stream, 0);
    data_stream
        .process_data_responses(global_data_summary.clone())
        .unwrap();
    for _ in 0..2 {
        verify_epoch_ending_notification(
            &mut stream_listener,
            create_ledger_info(0, MIN_ADVERTISED_EPOCH, true),
        )
        .await;
    }
    assert_none!(stream_listener.select_next_some().now_or_never());

    // Set the response for the first and third request and verify one notification sent
    set_epoch_ending_response_in_queue(&mut data_stream, 0);
    set_epoch_ending_response_in_queue(&mut data_stream, 2);
    data_stream
        .process_data_responses(global_data_summary.clone())
        .unwrap();
    verify_epoch_ending_notification(
        &mut stream_listener,
        create_ledger_info(0, MIN_ADVERTISED_EPOCH, true),
    )
    .await;
    assert_none!(stream_listener.select_next_some().now_or_never());

    // Set the response for the first and third request and verify three notifications sent
    set_epoch_ending_response_in_queue(&mut data_stream, 0);
    set_epoch_ending_response_in_queue(&mut data_stream, 2);
    data_stream
        .process_data_responses(global_data_summary.clone())
        .unwrap();
    for _ in 0..3 {
        verify_epoch_ending_notification(
            &mut stream_listener,
            create_ledger_info(0, MIN_ADVERTISED_EPOCH, true),
        )
        .await;
    }
    assert_none!(stream_listener.select_next_some().now_or_never());
}

/// Creates an epoch ending stream starting at `start_epoch`
fn create_epoch_ending_stream(
    start_epoch: u64,
) -> (DataStream<MockDiemDataClient>, DataStreamListener) {
    // Create an epoch ending stream request
    let stream_request =
        StreamRequest::GetAllEpochEndingLedgerInfos(GetAllEpochEndingLedgerInfosRequest {
            start_epoch,
        });

    // Create an advertised data containing only epoch ending ledger infos
    let advertised_data = AdvertisedData {
        account_states: vec![],
        epoch_ending_ledger_infos: vec![CompleteDataRange::new(
            MIN_ADVERTISED_EPOCH,
            MAX_ADVERTISED_EPOCH,
        )],
        synced_ledger_infos: vec![],
        transactions: vec![],
        transaction_outputs: vec![],
    };

    // Create a diem data client mock and notification generator
    let diem_data_client = MockDiemDataClient::new();
    let notification_generator = Arc::new(AtomicU64::new(0));

    // Return the data stream and listener pair
    DataStream::new(
        &stream_request,
        diem_data_client,
        notification_generator,
        &advertised_data,
    )
    .unwrap()
}

fn create_global_data_summary(chunk_sizes: u64) -> GlobalDataSummary {
    let mut global_data_summary = GlobalDataSummary::empty();
    global_data_summary.optimal_chunk_sizes = create_optimal_chunk_sizes(chunk_sizes);
    global_data_summary
}

fn create_optimal_chunk_sizes(chunk_sizes: u64) -> OptimalChunkSizes {
    OptimalChunkSizes {
        account_states_chunk_size: chunk_sizes,
        epoch_chunk_size: chunk_sizes,
        transaction_chunk_size: chunk_sizes,
        transaction_output_chunk_size: chunk_sizes,
    }
}

/// Sets the client response at the index in the pending queue to contain an
/// epoch ending data response.
fn set_epoch_ending_response_in_queue(
    data_stream: &mut DataStream<MockDiemDataClient>,
    index: usize,
) {
    // Set the response at the specified index
    let (sent_requests, _) = data_stream.get_sent_requests_and_notifications();
    let pending_response = sent_requests.as_mut().unwrap().get_mut(index).unwrap();
    let client_response = Some(Ok(create_data_client_response(
        DataClientPayload::EpochEndingLedgerInfos(vec![create_ledger_info(
            0,
            MIN_ADVERTISED_EPOCH,
            true,
        )]),
    )));
    pending_response.lock().client_response = client_response;
}

/// Clears the pending queue of the given data stream and inserts a single
/// response into the head of the queue.
fn insert_response_into_pending_queue(
    data_stream: &mut DataStream<MockDiemDataClient>,
    pending_response: PendingClientResponse,
) {
    // Clear the queue
    let (sent_requests, _) = data_stream.get_sent_requests_and_notifications();
    sent_requests.as_mut().unwrap().clear();

    // Insert the pending response
    let pending_response = Arc::new(Mutex::new(Box::new(pending_response)));
    sent_requests.as_mut().unwrap().push_front(pending_response);
}

/// Verifies that a client request was resubmitted (i.e., pushed to the head of the
/// sent request queue)
fn verify_client_request_resubmitted(
    data_stream: &mut DataStream<MockDiemDataClient>,
    client_request: DataClientRequest,
) {
    let (sent_requests, _) = data_stream.get_sent_requests_and_notifications();
    let pending_response = sent_requests.as_mut().unwrap().pop_front().unwrap();
    assert_eq!(pending_response.lock().client_request, client_request);
    assert_none!(pending_response.lock().client_response.as_ref());
}

/// Verifies that a single epoch ending notification is received by the
/// data listener and that it contains the `expected_ledger_info`.
async fn verify_epoch_ending_notification(
    stream_listener: &mut DataStreamListener,
    expected_ledger_info: LedgerInfoWithSignatures,
) {
    if let Ok(data_notification) = timeout(
        Duration::from_secs(MAX_NOTIFICATION_TIMEOUT_SECS),
        stream_listener.select_next_some(),
    )
    .await
    {
        if let DataPayload::EpochEndingLedgerInfos(ledger_infos) = data_notification.data_payload {
            assert_eq!(ledger_infos[0], expected_ledger_info);
        } else {
            panic!(
                "Expected an epoch ending ledger info payload, but got: {:?}",
                data_notification
            );
        }
    } else {
        panic!("Timed out waiting for a data notification!");
    }
}
<|endoftext|>


<commit_before>use alloc::boxed::Box;

use collections::string::ToString;

use fs::{Resource, VecResource};

use system::error::Result;
use system::syscall::MODE_FILE;

pub fn resource() -> Result<Box<Resource>> {
    let mut string = format!("{:<6}{:<10}{}", "PATH", "SIZE (MB)", "NAME");

    for (i, disk) in unsafe { &mut *::env().disks.get() }.iter().enumerate() {
        string.push_str(&format!("{:<6}{:<10}{}\n", i, unsafe { & *disk.get() }.size()/1024/1024, unsafe { & *disk.get() }.name()));
    }

    Ok(box VecResource::new("sys:/disk".to_string(), string.into_bytes(), MODE_FILE))
}
<commit_msg>Disk size<commit_after>use alloc::boxed::Box;

use collections::string::ToString;

use fs::{Resource, VecResource};

use system::error::Result;
use system::syscall::MODE_FILE;

pub fn resource() -> Result<Box<Resource>> {
    let mut string = format!("{:<6}{:<10}{}\n", "PATH", "SIZE (MB)", "NAME");

    for (i, disk) in unsafe { &mut *::env().disks.get() }.iter().enumerate() {
        string.push_str(&format!("{:<6}{:<10}{}\n", i, unsafe { & *disk.get() }.size()/1024/1024, unsafe { & *disk.get() }.name()));
    }

    Ok(box VecResource::new("sys:/disk".to_string(), string.into_bytes(), MODE_FILE))
}
<|endoftext|>


<commit_before>// This file is part of the uutils coreutils package.
//
// (c) Jian Zeng <anonymousknight96@gmail.com>
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.

#[macro_use]
extern crate uucore;

use std::io::{stdin, Read};

use clap::App;
use uucore::encoding::Format;

pub mod base_common;

static ABOUT: &str = "
 With no FILE, or when FILE is -, read standard input.

 The data are encoded as described for the base32 alphabet in RFC
 4648. When decoding, the input may contain newlines in addition
 to the bytes of the formal base32 alphabet. Use --ignore-garbage
 to attempt to recover from any other non-alphabet bytes in the
 encoded stream.
";
static VERSION: &str = env!("CARGO_PKG_VERSION");

static BASE_CMD_PARSE_ERROR: i32 = 1;

fn usage() -> String {
    format!("{0} [OPTION]... [FILE]", uucore::execution_phrase())
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let format = Format::Base32;
    let usage = usage();
    let name = uucore::util_name();

    let config_result: Result<base_common::Config, String> =
        base_common::parse_base_cmd_args(args, name, VERSION, ABOUT, &usage);
    let config = config_result.unwrap_or_else(|s| crash!(BASE_CMD_PARSE_ERROR, "{}", s));

    // Create a reference to stdin so we can return a locked stdin from
    // parse_base_cmd_args
    let stdin_raw = stdin();
    let mut input: Box<dyn Read> = base_common::get_input(&config, &stdin_raw);

    base_common::handle_input(
        &mut input,
        format,
        config.wrap_cols,
        config.ignore_garbage,
        config.decode,
        name,
    );

    0
}

pub fn uu_app() -> App<'static, 'static> {
    base_common::base_app(uucore::util_name(), VERSION, ABOUT)
}
<commit_msg>base32: use UResult<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Jian Zeng <anonymousknight96@gmail.com>
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.

#[macro_use]
extern crate uucore;

use std::io::{stdin, Read};

use clap::App;
use uucore::{encoding::Format, error::UResult};

pub mod base_common;

static ABOUT: &str = "
 With no FILE, or when FILE is -, read standard input.

 The data are encoded as described for the base32 alphabet in RFC
 4648. When decoding, the input may contain newlines in addition
 to the bytes of the formal base32 alphabet. Use --ignore-garbage
 to attempt to recover from any other non-alphabet bytes in the
 encoded stream.
";

fn usage() -> String {
    format!("{0} [OPTION]... [FILE]", uucore::execution_phrase())
}

#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let format = Format::Base32;
    let usage = usage();

    let config: base_common::Config = base_common::parse_base_cmd_args(args, ABOUT, &usage)?;

    // Create a reference to stdin so we can return a locked stdin from
    // parse_base_cmd_args
    let stdin_raw = stdin();
    let mut input: Box<dyn Read> = base_common::get_input(&config, &stdin_raw)?;

    base_common::handle_input(
        &mut input,
        format,
        config.wrap_cols,
        config.ignore_garbage,
        config.decode,
    )
}

pub fn uu_app() -> App<'static, 'static> {
    base_common::base_app(ABOUT)
}
<|endoftext|>


<commit_before>use decoders::Image;
use imageops::OpBuffer;
use std::cmp;

pub fn demosaic_and_scale(img: &Image, minwidth: usize, minheight: usize) -> OpBuffer {
  // Calculate the resulting width/height and top-left corner after crops
  let width = (img.width as i64 - img.crops[1] - img.crops[3]) as usize;
  let height = (img.height as i64 - img.crops[0] - img.crops[2]) as usize;
  let x = img.crops[3] as usize;
  let y = img.crops[0] as usize;

  let scale = if minwidth == 0 || minheight == 0 {
    0
  } else {
    cmp::min(width / minwidth, height / minheight)
  };

  if scale <= 1 {
    full(img, x, y, width, height)
  } else {
    scaled(img, cmp::min(scale, 16), x, y, width, height) // Never go less than 1/16th demosaic
  }
}

pub fn full(img: &Image, xs: usize, ys: usize, width: usize, height: usize) -> OpBuffer {
  let mut out = OpBuffer::new(width, height, 4);
  let crop_cfa = img.cfa.shift(xs, ys);

  // First we set the colors we already have
  out.mutate_lines(&(|line: &mut [f32], row| {
    for (col, (pixout, pixin)) in line.chunks_mut(4).zip(img.data[img.width*(row+ys)+xs..].chunks(1)).enumerate() {
      let color = crop_cfa.color_at(row, col);
      pixout[color] = pixin[0] as f32;
    }
  }));

  // Now we go around the image setting the unset colors to the average of the
  // surrounding pixels
  out.mutate_lines(&(|line: &mut [f32], row| {
    for col in 0..width {
      let mut sums: [f32; 4] = [0.0;4];
      let mut counts: [u32; 4] = [0; 4];
      let color = crop_cfa.color_at(row, col);

      for y in (cmp::max(0,(row as isize)-1) as usize) .. cmp::min(height, row+2) {
        for x in (cmp::max(0,(col as isize)-1) as usize) .. cmp::min(width, col+2) {
          let c = crop_cfa.color_at(y, x);
          if c != color {
            sums[c] += img.data[(y+ys)*img.width+(x+xs)] as f32;
            counts[c] += 1;
          }
        }
      }

      for c in 0..4 {
        if c != color && counts[c] > 0 {
          line[col*4+c] = sums[c] / (counts[c] as f32);
        }
      }
    }
  }));

  out
}

pub fn scaled(img: &Image, scale: usize, xs: usize, ys: usize, width: usize, height: usize) -> OpBuffer {
  let nwidth = width/scale;
  let nheight = height/scale;
  let mut out = OpBuffer::new(nwidth, nheight, 4);
  let crop_cfa = img.cfa.shift(xs, ys);

  // Go around the image averaging every block of pixels
  out.mutate_lines(&(|line: &mut [f32], row| {
    for col in 0..nwidth {
      let mut sums: [f32; 4] = [0.0;4];
      let mut counts: [u32; 4] = [0; 4];

      for y in row*scale..(row+1)*scale {
        for x in col*scale..(col+1)*scale {
          let c = crop_cfa.color_at(y, x);
          sums[c] += img.data[(y+ys)*img.width+(x+xs)] as f32;
          counts[c] += 1;
        }
      }

      for c in 0..4 {
        if counts[c] > 0 {
          line[col*4+c] = sums[c] / (counts[c] as f32);
        }
      }
    }
  }));

  out
}
<commit_msg>demosaic: fractional scaling and fixup size calcs<commit_after>use decoders::Image;
use imageops::OpBuffer;
use std::cmp;

pub fn demosaic_and_scale(img: &Image, minwidth: usize, minheight: usize) -> OpBuffer {
  // Calculate the resulting width/height and top-left corner after crops
  let width = (img.width as i64 - img.crops[1] - img.crops[3]) as usize;
  let height = (img.height as i64 - img.crops[0] - img.crops[2]) as usize;
  let x = img.crops[3] as usize;
  let y = img.crops[0] as usize;

  let (scale, nwidth, nheight) = if minwidth == 0 || minheight == 0 {
    (0.0, width, height)
  } else {
    // Do the calculations manually to avoid off-by-one errors from floating point rounding
    let xscale = (width as f32) / (minwidth as f32);
    let yscale = (height as f32) / (minheight as f32);
    if yscale > xscale {
      (yscale, ((width as f32)/yscale) as usize, minheight)
    } else {
      (xscale, minwidth, ((height as f32)/xscale) as usize)
    }
  };

  if scale < 2.0 {
    full(img, x, y, width, height)
  } else {
    scaled(img, x, y, width, height, nwidth, nheight)
  }
}

pub fn full(img: &Image, xs: usize, ys: usize, width: usize, height: usize) -> OpBuffer {
  let mut out = OpBuffer::new(width, height, 4);
  let crop_cfa = img.cfa.shift(xs, ys);

  // First we set the colors we already have
  out.mutate_lines(&(|line: &mut [f32], row| {
    for (col, (pixout, pixin)) in line.chunks_mut(4).zip(img.data[img.width*(row+ys)+xs..].chunks(1)).enumerate() {
      let color = crop_cfa.color_at(row, col);
      pixout[color] = pixin[0] as f32;
    }
  }));

  // Now we go around the image setting the unset colors to the average of the
  // surrounding pixels
  out.mutate_lines(&(|line: &mut [f32], row| {
    for col in 0..width {
      let mut sums: [f32; 4] = [0.0;4];
      let mut counts: [u32; 4] = [0; 4];
      let color = crop_cfa.color_at(row, col);

      for y in (cmp::max(0,(row as isize)-1) as usize) .. cmp::min(height, row+2) {
        for x in (cmp::max(0,(col as isize)-1) as usize) .. cmp::min(width, col+2) {
          let c = crop_cfa.color_at(y, x);
          if c != color {
            sums[c] += img.data[(y+ys)*img.width+(x+xs)] as f32;
            counts[c] += 1;
          }
        }
      }

      for c in 0..4 {
        if c != color && counts[c] > 0 {
          line[col*4+c] = sums[c] / (counts[c] as f32);
        }
      }
    }
  }));

  out
}

pub fn scaled(img: &Image, xs: usize, ys: usize, width: usize, height: usize, nwidth: usize, nheight: usize) -> OpBuffer {
  let mut out = OpBuffer::new(nwidth, nheight, 4);
  let crop_cfa = img.cfa.shift(xs, ys);

  let rowskip = (width as f32) / (nwidth as f32);
  let colskip = (height as f32) / (nheight as f32);

  // Go around the image averaging blocks of pixels
  out.mutate_lines(&(|line: &mut [f32], row| {
    for col in 0..nwidth {
      let mut sums: [f32; 4] = [0.0;4];
      let mut counts: [f32; 4] = [0.0;4];

      let fromrow = ((row as f32)*rowskip).floor() as usize;
      let torow = cmp::min(height, (((row+1) as f32)*rowskip).ceil() as usize);
      for y in fromrow..torow {
        let fromcol = ((col as f32)*colskip).floor() as usize;
        let tocol = cmp::min(width, (((col+1) as f32)*colskip).ceil() as usize);
        for x in fromcol..tocol {
          let c = crop_cfa.color_at(y, x);
          sums[c] += img.data[(y+ys)*img.width+(x+xs)] as f32;
          counts[c] += 1.0;
        }
      }

      for c in 0..4 {
        if counts[c] > 0.0 {
          line[col*4+c] = sums[c] / (counts[c] as f32);
        }
      }
    }
  }));

  out
}
<|endoftext|>