Skip to content

Commit caa96d6

Browse files
committed
impl: backends registry, KvsMap backend params
- Added `KvsBackendRegistry`. - `KvsBuilder` accepts backend params through `KvsMap`. - Built-in backend access is now hidden. - `backend_registration` example.
1 parent b6d018d commit caa96d6

File tree

18 files changed

+760
-407
lines changed

18 files changed

+760
-407
lines changed

README.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -183,12 +183,6 @@ Snapshot Count:
183183
Snapshot Restore:
184184
kvs_tool -o snapshotrestore -s 1
185185
186-
Get KVS Filename:
187-
kvs_tool -o getkvsfilename -s 1
188-
189-
Get Hash Filename:
190-
kvs_tool -o gethashfilename -s 1
191-
192186
---------------------------------------
193187
194188
Create Test Data:
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
//! Example for custom backend registration.
2+
//! - Implementation of `KvsBackend` traits.
3+
//! - Registration of custom backend.
4+
//! - Creation of KVS instance utilizing custom backend.
5+
6+
use rust_kvs::prelude::*;
7+
use tempfile::tempdir;
8+
9+
/// Mock backend implementation.
10+
/// Only `load_kvs` is implemented.
11+
struct MockBackend;
12+
13+
impl KvsBackend for MockBackend {
14+
fn load_kvs(
15+
&self,
16+
_instance_id: InstanceId,
17+
_snapshot_id: SnapshotId,
18+
) -> Result<KvsMap, ErrorCode> {
19+
println!("`load_kvs` used");
20+
Ok(KvsMap::new())
21+
}
22+
23+
fn load_defaults(&self, _instance_id: InstanceId) -> Result<KvsMap, ErrorCode> {
24+
unimplemented!()
25+
}
26+
27+
fn flush(&self, _instance_id: InstanceId, _kvs_map: &KvsMap) -> Result<(), ErrorCode> {
28+
unimplemented!()
29+
}
30+
31+
fn snapshot_count(&self, _instance_id: InstanceId) -> usize {
32+
unimplemented!()
33+
}
34+
35+
fn snapshot_max_count(&self) -> usize {
36+
unimplemented!()
37+
}
38+
39+
fn snapshot_restore(
40+
&self,
41+
_instance_id: InstanceId,
42+
_snapshot_id: SnapshotId,
43+
) -> Result<KvsMap, ErrorCode> {
44+
unimplemented!()
45+
}
46+
}
47+
48+
/// Mock backend factory implementation.
49+
struct MockBackendFactory;
50+
51+
impl KvsBackendFactory for MockBackendFactory {
52+
fn create(&self, _parameters: &KvsMap) -> Result<Box<dyn KvsBackend>, ErrorCode> {
53+
Ok(Box::new(MockBackend))
54+
}
55+
}
56+
57+
fn main() -> Result<(), ErrorCode> {
58+
// Temporary directory.
59+
let dir = tempdir()?;
60+
let dir_string = dir.path().to_string_lossy().to_string();
61+
62+
// Register `MockBackendFactory`.
63+
KvsBackendRegistry::register("mock", || Box::new(MockBackendFactory))?;
64+
65+
// Build KVS instance with mock backend.
66+
{
67+
let instance_id = InstanceId(0);
68+
let parameters = KvsMap::from([("name".to_string(), KvsValue::String("mock".to_string()))]);
69+
let builder = KvsBuilder::new(instance_id)
70+
.backend_parameters(parameters)
71+
.defaults(KvsDefaults::Ignored);
72+
let kvs = builder.build()?;
73+
74+
println!(
75+
"KVS instance with mock backend - parameters: {:?}",
76+
kvs.parameters()
77+
);
78+
}
79+
80+
// Build KVS instance with JSON backend - default parameters.
81+
{
82+
let instance_id = InstanceId(1);
83+
let builder = KvsBuilder::new(instance_id).defaults(KvsDefaults::Ignored);
84+
let kvs = builder.build()?;
85+
86+
println!(
87+
"KVS instance with default JSON backend - parameters: {:?}",
88+
kvs.parameters()
89+
);
90+
}
91+
92+
// Build KVS instance with JSON backend - `working_dir` set.
93+
{
94+
let instance_id = InstanceId(2);
95+
let parameters = KvsMap::from([
96+
("name".to_string(), KvsValue::String("json".to_string())),
97+
("working_dir".to_string(), KvsValue::String(dir_string)),
98+
]);
99+
let builder = KvsBuilder::new(instance_id)
100+
.backend_parameters(parameters)
101+
.defaults(KvsDefaults::Ignored);
102+
let kvs = builder.build()?;
103+
104+
println!(
105+
"KVS instance with JSON backend - parameters: {:?}",
106+
kvs.parameters()
107+
);
108+
}
109+
110+
Ok(())
111+
}

