Storage progress
This commit is contained in:
parent
9aa5430851
commit
a3a95b179b
12
firmware/Cargo.lock
generated
12
firmware/Cargo.lock
generated
|
|
@ -11,6 +11,7 @@ dependencies = [
|
|||
"bytemuck",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"chrono",
|
||||
"const-gen",
|
||||
"critical-section",
|
||||
"data-encoding-macro",
|
||||
|
|
@ -45,10 +46,12 @@ dependencies = [
|
|||
"panic-rtt-target",
|
||||
"password-hash 0.1.0",
|
||||
"pastey 0.2.1",
|
||||
"postcard",
|
||||
"printf-compat",
|
||||
"rand_core 0.9.5",
|
||||
"rmk",
|
||||
"rtt-target",
|
||||
"serde",
|
||||
"sha2",
|
||||
"slint",
|
||||
"slint-build",
|
||||
|
|
@ -818,6 +821,7 @@ dependencies = [
|
|||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
"windows-link",
|
||||
]
|
||||
|
|
@ -1836,6 +1840,12 @@ dependencies = [
|
|||
"embedded-hal 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.6.1"
|
||||
|
|
@ -5670,6 +5680,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
|
||||
dependencies = [
|
||||
"cobs",
|
||||
"embedded-io 0.4.0",
|
||||
"embedded-io 0.6.1",
|
||||
"heapless 0.7.17",
|
||||
"postcard-derive",
|
||||
"serde",
|
||||
|
|
|
|||
|
|
@ -80,6 +80,10 @@ password-hash = { path = "../password-hash", default-features = false }
|
|||
hmac = "0.12.1"
|
||||
data-encoding-macro = "0.1.19"
|
||||
embedded-storage-async = "0.4.1"
|
||||
postcard = { version = "1.1", default-features = false, features = ["alloc", "postcard-derive"] } # TODO: defmt
|
||||
serde = { version = "1.0", default-features = false, features = ["derive"] }
|
||||
# serde_with = { version = "3.16", default-features = false, features = ["alloc", "macros"] }
|
||||
chrono = { version = "0.4.43", default-features = false, features = ["alloc", "serde"] } # TODO: defmt
|
||||
|
||||
# Crates for serial UART CLI
|
||||
embedded-cli = { version = "0.2.1", default-features = false, features = ["help", "macros"] }
|
||||
|
|
|
|||
|
|
@ -126,11 +126,11 @@ impl AcidDatabase {
|
|||
|
||||
#[cfg(feature = "format-db")]
|
||||
{
|
||||
warn!("Formatting EKV database...");
|
||||
log::warn!("Formatting EKV database...");
|
||||
db.format()
|
||||
.await
|
||||
.unwrap_or_else(|error| panic!("Failed to format the EKV database: {error:?}"));
|
||||
warn!("EKV database formatted successfully.");
|
||||
log::warn!("EKV database formatted successfully.");
|
||||
}
|
||||
|
||||
match db.mount().await {
|
||||
|
|
@ -146,7 +146,7 @@ type DbPathSegment<'a> = Cow<'a, str>;
|
|||
type DbPathBuf<'a> = Vec<DbPathSegment<'a>>;
|
||||
type DbPath<'a> = [DbPathSegment<'a>];
|
||||
|
||||
struct DbKey {
|
||||
pub struct DbKey {
|
||||
/// Segments separated by `0x00`, with the whole thing suffixed with `[0x00, 0xFF]`.
|
||||
bytes: Vec<u8>,
|
||||
}
|
||||
|
|
@ -160,12 +160,12 @@ impl Deref for DbKey {
|
|||
}
|
||||
|
||||
impl DbKey {
|
||||
fn from_raw(mut key: Vec<u8>) -> Self {
|
||||
pub fn from_raw(mut key: Vec<u8>) -> Self {
|
||||
key.extend_from_slice(&[0x00, 0xFF]);
|
||||
Self { bytes: key }
|
||||
}
|
||||
|
||||
fn new<'a>(path: impl IntoIterator<Item = DbPathSegment<'a>>) -> Self {
|
||||
pub fn new<'a>(path: impl IntoIterator<Item = DbPathSegment<'a>>) -> Self {
|
||||
// Null bytes are not allowed within path segments, and will cause a panic.
|
||||
// Bytes of `0xFF` cannot appear in valid UTF-8.
|
||||
// The byte vector stored in a `DbKey` is formed by interspersing the segments with `0x00`, and suffixing the key with `[0x00, 0xFF]`.
|
||||
|
|
@ -188,20 +188,17 @@ impl DbKey {
|
|||
bytes.push(0x00);
|
||||
}
|
||||
|
||||
if let Some(last_byte) = bytes.last_mut() {
|
||||
assert!(!bytes.is_empty(), "An empty path is not a valid path.");
|
||||
bytes.push(0xFF);
|
||||
} else {
|
||||
panic!("An empty path is not a valid path.");
|
||||
}
|
||||
|
||||
DbKey { bytes }
|
||||
}
|
||||
|
||||
fn range_of_children(&self) -> Range<&[u8]> {
|
||||
pub fn range_of_children(&self) -> Range<&[u8]> {
|
||||
(&self.bytes[0..(self.bytes.len() - 1)])..(&self.bytes[..])
|
||||
}
|
||||
|
||||
fn segments(&self) -> impl Iterator<Item = DbPathSegment<'_>> {
|
||||
pub fn segments(&self) -> impl Iterator<Item = DbPathSegment<'_>> {
|
||||
struct SegmentIterator<'a> {
|
||||
rest: &'a [u8],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,15 @@ use core::{
|
|||
ops::{Deref, DerefMut, Range},
|
||||
};
|
||||
|
||||
use alloc::{borrow::Cow, boxed::Box, ffi::CString, vec::Vec};
|
||||
use alloc::{
|
||||
borrow::Cow,
|
||||
boxed::Box,
|
||||
ffi::CString,
|
||||
string::{String, ToString},
|
||||
vec,
|
||||
vec::Vec,
|
||||
};
|
||||
use chrono::{DateTime, NaiveDateTime};
|
||||
use ekv::{Database, MountError, flash::PageID};
|
||||
use embassy_embedded_hal::{adapter::BlockingAsync, flash::partition::Partition};
|
||||
use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex};
|
||||
|
|
@ -16,14 +24,17 @@ use esp_storage::FlashStorage;
|
|||
use itertools::Itertools;
|
||||
use log::{info, warn};
|
||||
use rmk::futures::TryFutureExt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use slint::SharedString;
|
||||
use spectre_api_sys::{SpectreAlgorithm, SpectreCounter, SpectreKeyPurpose, SpectreUserKey};
|
||||
use spectre_api_sys::{
|
||||
SpectreAlgorithm, SpectreCounter, SpectreKeyPurpose, SpectreResultType, SpectreUserKey,
|
||||
};
|
||||
|
||||
#[cfg(feature = "limit-fps")]
|
||||
use crate::FRAME_DURATION_MIN;
|
||||
use crate::{
|
||||
SIGNAL_LCD_SUBMIT, SIGNAL_UI_RENDER,
|
||||
db::{AcidDatabase, PartitionAcid},
|
||||
db::{AcidDatabase, DbKey, DbPathSpectreUsers, PartitionAcid},
|
||||
ui::backend::SlintBackend,
|
||||
util::DurationExt,
|
||||
};
|
||||
|
|
@ -33,10 +44,131 @@ pub mod window_adapter;
|
|||
|
||||
slint::include_modules!();
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
|
||||
struct SpectreUsersConfig {
|
||||
users: Vec<SpectreUserConfig>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
|
||||
struct SpectreUserConfig {
|
||||
username: String,
|
||||
}
|
||||
|
||||
trait ReprConvert: Copy {
|
||||
type Repr: Copy;
|
||||
|
||||
fn into_repr(self) -> Self::Repr;
|
||||
fn from_repr(repr: Self::Repr) -> Self;
|
||||
}
|
||||
|
||||
impl ReprConvert for SpectreAlgorithm {
|
||||
type Repr = u32;
|
||||
|
||||
fn from_repr(repr: Self::Repr) -> Self {
|
||||
unsafe { core::mem::transmute::<Self::Repr, Self>(repr) }
|
||||
}
|
||||
|
||||
fn into_repr(self) -> Self::Repr {
|
||||
self as Self::Repr
|
||||
}
|
||||
}
|
||||
|
||||
impl ReprConvert for SpectreResultType {
|
||||
type Repr = u32;
|
||||
|
||||
fn from_repr(repr: Self::Repr) -> Self {
|
||||
unsafe { core::mem::transmute::<Self::Repr, Self>(repr) }
|
||||
}
|
||||
|
||||
fn into_repr(self) -> Self::Repr {
|
||||
self as Self::Repr
|
||||
}
|
||||
}
|
||||
|
||||
mod with_repr {
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
|
||||
use super::ReprConvert;
|
||||
|
||||
pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
T: ReprConvert<Repr = u32>,
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_u32(value.into_repr())
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
|
||||
where
|
||||
T: ReprConvert<Repr = u32>,
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
<u32 as Deserialize>::deserialize(deserializer).map(T::from_repr)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
|
||||
struct SpectreSiteConfig {
|
||||
#[serde(with = "with_repr")]
|
||||
algorithm: SpectreAlgorithm,
|
||||
counter: SpectreCounter::Type,
|
||||
#[serde(rename = "type")]
|
||||
#[serde(with = "with_repr")]
|
||||
result_type: SpectreResultType,
|
||||
password: Option<String>,
|
||||
#[serde(with = "with_repr")]
|
||||
login_type: SpectreResultType,
|
||||
login_name: Option<String>,
|
||||
uses: u32,
|
||||
last_used: NaiveDateTime,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct SpectreSite {
|
||||
username: String,
|
||||
site_name: String,
|
||||
config: SpectreSiteConfig,
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
pub async fn run_renderer_task(backend: SlintBackend, flash_part_acid: PartitionAcid) {
|
||||
let db = AcidDatabase::mount(flash_part_acid).await;
|
||||
let write = db.write_transaction().await;
|
||||
|
||||
let value = SpectreUsersConfig {
|
||||
users: vec![SpectreUserConfig {
|
||||
username: "test".to_string(),
|
||||
}],
|
||||
};
|
||||
|
||||
{
|
||||
let mut write = db.write_transaction().await;
|
||||
write
|
||||
.write(
|
||||
&DbKey::new(DbPathSpectreUsers),
|
||||
&postcard::to_allocvec(&value).unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
write.commit().await.unwrap();
|
||||
}
|
||||
|
||||
let read_value = {
|
||||
let read = db.read_transaction().await;
|
||||
// TODO: https://github.com/embassy-rs/ekv/issues/20
|
||||
let mut buffer = Vec::new();
|
||||
let length = read
|
||||
.read(&DbKey::new(DbPathSpectreUsers), &mut buffer)
|
||||
.await
|
||||
.unwrap();
|
||||
buffer.resize(length, 0);
|
||||
read.read(&DbKey::new(DbPathSpectreUsers), &mut buffer)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
postcard::from_bytes::<SpectreUsersConfig>(&buffer).unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(value, read_value, "values do not match");
|
||||
|
||||
// TODO:
|
||||
// * Store a config as a versioned postcard-serialized struct
|
||||
|
|
|
|||
Loading…
Reference in a new issue