add patterns storage functions.
This commit is contained in:
parent
a1b5859cd6
commit
26e825bb19
|
@ -1,4 +1,4 @@
|
||||||
use std::sync::Arc;
|
use std::{collections::VecDeque, sync::Arc};
|
||||||
|
|
||||||
use btleplug::{
|
use btleplug::{
|
||||||
api::{Central as _, Peripheral as _},
|
api::{Central as _, Peripheral as _},
|
||||||
|
@ -9,6 +9,7 @@ use tauri::{async_runtime::RwLock, AppHandle, Emitter, State};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bluetooth, errors,
|
bluetooth, errors,
|
||||||
|
pattern::Pattern,
|
||||||
state::{self as global, AppState},
|
state::{self as global, AppState},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -128,3 +129,17 @@ pub async fn stop_scan_devices(app_handle: AppHandle) -> Result<(), errors::AppE
|
||||||
bluetooth::stop_scan(Arc::new(app_handle)).await?;
|
bluetooth::stop_scan(Arc::new(app_handle)).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn list_patterns(
|
||||||
|
app_state: State<'_, Arc<RwLock<AppState>>>,
|
||||||
|
keyword: Option<String>,
|
||||||
|
) -> Result<VecDeque<Pattern>, errors::AppError> {
|
||||||
|
let state = app_state.read().await;
|
||||||
|
let patterns = state
|
||||||
|
.db
|
||||||
|
.get_patterns(keyword)
|
||||||
|
.await
|
||||||
|
.map_err(|e| errors::AppError::StorageFailure(e.to_string()))?;
|
||||||
|
Ok(patterns)
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
use std::{fs::DirBuilder, path::PathBuf};
|
use std::{collections::VecDeque, fs::DirBuilder, path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
use tauri::{path::PathResolver, AppHandle, Manager, Runtime};
|
use tauri::{path::PathResolver, AppHandle, Manager, Runtime};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::pattern::Pattern;
|
||||||
|
|
||||||
|
static PATTERN_KEY_PREFIX: &[u8] = b"PATTERN:";
|
||||||
|
|
||||||
fn ensure_dir<R: Runtime>(resolver: &PathResolver<R>) -> anyhow::Result<PathBuf> {
|
fn ensure_dir<R: Runtime>(resolver: &PathResolver<R>) -> anyhow::Result<PathBuf> {
|
||||||
let config_dir = resolver
|
let config_dir = resolver
|
||||||
|
@ -16,8 +22,81 @@ fn ensure_dir<R: Runtime>(resolver: &PathResolver<R>) -> anyhow::Result<PathBuf>
|
||||||
Ok(config_dir)
|
Ok(config_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_config_db<R: Runtime>(app_handle: &AppHandle<R>) -> anyhow::Result<sled::Db> {
|
pub struct ConfigDb(Arc<Mutex<sled::Db>>);
|
||||||
let config_dir = ensure_dir(app_handle.path())?;
|
|
||||||
let db_path = config_dir.join("conf.db");
|
impl ConfigDb {
|
||||||
sled::open(&db_path).map_err(|e| anyhow::anyhow!("Unable to open config database: {}", e))
|
pub fn open<R: Runtime>(app_handle: &AppHandle<R>) -> anyhow::Result<Self> {
|
||||||
|
let config_dir = ensure_dir(app_handle.path())?;
|
||||||
|
let db_path = config_dir.join("conf.db");
|
||||||
|
let db = sled::open(&db_path)
|
||||||
|
.map_err(|e| anyhow::anyhow!("Unable to open config database: {}", e))?;
|
||||||
|
Ok(Self(Arc::new(Mutex::new(db))))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn store_pattern(&self, pattern: &Pattern) -> anyhow::Result<()> {
|
||||||
|
let db = self.0.lock().await;
|
||||||
|
let key = PATTERN_KEY_PREFIX
|
||||||
|
.iter()
|
||||||
|
.chain(pattern.id.as_bytes())
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<u8>>();
|
||||||
|
db.insert(key, pattern)
|
||||||
|
.map_err(|e| anyhow::anyhow!("Unable to store pattern: {}", e))?;
|
||||||
|
db.flush_async()
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow::anyhow!("Unable to save db: {}", e))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_patterns(&self, keyword: Option<String>) -> anyhow::Result<VecDeque<Pattern>> {
|
||||||
|
let db = self.0.lock().await;
|
||||||
|
let patterns = db.scan_prefix(PATTERN_KEY_PREFIX);
|
||||||
|
let mut result: VecDeque<Pattern> = VecDeque::new();
|
||||||
|
for entry in patterns {
|
||||||
|
if let Ok((_, pattern)) = entry {
|
||||||
|
let pattern = Into::<Pattern>::into(pattern);
|
||||||
|
if let Some(keyword) = &keyword {
|
||||||
|
if !pattern.name.contains(keyword) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let index =
|
||||||
|
result.binary_search_by(|item| item.created_at.cmp(&pattern.created_at));
|
||||||
|
match index {
|
||||||
|
Ok(index) => result.insert(index + 1, pattern),
|
||||||
|
Err(index) => result.insert(index, pattern),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_pattern(&self, pattern_id: &Uuid) -> anyhow::Result<Option<Pattern>> {
|
||||||
|
let db = self.0.lock().await;
|
||||||
|
let key = PATTERN_KEY_PREFIX
|
||||||
|
.iter()
|
||||||
|
.chain(pattern_id.as_bytes())
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<u8>>();
|
||||||
|
let pattern = db
|
||||||
|
.get(key)
|
||||||
|
.map_err(|e| anyhow::anyhow!("Unable to get pattern: {}", e))?
|
||||||
|
.map(|p| Into::<Pattern>::into(p));
|
||||||
|
Ok(pattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn remove_pattern(&self, pattern_id: &Uuid) -> anyhow::Result<()> {
|
||||||
|
let db = self.0.lock().await;
|
||||||
|
let key = PATTERN_KEY_PREFIX
|
||||||
|
.iter()
|
||||||
|
.chain(pattern_id.as_bytes())
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<u8>>();
|
||||||
|
db.remove(key)
|
||||||
|
.map_err(|e| anyhow::anyhow!("Unable to remove pattern: {}", e))?;
|
||||||
|
db.flush_async()
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow::anyhow!("Unable to save db: {}", e))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,4 +23,6 @@ pub enum AppError {
|
||||||
UnableToRetrievePeripheralState,
|
UnableToRetrievePeripheralState,
|
||||||
#[error("Unable to retrieve peripheral services")]
|
#[error("Unable to retrieve peripheral services")]
|
||||||
UnableToRetrievePeripheralServices,
|
UnableToRetrievePeripheralServices,
|
||||||
|
#[error("Visit data storage failed: {0}")]
|
||||||
|
StorageFailure(String),
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,8 @@ pub fn run() {
|
||||||
cmd::channel_b_state,
|
cmd::channel_b_state,
|
||||||
cmd::activate_central_adapter,
|
cmd::activate_central_adapter,
|
||||||
cmd::start_scan_devices,
|
cmd::start_scan_devices,
|
||||||
cmd::stop_scan_devices
|
cmd::stop_scan_devices,
|
||||||
|
cmd::list_patterns
|
||||||
])
|
])
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
|
|
|
@ -10,10 +10,10 @@ use tauri::{
|
||||||
};
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::config_db;
|
use crate::config_db::ConfigDb;
|
||||||
|
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub db: Arc<Mutex<sled::Db>>,
|
pub db: ConfigDb,
|
||||||
pub central_event_handler: Arc<Mutex<Option<JoinHandle<()>>>>,
|
pub central_event_handler: Arc<Mutex<Option<JoinHandle<()>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,10 +32,10 @@ static CONNECTED_PERIPHERAL: LazyLock<Arc<Mutex<Option<PeripheralId>>>> =
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
pub async fn new<R: Runtime>(app: &AppHandle<R>) -> anyhow::Result<Self> {
|
pub async fn new<R: Runtime>(app: &AppHandle<R>) -> anyhow::Result<Self> {
|
||||||
let db = config_db::open_config_db(app)?;
|
let db = ConfigDb::open(app)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
db: Arc::new(Mutex::new(db)),
|
db,
|
||||||
central_event_handler: Arc::new(Mutex::new(None)),
|
central_event_handler: Arc::new(Mutex::new(None)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user