use alloc::string::String;
use alloc::vec::Vec;
use core::convert::From;
use core::fmt;
use core::ops::Deref;
use serde::{Deserialize, Serialize};
use uguid::Guid;
use zerocopy::{FromBytes, IntoBytes};
use virtfw_libefi::efivar::types::{EfiVar, EfiVarAttr, EfiVarId};
use virtfw_libefi::guids;
use virtfw_libefi::types::EfiTime;
use crate::store::EfiVarStore;
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct JsonHexString(String);
impl Deref for JsonHexString {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl TryFrom<&JsonHexString> for Vec<u8> {
type Error = hex::FromHexError;
fn try_from(hs: &JsonHexString) -> Result<Self, Self::Error> {
hex::decode(&hs.0)
}
}
impl From<&[u8]> for JsonHexString {
fn from(data: &[u8]) -> Self {
Self(hex::encode(data))
}
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
pub struct JsonEfiVariable {
pub guid: Guid,
pub name: String,
pub attr: u32,
pub data: JsonHexString,
#[serde(skip_serializing_if = "Option::is_none")]
pub time: Option<JsonHexString>,
#[serde(skip_serializing_if = "Option::is_none")]
pub digest: Option<JsonHexString>,
}
impl JsonEfiVariable {
pub fn parse_time(&self) -> Option<EfiTime> {
let hexstr = self.time.as_ref()?;
let bytes = Vec::<u8>::try_from(hexstr).ok()?;
let efitime = EfiTime::read_from_bytes(&bytes).ok()?;
Some(efitime)
}
}
impl TryFrom<&JsonEfiVariable> for EfiVar {
type Error = hex::FromHexError;
fn try_from(v: &JsonEfiVariable) -> Result<Self, Self::Error> {
let attr = EfiVarAttr::from(v.attr);
let data = Vec::<u8>::try_from(&v.data)?;
let time = v.parse_time();
let id = v.into();
Ok(EfiVar::new_with_vec_full(id, attr, data, time))
}
}
impl From<&JsonEfiVariable> for EfiVarId {
fn from(v: &JsonEfiVariable) -> Self {
Self::new(v.name.clone(), v.guid)
}
}
impl From<&EfiVar> for JsonEfiVariable {
fn from(v: &EfiVar) -> Self {
Self {
guid: *v.guid(),
name: v.name().into(),
attr: v.attr().into(),
data: JsonHexString::from(v.data()),
time: v.time().map(|t| JsonHexString::from(t.as_bytes())),
digest: None,
}
}
}
impl fmt::Display for JsonEfiVariable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let attr = EfiVarAttr::from(self.attr);
write!(
f,
"{:36} {:24} {:3} byte(s) {}",
guids::pretty_name(&self.guid),
self.name,
self.data.len() / 2,
attr,
)
}
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
pub struct JsonEfiVarStore {
pub version: u32,
pub variables: Vec<JsonEfiVariable>,
}
impl EfiVarStore {
pub fn json_import(&mut self, jstore: &JsonEfiVarStore) {
for jvar in &jstore.variables {
let var = EfiVar::try_from(jvar).unwrap();
self.set_unchecked(var);
}
}
pub fn json_export(&self) -> JsonEfiVarStore {
let variables = self
.variables
.iter()
.filter(|v| v.attr().non_volatile())
.map(JsonEfiVariable::from)
.collect();
JsonEfiVarStore {
version: 2,
variables,
}
}
}