src/rust/rust_kvs/examples/basic.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@ use std::collections::HashMap;
88
use tempfile::tempdir;
99

1010
fn main() -> Result<(), ErrorCode> {
11-
// Temporary directory and common backend.
11+
// Temporary directory.
1212
let dir = tempdir()?;
13-
let dir_path = dir.path().to_path_buf();
14-
let backend = Box::new(JsonBackendBuilder::new().working_dir(dir_path).build());
13+
let dir_string = dir.path().to_string_lossy().to_string();
14+
let backend_parameters = KvsMap::from([
15+
("name".to_string(), KvsValue::String("json".to_string())),
16+
("working_dir".to_string(), KvsValue::String(dir_string)),
17+
]);
1518

1619
// Instance ID for KVS object instances.
1720
let instance_id = InstanceId(0);
@@ -21,7 +24,7 @@ fn main() -> Result<(), ErrorCode> {
2124
// `kvs_load` is explicitly set to `KvsLoad::Optional`, but this is the default value.
2225
// KVS files are not required.
2326
let builder = KvsBuilder::new(instance_id)
24-
.backend(backend.clone())
27+
.backend_parameters(backend_parameters.clone())
2528
.kvs_load(KvsLoad::Optional);
2629
let kvs = builder.build()?;
2730

@@ -66,7 +69,7 @@ fn main() -> Result<(), ErrorCode> {
6669
// Build KVS instance for given instance ID and temporary directory.
6770
// `kvs_load` is set to `KvsLoad::Required` - KVS files must already exist from previous KVS instance.
6871
let builder = KvsBuilder::new(instance_id)
69-
.backend(backend)
72+
.backend_parameters(backend_parameters)
7073
.kvs_load(KvsLoad::Required);
7174
let kvs = builder.build()?;
7275

src/rust/rust_kvs/examples/defaults.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,13 @@ fn create_defaults_file(dir_path: PathBuf, instance_id: InstanceId) -> Result<()
3131
}
3232

3333
fn main() -> Result<(), ErrorCode> {
34-
// Temporary directory and common backend.
34+
// Temporary directory.
3535
let dir = tempdir()?;
36-
let dir_path = dir.path().to_path_buf();
37-
let backend = Box::new(JsonBackendBuilder::new().working_dir(dir_path).build());
36+
let dir_string = dir.path().to_string_lossy().to_string();
37+
let backend_parameters = KvsMap::from([
38+
("name".to_string(), KvsValue::String("json".to_string())),
39+
("working_dir".to_string(), KvsValue::String(dir_string)),
40+
]);
3841

3942
// Instance ID for KVS object instances.
4043
let instance_id = InstanceId(0);
@@ -45,7 +48,7 @@ fn main() -> Result<(), ErrorCode> {
4548
// Build KVS instance for given instance ID and temporary directory.
4649
// `defaults` is set to `KvsDefaults::Required` - defaults are required.
4750
let builder = KvsBuilder::new(instance_id)
48-
.backend(backend.clone())
51+
.backend_parameters(backend_parameters)
4952
.defaults(KvsDefaults::Required);
5053
let kvs = builder.build()?;
5154

src/rust/rust_kvs/examples/snapshots.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ use tempfile::tempdir;
88
fn main() -> Result<(), ErrorCode> {
99
// Temporary directory and common backend.
1010
let dir = tempdir()?;
11-
let dir_path = dir.path().to_path_buf();
12-
let backend = Box::new(JsonBackendBuilder::new().working_dir(dir_path).build());
11+
let dir_string = dir.path().to_string_lossy().to_string();
12+
let backend_parameters = KvsMap::from([
13+
("name".to_string(), KvsValue::String("json".to_string())),
14+
("working_dir".to_string(), KvsValue::String(dir_string)),
15+
]);
1316

1417
// Instance ID for KVS object instances.
1518
let instance_id = InstanceId(0);
@@ -18,7 +21,7 @@ fn main() -> Result<(), ErrorCode> {
1821
println!("-> `snapshot_count` and `snapshot_max_count` usage");
1922

2023
// Build KVS instance for given instance ID and temporary directory.
21-
let builder = KvsBuilder::new(instance_id).backend(backend.clone());
24+
let builder = KvsBuilder::new(instance_id).backend_parameters(backend_parameters.clone());
2225
let kvs = builder.build()?;
2326

2427
let max_count = kvs.snapshot_max_count() as u32;
@@ -39,7 +42,7 @@ fn main() -> Result<(), ErrorCode> {
3942
println!("-> `snapshot_restore` usage");
4043

4144
// Build KVS instance for given instance ID and temporary directory.
42-
let builder = KvsBuilder::new(instance_id).backend(backend.clone());
45+
let builder = KvsBuilder::new(instance_id).backend_parameters(backend_parameters);
4346
let kvs = builder.build()?;
4447

4548
let max_count = kvs.snapshot_max_count() as u32;

src/rust/rust_kvs/src/error_code.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@ pub enum ErrorCode {
8282

8383
/// Instance parameters mismatch
8484
InstanceParametersMismatch,
85+
86+
/// Requested unknown backend
87+
UnknownBackend,
88+
89+
/// Backend already registered
90+
BackendAlreadyRegistered,
91+
92+
/// Invalid backend parameters.
93+
InvalidBackendParameters,
8594
}
8695

8796
impl From<std::io::Error> for ErrorCode {

src/rust/rust_kvs/src/json_backend.rs

Lines changed: 91 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
use crate::error_code::ErrorCode;
1313
use crate::kvs_api::{InstanceId, SnapshotId};
14-
use crate::kvs_backend::KvsBackend;
14+
use crate::kvs_backend::{KvsBackend, KvsBackendFactory};
1515
use crate::kvs_value::{KvsMap, KvsValue};
1616
use std::collections::HashMap;
1717
use std::fs;
@@ -151,7 +151,7 @@ impl From<JsonGenerateError> for ErrorCode {
151151
}
152152

153153
/// Builder for `JsonBackend`.
154-
pub struct JsonBackendBuilder {
154+
pub(crate) struct JsonBackendBuilder {
155155
working_dir: PathBuf,
156156
snapshot_max_count: usize,
157157
}
@@ -190,7 +190,7 @@ impl Default for JsonBackendBuilder {
190190

191191
/// KVS backend implementation based on TinyJSON.
192192
#[derive(Clone, PartialEq)]
193-
pub struct JsonBackend {
193+
pub(crate) struct JsonBackend {
194194
working_dir: PathBuf,
195195
snapshot_max_count: usize,
196196
}
@@ -255,7 +255,7 @@ impl JsonBackend {
255255
ext.is_some_and(|ep| ep.to_str().is_some_and(|es| es == extension))
256256
}
257257

258-
pub(super) fn load(kvs_path: &Path, hash_path: Option<&PathBuf>) -> Result<KvsMap, ErrorCode> {
258+
pub(crate) fn load(kvs_path: &Path, hash_path: Option<&PathBuf>) -> Result<KvsMap, ErrorCode> {
259259
if !Self::check_extension(kvs_path, "json") {
260260
return Err(ErrorCode::KvsFileReadError);
261261
}
@@ -299,7 +299,7 @@ impl JsonBackend {
299299
}
300300
}
301301

302-
pub(super) fn save(
302+
pub(crate) fn save(
303303
kvs_map: &KvsMap,
304304
kvs_path: &Path,
305305
hash_path: Option<&PathBuf>,
@@ -433,13 +433,41 @@ impl KvsBackend for JsonBackend {
433433
}
434434
}
435435

436+
/// `JsonBackend` factory.
437+
pub(crate) struct JsonBackendFactory;
438+
439+
impl KvsBackendFactory for JsonBackendFactory {
440+
fn create(&self, parameters: &KvsMap) -> Result<Box<dyn KvsBackend>, ErrorCode> {
441+
let mut builder = JsonBackendBuilder::new();
442+
443+
// Set working directory.
444+
if let Some(working_dir) = parameters.get("working_dir") {
445+
if let KvsValue::String(working_dir) = working_dir {
446+
builder = builder.working_dir(PathBuf::from(working_dir));
447+
} else {
448+
return Err(ErrorCode::InvalidBackendParameters);
449+
}
450+
}
451+
452+
// Set snapshot max count.
453+
if let Some(snapshot_max_count) = parameters.get("snapshot_max_count") {
454+
if let KvsValue::U64(snapshot_max_count) = snapshot_max_count {
455+
builder = builder.snapshot_max_count(*snapshot_max_count as usize);
456+
} else {
457+
return Err(ErrorCode::InvalidBackendParameters);
458+
}
459+
}
460+
461+
Ok(Box::new(builder.build()))
462+
}
463+
}
464+
436465
#[cfg(test)]
437466
mod json_value_to_kvs_value_conversion_tests {
467+
use crate::kvs_value::{KvsMap, KvsValue};
438468
use std::collections::HashMap;
439469
use tinyjson::JsonValue;
440470

441-
use crate::prelude::{KvsMap, KvsValue};
442-
443471
#[test]
444472
fn test_i32_ok() {
445473
let jv = JsonValue::from(HashMap::from([
@@ -1356,3 +1384,59 @@ mod kvs_backend_tests {
13561384
assert!(result.is_err_and(|e| e == ErrorCode::InvalidSnapshotId));
13571385
}
13581386
}
1387+
1388+
#[cfg(test)]
1389+
mod kvs_backend_factory_tests {
1390+
use crate::error_code::ErrorCode;
1391+
use crate::json_backend::JsonBackendFactory;
1392+
use crate::kvs_backend::KvsBackendFactory;
1393+
use crate::kvs_value::{KvsMap, KvsValue};
1394+
1395+
#[test]
1396+
fn test_create_default_ok() {
1397+
let factory = JsonBackendFactory;
1398+
let params = KvsMap::new();
1399+
let backend = factory.create(&params).unwrap();
1400+
// `working_dir` is not exposed in the API.
1401+
assert_eq!(backend.snapshot_max_count(), 3);
1402+
}
1403+
1404+
#[test]
1405+
fn test_create_params_ok() {
1406+
let factory = JsonBackendFactory;
1407+
let params = KvsMap::from([
1408+
(
1409+
"working_dir".to_string(),
1410+
KvsValue::String("/some/path/".to_string()),
1411+
),
1412+
("snapshot_max_count".to_string(), KvsValue::U64(1234)),
1413+
]);
1414+
let backend = factory.create(&params).unwrap();
1415+
// `working_dir` is not exposed in the API.
1416+
assert_eq!(backend.snapshot_max_count(), 1234);
1417+
}
1418+
1419+
#[test]
1420+
fn test_create_working_dir_invalid_type() {
1421+
let factory = JsonBackendFactory;
1422+
let params = KvsMap::from([("working_dir".to_string(), KvsValue::Boolean(true))]);
1423+
let result = factory.create(&params);
1424+
assert!(result.is_err_and(|e| e == ErrorCode::InvalidBackendParameters));
1425+
}
1426+
1427+
#[test]
1428+
fn test_create_snapshot_max_count_invalid_type() {
1429+
let factory = JsonBackendFactory;
1430+
let params = KvsMap::from([("snapshot_max_count".to_string(), KvsValue::I32(-123))]);
1431+
let result = factory.create(&params);
1432+
assert!(result.is_err_and(|e| e == ErrorCode::InvalidBackendParameters));
1433+
}
1434+
1435+
#[test]
1436+
fn test_create_unknown_param_ok() {
1437+
let factory = JsonBackendFactory;
1438+
let params = KvsMap::from([("unknown_param".to_string(), KvsValue::I32(12345))]);
1439+
let result = factory.create(&params);
1440+
assert!(result.is_ok());
1441+
}
1442+
}

0 commit comments

Comments
 (0)