use std::collections::HashMap;
use serde_json::Value;
use crate::domain::{Format, RawContent, Result, Source, SourceError, SourceKind, SourceMetadata};
#[derive(Debug, Default)]
pub struct MemorySourceBuilder {
data: HashMap<String, Value>,
required: bool,
name: Option<String>,
}
impl MemorySourceBuilder {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn insert(mut self, key: impl Into<String>, value: Value) -> Self {
self.data.insert(key.into(), value);
self
}
#[must_use]
pub fn extend(mut self, data: HashMap<String, Value>) -> Self {
self.data.extend(data);
self
}
#[must_use]
pub fn set(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.data.insert(key.into(), Value::String(value.into()));
self
}
#[must_use]
pub fn set_number(mut self, key: impl Into<String>, value: f64) -> Self {
self.data.insert(key.into(), serde_json::json!(value));
self
}
#[must_use]
pub fn set_bool(mut self, key: impl Into<String>, value: bool) -> Self {
self.data.insert(key.into(), Value::Bool(value));
self
}
#[must_use]
pub const fn required(mut self, required: bool) -> Self {
self.required = required;
self
}
#[must_use]
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
#[must_use]
pub fn build(self) -> MemorySource {
MemorySource {
data: self.data,
required: self.required,
name: self.name,
}
}
}
#[derive(Debug, Clone)]
pub struct MemorySource {
data: HashMap<String, Value>,
required: bool,
name: Option<String>,
}
impl MemorySource {
#[must_use]
pub fn new() -> Self {
Self {
data: HashMap::new(),
required: false,
name: None,
}
}
#[must_use]
pub fn builder() -> MemorySourceBuilder {
MemorySourceBuilder::new()
}
#[must_use]
pub fn from_json(value: Value) -> Self {
let data = if let Value::Object(map) = value {
map.into_iter().collect()
} else {
let mut map = HashMap::new();
map.insert("value".to_string(), value);
map
};
Self {
data,
required: false,
name: None,
}
}
pub fn from_serializable<T: serde::Serialize>(value: &T) -> Result<Self> {
let json =
serde_json::to_value(value).map_err(|e| SourceError::serialization(&e.to_string()))?;
Ok(Self::from_json(json))
}
#[must_use]
pub fn get(&self, key: &str) -> Option<&Value> {
self.data.get(key)
}
#[must_use]
pub fn contains(&self, key: &str) -> bool {
self.data.contains_key(key)
}
#[must_use]
pub fn len(&self) -> usize {
self.data.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn insert(&mut self, key: impl Into<String>, value: Value) {
self.data.insert(key.into(), value);
}
pub fn remove(&mut self, key: &str) -> Option<Value> {
self.data.remove(key)
}
pub fn clear(&mut self) {
self.data.clear();
}
fn to_json_object(&self) -> Value {
Value::Object(self.data.clone().into_iter().collect())
}
}
impl Default for MemorySource {
fn default() -> Self {
Self::new()
}
}
impl Source for MemorySource {
fn kind(&self) -> SourceKind {
SourceKind::Memory
}
fn metadata(&self) -> SourceMetadata {
SourceMetadata::new(self.name.clone().unwrap_or_else(|| "memory".to_string()))
.with_priority(50)
.with_optional(!self.required)
}
fn load_raw(&self) -> Result<RawContent> {
if self.data.is_empty() && self.required {
return Err(SourceError::not_found("Memory source is empty"));
}
let json = self.to_json_object();
let content =
serde_json::to_string(&json).map_err(|e| SourceError::serialization(&e.to_string()))?;
Ok(RawContent::from_string(content))
}
fn detect_format(&self) -> Option<Format> {
Some(Format::Json)
}
fn is_required(&self) -> bool {
self.required
}
}
#[cfg(test)]
#[allow(clippy::items_after_statements)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_memory_source_new() {
let source = MemorySource::new();
assert!(source.is_empty());
assert!(!source.is_required());
}
#[test]
fn test_memory_source_builder() {
let source = MemorySource::builder()
.set("name", "test")
.set_number("port", 8080.0)
.set_bool("debug", true)
.required(true)
.build();
assert!(source.is_required());
assert_eq!(source.len(), 3);
}
#[test]
fn test_memory_source_from_json() {
let json = json!({
"name": "myapp",
"version": "1.0.0"
});
let source = MemorySource::from_json(json);
assert_eq!(source.len(), 2);
assert_eq!(source.get("name"), Some(&json!("myapp")));
}
#[test]
fn test_memory_source_from_serializable() {
#[derive(Debug, serde::Serialize)]
struct Config {
name: String,
value: u32,
}
let config = Config {
name: "test".to_string(),
value: 42,
};
let source = MemorySource::from_serializable(&config).unwrap();
assert_eq!(source.get("name"), Some(&json!("test")));
assert_eq!(source.get("value"), Some(&json!(42)));
}
#[test]
fn test_memory_source_load_raw() {
let source = MemorySource::builder()
.set("name", "test-app")
.insert("value", serde_json::json!(42))
.build();
let raw = source.load_raw().unwrap();
let content = raw.as_str().unwrap();
#[derive(Debug, serde::Deserialize, PartialEq)]
struct TestConfig {
name: String,
value: u32,
}
let config: TestConfig = serde_json::from_str(content.as_ref()).unwrap();
assert_eq!(config.name, "test-app");
assert_eq!(config.value, 42);
}
#[test]
fn test_memory_source_load_nested() {
let source = MemorySource::builder()
.insert(
"database",
json!({
"host": "localhost",
"port": 5432
}),
)
.build();
let raw = source.load_raw().unwrap();
let content = raw.as_str().unwrap();
#[derive(Debug, serde::Deserialize, PartialEq)]
struct Database {
host: String,
port: u16,
}
#[derive(Debug, serde::Deserialize, PartialEq)]
struct TestConfig {
database: Database,
}
let config: TestConfig = serde_json::from_str(content.as_ref()).unwrap();
assert_eq!(config.database.host, "localhost");
assert_eq!(config.database.port, 5432);
}
#[test]
fn test_memory_source_modify() {
let mut source = MemorySource::new();
source.insert("key", json!("value"));
assert!(source.contains("key"));
source.remove("key");
assert!(!source.contains("key"));
}
#[test]
fn test_memory_source_clear() {
let mut source = MemorySource::builder().set("a", "1").set("b", "2").build();
source.clear();
assert!(source.is_empty());
}
#[test]
fn test_memory_source_empty_required() {
let source = MemorySource::builder().required(true).build();
let result = source.load_raw();
assert!(result.is_err());
}
#[test]
fn test_memory_source_metadata() {
let source = MemorySource::builder().name("test-source").build();
let meta = source.metadata();
assert_eq!(meta.name, "test-source");
assert_eq!(source.kind(), SourceKind::Memory);
assert_eq!(meta.priority, 50);
}
#[test]
fn test_memory_source_default_name() {
let source = MemorySource::new();
let meta = source.metadata();
assert_eq!(meta.name, "memory");
}
#[test]
fn test_memory_source_clone() {
let source = MemorySource::builder().set("name", "test").build();
let cloned = source.clone();
assert_eq!(cloned.len(), source.len());
}
}