127 lines
3.7 KiB
Rust
127 lines
3.7 KiB
Rust
use heromodels_derive::model;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
// Make the current crate visible as an extern crate named `heromodels_core`
|
|
extern crate self as heromodels_core;
|
|
extern crate serde_json; // ensure ::serde_json path resolves
|
|
|
|
// Mock the heromodels_core API at crate root (visible via the alias above)
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct IndexKey {
|
|
pub name: &'static str,
|
|
pub value: String,
|
|
}
|
|
|
|
pub trait Model: std::fmt::Debug + Clone + Serialize + for<'de> Deserialize<'de> + Send + Sync + 'static {
|
|
fn db_prefix() -> &'static str
|
|
where
|
|
Self: Sized;
|
|
fn get_id(&self) -> u32;
|
|
fn base_data_mut(&mut self) -> &mut BaseModelData;
|
|
fn db_keys(&self) -> Vec<IndexKey> {
|
|
Vec::new()
|
|
}
|
|
fn indexed_fields() -> Vec<&'static str> {
|
|
Vec::new()
|
|
}
|
|
}
|
|
|
|
pub trait Index {
|
|
type Model: Model;
|
|
type Key: ToString + ?Sized;
|
|
fn key() -> &'static str;
|
|
fn field_name() -> &'static str;
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct BaseModelData {
|
|
pub id: u32,
|
|
pub created_at: i64,
|
|
pub modified_at: i64,
|
|
pub comments: Vec<u32>,
|
|
}
|
|
|
|
impl BaseModelData {
|
|
pub fn new() -> Self {
|
|
let now = 1000;
|
|
Self { id: 0, created_at: now, modified_at: now, comments: Vec::new() }
|
|
}
|
|
pub fn update_modified(&mut self) { self.modified_at += 1; }
|
|
}
|
|
|
|
// Top-level field index tests
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[model]
|
|
pub struct TestUser {
|
|
base_data: heromodels_core::BaseModelData,
|
|
|
|
#[index]
|
|
username: String,
|
|
|
|
#[index]
|
|
is_active: bool,
|
|
}
|
|
|
|
#[test]
|
|
fn test_basic_model() {
|
|
assert_eq!(TestUser::db_prefix(), "test_user");
|
|
|
|
let user = TestUser {
|
|
base_data: heromodels_core::BaseModelData::new(),
|
|
username: "test".to_string(),
|
|
is_active: true,
|
|
};
|
|
|
|
let keys = user.db_keys();
|
|
assert_eq!(keys.len(), 2);
|
|
assert_eq!(keys[0].name, "username");
|
|
assert_eq!(keys[0].value, "test");
|
|
assert_eq!(keys[1].name, "is_active");
|
|
assert_eq!(keys[1].value, "true");
|
|
}
|
|
|
|
// Nested path index tests (including vector traversal)
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
struct GPU { gpu_brand: String }
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
struct CPU { cpu_brand: String }
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
struct DeviceInfo { vendor: String, cpu: Vec<CPU>, gpu: Vec<GPU> }
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[model]
|
|
pub struct NodeLike {
|
|
base_data: heromodels_core::BaseModelData,
|
|
|
|
#[index(path = "vendor")]
|
|
#[index(path = "cpu.cpu_brand")]
|
|
#[index(path = "gpu.gpu_brand")]
|
|
devices: DeviceInfo,
|
|
}
|
|
|
|
#[test]
|
|
fn test_nested_indexes() {
|
|
let n = NodeLike {
|
|
base_data: heromodels_core::BaseModelData::new(),
|
|
devices: DeviceInfo {
|
|
vendor: "SuperVendor".to_string(),
|
|
cpu: vec![CPU { cpu_brand: "Intel".into() }, CPU { cpu_brand: "AMD".into() }],
|
|
gpu: vec![GPU { gpu_brand: "NVIDIA".into() }, GPU { gpu_brand: "AMD".into() }],
|
|
},
|
|
};
|
|
|
|
let mut keys = n.db_keys();
|
|
// Sort for deterministic assertions
|
|
keys.sort_by(|a,b| a.name.cmp(b.name).then(a.value.cmp(&b.value)));
|
|
|
|
// Expect 1 (vendor) + 2 (cpu brands) + 2 (gpu brands) = 5 keys
|
|
assert_eq!(keys.len(), 5);
|
|
assert!(keys.iter().any(|k| k.name == "vendor" && k.value == "SuperVendor"));
|
|
assert!(keys.iter().any(|k| k.name == "cpu.cpu_brand" && k.value == "Intel"));
|
|
assert!(keys.iter().any(|k| k.name == "cpu.cpu_brand" && k.value == "AMD"));
|
|
assert!(keys.iter().any(|k| k.name == "gpu.gpu_brand" && k.value == "NVIDIA"));
|
|
assert!(keys.iter().any(|k| k.name == "gpu.gpu_brand" && k.value == "AMD"));
|
|
}
|