1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use std::collections::HashMap;
use std::fmt;
use std::path::PathBuf;

use anyhow::Result;
use serde::{Deserialize, Serialize};
use thiserror::Error;

use crate::impl_serializable_error;

/// Error related to the `Emulator` trait.
#[derive(Error, Debug, Serialize, Deserialize)]
pub enum EmuError {
    #[error("Invalid argument name: {0}")]
    InvalidArgumetName(String),
    #[error("Argument name: {0} has invalid value: {1}")]
    InvalidArgumentValue(String, String),
    #[error("Start failed with cause: {0}")]
    StartFailureCause(String),
    #[error("Stop failed with cause: {0}")]
    StopFailureCause(String),
    #[error("Can't restore resource to initial state: {0}")]
    ResetError(String),
    #[error("Runtime error: {0}")]
    RuntimeError(String),
}
impl_serializable_error!(EmuError);

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum EmuValue {
    Empty,
    String(String),
    FilePath(PathBuf),
    StringList(Vec<String>),
    FilePathList(Vec<PathBuf>),
}

impl From<String> for EmuValue {
    fn from(s: String) -> Self {
        EmuValue::String(s)
    }
}

impl From<Vec<String>> for EmuValue {
    fn from(str_array: Vec<String>) -> Self {
        EmuValue::StringList(str_array)
    }
}

impl From<PathBuf> for EmuValue {
    fn from(p: PathBuf) -> Self {
        EmuValue::FilePath(p)
    }
}

impl From<Vec<PathBuf>> for EmuValue {
    fn from(path_array: Vec<PathBuf>) -> Self {
        EmuValue::FilePathList(path_array)
    }
}

impl fmt::Display for EmuValue {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            EmuValue::Empty => write!(f, " "),
            EmuValue::String(value) => write!(f, "{}", value),
            EmuValue::FilePath(value) => write!(f, "{}", value.display()),
            EmuValue::StringList(value_list) => write!(f, "{}", value_list.join(",")),
            EmuValue::FilePathList(value_list) => {
                for item in value_list.iter() {
                    write!(f, "{} ", item.display())?;
                }
                Ok(())
            }
        }
    }
}

#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
pub enum EmuState {
    Off,
    On,
    Busy,
    Error,
}

impl fmt::Display for EmuState {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            EmuState::Off => write!(f, "Off"),
            EmuState::On => write!(f, "On"),
            EmuState::Busy => write!(f, "Busy"),
            EmuState::Error => write!(f, "Error"),
        }
    }
}

/// A trait which represents a `Emulator` instance
pub trait Emulator {
    /// Simple function with return `EmuState` representing current state of Emulator instance.
    fn get_state(&self) -> Result<EmuState>;

    /// Start Emulator with provided arguments.
    /// Parameter `factory_reset` is a flag that describe if internal state
    /// and "resources" should be set to its "initial state" before applying
    /// the changes from the parameter `args`.
    /// If `factory_reset` is set to true - all internal state and "resources"
    /// will be set to its "initial state". The "initial state" for most devices
    /// means that the content is set to zero.
    /// If `factory_reset` is set to false - all internal state will be preserved
    /// form last run.
    /// Parameter `args` describe set of named flags, value and resources passed
    /// to Emulator in order to update the content of its internal state.
    /// In the context of the function below, "resources" denote files
    /// representing the state of devices, for example: EEPROM, FUSES, FLASH, SRAM...
    /// (most often binary files). Resources files sent via parameter `args`
    /// cannot be modified by the Emulator directly, the Emulator backend should have
    /// copy-on-write implementations to enable resource modification during DUT Emulation.
    /// It should be noted that the method of resources interpretation
    /// and their format depends on the backend implementing the Emulator trait.
    /// More detail about supported `args` value and its content can be found
    /// in the documentation of individual backend.
    fn start(&self, factory_reset: bool, args: &HashMap<String, EmuValue>) -> Result<()>;

    /// Stop emulator instance.
    fn stop(&self) -> Result<()>;
}