don't use strings for paths
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, OnceLock, Mutex, RwLock};
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -35,11 +35,11 @@ static DATA_STORAGES: OnceLock<RwLock<HashMap<u64, Arc<dyn StorageBackend>>>> =
|
||||
static DATA_INIT_LOCK: Mutex<()> = Mutex::new(());
|
||||
|
||||
fn init_admin_storage(
|
||||
base_dir: &str,
|
||||
base_dir: &Path,
|
||||
backend: options::BackendType,
|
||||
admin_secret: &str,
|
||||
) -> Result<Arc<dyn StorageBackend>, DBError> {
|
||||
let db_file = PathBuf::from(base_dir).join("0.db");
|
||||
let db_file = base_dir.join("0.db");
|
||||
if let Some(parent_dir) = db_file.parent() {
|
||||
std::fs::create_dir_all(parent_dir).map_err(|e| {
|
||||
DBError(format!("Failed to create directory {}: {}", parent_dir.display(), e))
|
||||
@@ -57,24 +57,25 @@ fn init_admin_storage(
|
||||
|
||||
// Get or initialize a cached handle to admin DB 0 per base_dir (thread-safe, no double-open race)
|
||||
pub fn open_admin_storage(
|
||||
base_dir: &str,
|
||||
base_dir: &Path,
|
||||
backend: options::BackendType,
|
||||
admin_secret: &str,
|
||||
) -> Result<Arc<dyn StorageBackend>, DBError> {
|
||||
let map = ADMIN_STORAGES.get_or_init(|| RwLock::new(HashMap::new()));
|
||||
let key = base_dir.display().to_string();
|
||||
// Fast path
|
||||
if let Some(st) = map.read().unwrap().get(base_dir) {
|
||||
if let Some(st) = map.read().unwrap().get(&key) {
|
||||
return Ok(st.clone());
|
||||
}
|
||||
// Slow path with write lock
|
||||
{
|
||||
let mut w = map.write().unwrap();
|
||||
if let Some(st) = w.get(base_dir) {
|
||||
if let Some(st) = w.get(&key) {
|
||||
return Ok(st.clone());
|
||||
}
|
||||
|
||||
// Detect existing 0.db backend by filesystem, if present.
|
||||
let admin_path = PathBuf::from(base_dir).join("0.db");
|
||||
let admin_path = base_dir.join("0.db");
|
||||
let detected = if admin_path.exists() {
|
||||
if admin_path.is_file() {
|
||||
Some(options::BackendType::Redb)
|
||||
@@ -102,14 +103,14 @@ pub fn open_admin_storage(
|
||||
};
|
||||
|
||||
let st = init_admin_storage(base_dir, effective_backend, admin_secret)?;
|
||||
w.insert(base_dir.to_string(), st.clone());
|
||||
w.insert(key, st.clone());
|
||||
Ok(st)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure admin structures exist in encrypted DB 0
|
||||
pub fn ensure_bootstrap(
|
||||
base_dir: &str,
|
||||
base_dir: &Path,
|
||||
backend: options::BackendType,
|
||||
admin_secret: &str,
|
||||
) -> Result<(), DBError> {
|
||||
@@ -125,7 +126,7 @@ pub fn ensure_bootstrap(
|
||||
|
||||
// Get or initialize a shared handle to a data DB (> 0), avoiding double-open across subsystems
|
||||
pub fn open_data_storage(
|
||||
base_dir: &str,
|
||||
base_dir: &Path,
|
||||
backend: options::BackendType,
|
||||
admin_secret: &str,
|
||||
id: u64,
|
||||
@@ -159,7 +160,7 @@ pub fn open_data_storage(
|
||||
// 2) If missing, sniff filesystem (file => Redb, dir => Sled), then persist into admin meta
|
||||
// 3) Fallback to requested 'backend' (startup default) if nothing else is known
|
||||
let meta_backend = get_database_backend(base_dir, backend.clone(), admin_secret, id).ok().flatten();
|
||||
let db_path = PathBuf::from(base_dir).join(format!("{}.db", id));
|
||||
let db_path = base_dir.join(format!("{}.db", id));
|
||||
let sniffed_backend = if db_path.exists() {
|
||||
if db_path.is_file() {
|
||||
Some(options::BackendType::Redb)
|
||||
@@ -214,7 +215,7 @@ pub fn open_data_storage(
|
||||
|
||||
// Allocate the next DB id and persist new pointer
|
||||
pub fn allocate_next_id(
|
||||
base_dir: &str,
|
||||
base_dir: &Path,
|
||||
backend: options::BackendType,
|
||||
admin_secret: &str,
|
||||
) -> Result<u64, DBError> {
|
||||
@@ -238,7 +239,7 @@ pub fn allocate_next_id(
|
||||
|
||||
// Check existence of a db id in admin:dbs
|
||||
pub fn db_exists(
|
||||
base_dir: &str,
|
||||
base_dir: &Path,
|
||||
backend: options::BackendType,
|
||||
admin_secret: &str,
|
||||
id: u64,
|
||||
@@ -249,7 +250,7 @@ pub fn db_exists(
|
||||
|
||||
// Get per-db encryption key, if any
|
||||
pub fn get_enc_key(
|
||||
base_dir: &str,
|
||||
base_dir: &Path,
|
||||
backend: options::BackendType,
|
||||
admin_secret: &str,
|
||||
id: u64,
|
||||
@@ -260,7 +261,7 @@ pub fn get_enc_key(
|
||||
|
||||
// Set per-db encryption key (called during create)
|
||||
pub fn set_enc_key(
|
||||
base_dir: &str,
|
||||
base_dir: &Path,
|
||||
backend: options::BackendType,
|
||||
admin_secret: &str,
|
||||
id: u64,
|
||||
@@ -272,7 +273,7 @@ pub fn set_enc_key(
|
||||
|
||||
// Set database public flag
|
||||
pub fn set_database_public(
|
||||
base_dir: &str,
|
||||
base_dir: &Path,
|
||||
backend: options::BackendType,
|
||||
admin_secret: &str,
|
||||
id: u64,
|
||||
@@ -286,7 +287,7 @@ pub fn set_database_public(
|
||||
|
||||
// Persist per-db backend type in admin metadata (module-scope)
|
||||
pub fn set_database_backend(
|
||||
base_dir: &str,
|
||||
base_dir: &Path,
|
||||
backend: options::BackendType,
|
||||
admin_secret: &str,
|
||||
id: u64,
|
||||
@@ -304,7 +305,7 @@ pub fn set_database_backend(
|
||||
}
|
||||
|
||||
pub fn get_database_backend(
|
||||
base_dir: &str,
|
||||
base_dir: &Path,
|
||||
backend: options::BackendType,
|
||||
admin_secret: &str,
|
||||
id: u64,
|
||||
@@ -321,7 +322,7 @@ pub fn get_database_backend(
|
||||
|
||||
// Set database name
|
||||
pub fn set_database_name(
|
||||
base_dir: &str,
|
||||
base_dir: &Path,
|
||||
backend: options::BackendType,
|
||||
admin_secret: &str,
|
||||
id: u64,
|
||||
@@ -335,7 +336,7 @@ pub fn set_database_name(
|
||||
|
||||
// Get database name
|
||||
pub fn get_database_name(
|
||||
base_dir: &str,
|
||||
base_dir: &Path,
|
||||
backend: options::BackendType,
|
||||
admin_secret: &str,
|
||||
id: u64,
|
||||
@@ -359,7 +360,7 @@ fn load_public(
|
||||
|
||||
// Add access key for db (value format: "Read:ts" or "ReadWrite:ts")
|
||||
pub fn add_access_key(
|
||||
base_dir: &str,
|
||||
base_dir: &Path,
|
||||
backend: options::BackendType,
|
||||
admin_secret: &str,
|
||||
id: u64,
|
||||
@@ -378,7 +379,7 @@ pub fn add_access_key(
|
||||
|
||||
// Delete access key by hash
|
||||
pub fn delete_access_key(
|
||||
base_dir: &str,
|
||||
base_dir: &Path,
|
||||
backend: options::BackendType,
|
||||
admin_secret: &str,
|
||||
id: u64,
|
||||
@@ -391,7 +392,7 @@ pub fn delete_access_key(
|
||||
|
||||
// List access keys, returning (hash, perms, created_at_secs)
|
||||
pub fn list_access_keys(
|
||||
base_dir: &str,
|
||||
base_dir: &Path,
|
||||
backend: options::BackendType,
|
||||
admin_secret: &str,
|
||||
id: u64,
|
||||
@@ -411,7 +412,7 @@ pub fn list_access_keys(
|
||||
// - Ok(Some(Permissions)) when access is allowed
|
||||
// - Ok(None) when not allowed or db missing (caller can distinguish by calling db_exists)
|
||||
pub fn verify_access(
|
||||
base_dir: &str,
|
||||
base_dir: &Path,
|
||||
backend: options::BackendType,
|
||||
admin_secret: &str,
|
||||
id: u64,
|
||||
@@ -456,7 +457,7 @@ pub fn verify_access(
|
||||
|
||||
// Enumerate all db ids
|
||||
pub fn list_dbs(
|
||||
base_dir: &str,
|
||||
base_dir: &Path,
|
||||
backend: options::BackendType,
|
||||
admin_secret: &str,
|
||||
) -> Result<Vec<u64>, DBError> {
|
||||
|
@@ -1427,7 +1427,7 @@ async fn incr_cmd(server: &Server, key: &String) -> Result<Protocol, DBError> {
|
||||
|
||||
fn config_get_cmd(name: &String, server: &Server) -> Result<Protocol, DBError> {
|
||||
let value = match name.as_str() {
|
||||
"dir" => Some(server.option.dir.clone()),
|
||||
"dir" => Some(server.option.dir.display().to_string()),
|
||||
"dbfilename" => Some(format!("{}.db", server.selected_db)),
|
||||
"databases" => Some("16".to_string()), // Hardcoded as per original logic
|
||||
_ => None,
|
||||
|
@@ -1,5 +1,6 @@
|
||||
// #![allow(unused_imports)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
use herodb::server;
|
||||
@@ -13,7 +14,7 @@ use clap::Parser;
|
||||
struct Args {
|
||||
/// The directory of Redis DB file
|
||||
#[arg(long)]
|
||||
dir: String,
|
||||
dir: PathBuf,
|
||||
|
||||
/// The port of the Redis server, default is 6379 if not specified
|
||||
#[arg(long)]
|
||||
|
@@ -1,3 +1,5 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum BackendType {
|
||||
Redb,
|
||||
@@ -7,7 +9,7 @@ pub enum BackendType {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DBOption {
|
||||
pub dir: String,
|
||||
pub dir: PathBuf,
|
||||
pub port: u16,
|
||||
pub debug: bool,
|
||||
// Deprecated for data DBs; retained for backward-compat on CLI parsing
|
||||
|
@@ -1,4 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
|
||||
@@ -165,7 +166,7 @@ pub trait Rpc {
|
||||
/// RPC Server implementation
|
||||
pub struct RpcServerImpl {
|
||||
/// Base directory for database files
|
||||
base_dir: String,
|
||||
base_dir: PathBuf,
|
||||
/// Managed database servers
|
||||
servers: Arc<RwLock<HashMap<u64, Arc<Server>>>>,
|
||||
/// Default backend type
|
||||
@@ -176,7 +177,7 @@ pub struct RpcServerImpl {
|
||||
|
||||
impl RpcServerImpl {
|
||||
/// Create a new RPC server instance
|
||||
pub fn new(base_dir: String, backend: crate::options::BackendType, admin_secret: String) -> Self {
|
||||
pub fn new(base_dir: PathBuf, backend: crate::options::BackendType, admin_secret: String) -> Self {
|
||||
Self {
|
||||
base_dir,
|
||||
servers: Arc::new(RwLock::new(HashMap::new())),
|
||||
@@ -351,7 +352,7 @@ impl RpcServerImpl {
|
||||
backend,
|
||||
encrypted,
|
||||
redis_version: Some("7.0".to_string()),
|
||||
storage_path: Some(server.option.dir.clone()),
|
||||
storage_path: Some(server.option.dir.display().to_string()),
|
||||
size_on_disk,
|
||||
key_count,
|
||||
created_at,
|
||||
|
@@ -1,11 +1,12 @@
|
||||
use std::net::SocketAddr;
|
||||
use std::path::PathBuf;
|
||||
use jsonrpsee::server::{ServerBuilder, ServerHandle};
|
||||
use jsonrpsee::RpcModule;
|
||||
|
||||
use crate::rpc::{RpcServer, RpcServerImpl};
|
||||
|
||||
/// Start the RPC server on the specified address
|
||||
pub async fn start_rpc_server(addr: SocketAddr, base_dir: String, backend: crate::options::BackendType, admin_secret: String) -> Result<ServerHandle, Box<dyn std::error::Error + Send + Sync>> {
|
||||
pub async fn start_rpc_server(addr: SocketAddr, base_dir: PathBuf, backend: crate::options::BackendType, admin_secret: String) -> Result<ServerHandle, Box<dyn std::error::Error + Send + Sync>> {
|
||||
// Create the RPC server implementation
|
||||
let rpc_impl = RpcServerImpl::new(base_dir, backend, admin_secret);
|
||||
|
||||
@@ -34,7 +35,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_rpc_server_startup() {
|
||||
let addr = "127.0.0.1:0".parse().unwrap(); // Use port 0 for auto-assignment
|
||||
let base_dir = "/tmp/test_rpc".to_string();
|
||||
let base_dir = PathBuf::from("/tmp/test_rpc");
|
||||
let backend = crate::options::BackendType::Redb; // Default for test
|
||||
|
||||
let handle = start_rpc_server(addr, base_dir, backend, "test-admin".to_string()).await.unwrap();
|
||||
|
@@ -1,4 +1,5 @@
|
||||
use herodb::{server::Server, options::DBOption};
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::TcpStream;
|
||||
@@ -22,7 +23,7 @@ async fn debug_hset_simple() {
|
||||
|
||||
let port = 16500;
|
||||
let option = DBOption {
|
||||
dir: test_dir.to_string(),
|
||||
dir: PathBuf::from(test_dir),
|
||||
port,
|
||||
debug: false,
|
||||
encrypt: false,
|
||||
|
@@ -1,4 +1,5 @@
|
||||
use herodb::{server::Server, options::DBOption};
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::TcpStream;
|
||||
@@ -13,7 +14,7 @@ async fn debug_hset_return_value() {
|
||||
std::fs::create_dir_all(&test_dir).unwrap();
|
||||
|
||||
let option = DBOption {
|
||||
dir: test_dir.to_string(),
|
||||
dir: PathBuf::from(test_dir),
|
||||
port: 16390,
|
||||
debug: false,
|
||||
encrypt: false,
|
||||
|
@@ -1,4 +1,5 @@
|
||||
use herodb::{server::Server, options::DBOption};
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::TcpStream;
|
||||
@@ -17,7 +18,7 @@ async fn start_test_server(test_name: &str) -> (Server, u16) {
|
||||
std::fs::create_dir_all(&test_dir).unwrap();
|
||||
|
||||
let option = DBOption {
|
||||
dir: test_dir,
|
||||
dir: PathBuf::from(test_dir),
|
||||
port,
|
||||
debug: true,
|
||||
encrypt: false,
|
||||
|
@@ -1,6 +1,7 @@
|
||||
use herodb::rpc::{BackendType, DatabaseConfig};
|
||||
use herodb::admin_meta;
|
||||
use herodb::options::BackendType as OptionsBackendType;
|
||||
use std::path::Path;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_rpc_server_basic() {
|
||||
@@ -70,11 +71,11 @@ async fn test_database_name_persistence() {
|
||||
let _ = std::fs::remove_dir_all(base_dir);
|
||||
|
||||
// Set the database name
|
||||
admin_meta::set_database_name(base_dir, backend.clone(), admin_secret, db_id, test_name)
|
||||
admin_meta::set_database_name(Path::new(base_dir), backend.clone(), admin_secret, db_id, test_name)
|
||||
.expect("Failed to set database name");
|
||||
|
||||
// Retrieve the database name
|
||||
let retrieved_name = admin_meta::get_database_name(base_dir, backend, admin_secret, db_id)
|
||||
let retrieved_name = admin_meta::get_database_name(Path::new(base_dir), backend, admin_secret, db_id)
|
||||
.expect("Failed to get database name");
|
||||
|
||||
// Verify the name matches
|
||||
|
@@ -1,4 +1,5 @@
|
||||
use herodb::{server::Server, options::DBOption};
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use tokio::time::sleep;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
@@ -19,7 +20,7 @@ async fn start_test_server(test_name: &str) -> (Server, u16) {
|
||||
std::fs::create_dir_all(&test_dir).unwrap();
|
||||
|
||||
let option = DBOption {
|
||||
dir: test_dir,
|
||||
dir: PathBuf::from(test_dir),
|
||||
port,
|
||||
debug: true,
|
||||
encrypt: false,
|
||||
|
@@ -1,4 +1,5 @@
|
||||
use herodb::{server::Server, options::DBOption};
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::TcpStream;
|
||||
@@ -17,7 +18,7 @@ async fn start_test_server(test_name: &str) -> (Server, u16) {
|
||||
std::fs::create_dir_all(&test_dir).unwrap();
|
||||
|
||||
let option = DBOption {
|
||||
dir: test_dir,
|
||||
dir: PathBuf::from(test_dir),
|
||||
port,
|
||||
debug: false,
|
||||
encrypt: false,
|
||||
|
@@ -1,4 +1,5 @@
|
||||
use herodb::{options::DBOption, server::Server};
|
||||
use std::path::PathBuf;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::time::{sleep, Duration};
|
||||
@@ -17,7 +18,7 @@ async fn start_test_server(test_name: &str) -> (Server, u16) {
|
||||
std::fs::create_dir_all(&test_dir).unwrap();
|
||||
|
||||
let option = DBOption {
|
||||
dir: test_dir,
|
||||
dir: PathBuf::from(test_dir),
|
||||
port,
|
||||
debug: false,
|
||||
encrypt: false,
|
||||
|
Reference in New Issue
Block a user