#![allow(unused_macros)]
use crate::vmerr;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use std::string::FromUtf8Error;
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct VmError {
repr: Repr,
}
impl std::error::Error for VmError {}
impl std::fmt::Display for VmError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
format!("{:?}", self).fmt(f)
}
}
impl VmError {
pub fn get_invalid_state(&self) -> Option<VmPowerState> {
match &self.repr {
Repr::Simple(ErrorKind::InvalidPowerState(x)) => Some(*x),
_ => None,
}
}
pub fn get_repr(&self) -> &Repr { &self.repr }
pub fn is_invalid_state_running(&self) -> Option<bool> {
self.get_invalid_state().map(|x| x.is_running())
}
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum Repr {
Simple(ErrorKind),
Unknown(String),
SerializeError,
IoError,
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum ErrorKind {
AuthenticationFailed,
ExecutionFailed(String),
FileError(String),
GuestAuthenticationFailed,
GuestFileNotFound,
GuestFileExists,
HostFileNotFound,
HostFileExists,
InvalidParameter(String),
InvalidPowerState(VmPowerState),
FromUtf8Error(FromUtf8Error),
NetworkAdaptorNotFound,
NetworkNotFound,
PrivilegesRequired,
ServiceIsNotRunning,
SnapshotNotFound,
SnapshotExists,
Timeout,
UnexpectedResponse(String),
UnsupportedCommand,
VmIsNotSpecified,
VmNotFound,
}
impl From<Repr> for VmError {
fn from(repr: Repr) -> Self { Self { repr } }
}
impl From<std::io::Error> for VmError {
fn from(_: std::io::Error) -> Self { vmerr!(@r Repr::IoError) }
}
impl From<serde_json::Error> for VmError {
fn from(_: serde_json::Error) -> Self { vmerr!(@r Repr::SerializeError) }
}
impl From<ErrorKind> for VmError {
fn from(e: ErrorKind) -> Self {
Self {
repr: Repr::Simple(e),
}
}
}
pub type VmResult<T> = Result<T, VmError>;
#[macro_export]
macro_rules! vmerr {
($x:expr) => {
Err($crate::types::VmError::from($x))
};
(@r $x:expr) => {
$crate::types::VmError::from($x)
};
}
macro_rules! starts_err {
($s:expr, $x:expr, $y:expr) => {
if $s.starts_with($x) {
return $crate::types::VmError::from($y);
}
};
}
pub trait VmCmd {
fn list_vms(&self) -> VmResult<Vec<Vm>>;
fn set_vm_by_id(&mut self, id: &str) -> VmResult<()>;
fn set_vm_by_name(&mut self, name: &str) -> VmResult<()>;
fn set_vm_by_path(&mut self, path: &str) -> VmResult<()>;
}
pub trait PowerCmd {
fn start(&self) -> VmResult<()>;
fn stop<D: Into<Option<Duration>>>(&self, timeout: D) -> VmResult<()>;
fn hard_stop(&self) -> VmResult<()>;
fn suspend(&self) -> VmResult<()>;
fn resume(&self) -> VmResult<()>;
fn is_running(&self) -> VmResult<bool>;
fn reboot<D: Into<Option<Duration>>>(&self, timeout: D) -> VmResult<()>;
fn hard_reboot(&self) -> VmResult<()>;
fn pause(&self) -> VmResult<()>;
fn unpause(&self) -> VmResult<()>;
}
pub trait SnapshotCmd {
fn list_snapshots(&self) -> VmResult<Vec<Snapshot>>;
fn take_snapshot(&self, name: &str) -> VmResult<()>;
fn revert_snapshot(&self, name: &str) -> VmResult<()>;
fn delete_snapshot(&self, name: &str) -> VmResult<()>;
}
pub trait GuestCmd {
fn exec_cmd(&self, guest_args: &[&str]) -> VmResult<()>;
fn copy_from_guest_to_host(
&self,
from_guest_path: &str,
to_host_path: &str,
) -> VmResult<()>;
fn copy_from_host_to_guest(
&self,
from_host_path: &str,
to_guest_path: &str,
) -> VmResult<()>;
}
pub trait NicCmd {
fn list_nics(&self) -> VmResult<Vec<Nic>>;
fn add_nic(&self, nic: &Nic) -> VmResult<()>;
fn update_nic(&self, nic: &Nic) -> VmResult<()>;
fn remove_nic(&self, nic: &Nic) -> VmResult<()>;
}
pub trait SharedFolderCmd {
fn list_shared_folders(&self) -> VmResult<Vec<SharedFolder>>;
fn mount_shared_folder(&self, shfs: &SharedFolder) -> VmResult<()>;
fn unmount_shared_folder(&self, shfs: &SharedFolder) -> VmResult<()>;
fn delete_shared_folder(&self, shfs: &SharedFolder) -> VmResult<()>;
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Vm {
pub id: Option<String>,
pub name: Option<String>,
pub path: Option<String>,
}
impl PartialEq for Vm {
fn eq(&self, other: &Self) -> bool {
if let (Some(x), Some(x2)) = (&self.id, &other.id) {
return x == x2;
}
if let (Some(x), Some(x2)) = (&self.path, &other.path) {
return x == x2;
}
if let (Some(x), Some(x2)) = (&self.name, &other.name) {
return x == x2;
}
false
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Snapshot {
pub id: Option<String>,
pub name: Option<String>,
pub detail: Option<String>,
}
impl PartialEq for Snapshot {
fn eq(&self, other: &Self) -> bool {
if let (Some(x), Some(x2)) = (&self.id, &other.id) {
return x == x2;
}
if let (Some(x), Some(x2)) = (&self.name, &other.name) {
return x == x2;
}
false
}
}
#[derive(Debug, Eq, PartialEq, Clone, Hash, Serialize, Deserialize)]
pub enum NicType {
Bridge,
#[allow(clippy::upper_case_acronyms)]
NAT,
HostOnly,
Custom(String),
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, Hash)]
pub struct Nic {
pub id: Option<String>,
pub name: Option<String>,
pub ty: Option<NicType>,
pub mac_address: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, Hash)]
pub struct SharedFolder {
pub id: Option<String>,
pub name: Option<String>,
pub guest_path: Option<String>,
pub host_path: Option<String>,
pub is_readonly: bool,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum VmPowerState {
Running,
NotRunning,
Stopped,
Suspended,
Paused,
Unknown,
}
impl VmPowerState {
#[inline]
pub fn is_running(&self) -> bool { *self == Self::Running }
}
macro_rules! impl_setter {
($(#[$inner:meta])* $name:ident : $t:ty) => {
$(#[$inner])*
pub fn $name<T: Into<$t>>(&mut self, $name: T) -> &mut Self {
self.$name = $name.into();
self
}
};
(@opt $(#[$inner:meta])* $name:ident : $t:ty) => {
$(#[$inner])*
pub fn $name<T: Into<Option<$t>>>(&mut self, $name: T) -> &mut Self {
self.$name = $name.into();
self
}
};
}