Skip to main content

meld_core/
lib.rs

1use std::sync::Arc;
2
3use thiserror::Error;
4
5pub mod auth;
6
7pub type MeldResult<T> = Result<T, MeldError>;
8pub type AlloyResult<T> = MeldResult<T>;
9
10#[derive(Debug, Clone)]
11pub struct AppConfig {
12    pub service_name: String,
13    pub environment: String,
14}
15
16impl AppConfig {
17    pub fn local(service_name: impl Into<String>) -> Self {
18        Self {
19            service_name: service_name.into(),
20            environment: "local".to_string(),
21        }
22    }
23}
24
25#[derive(Debug, Error)]
26pub enum MeldError {
27    #[error("validation error: {0}")]
28    Validation(String),
29    #[error("internal error: {0}")]
30    Internal(String),
31}
32
33pub type AlloyError = MeldError;
34
35pub trait GreetingEngine: Send + Sync {
36    fn greet(&self, name: &str) -> MeldResult<String>;
37}
38
39pub trait MetricsSink: Send + Sync {
40    fn incr_counter(&self, name: &str);
41}
42
43#[derive(Debug, Default)]
44pub struct NoopMetrics;
45
46impl MetricsSink for NoopMetrics {
47    fn incr_counter(&self, _name: &str) {}
48}
49
50#[derive(Debug, Clone)]
51pub struct StaticGreetingEngine {
52    prefix: String,
53}
54
55impl StaticGreetingEngine {
56    pub fn new(prefix: impl Into<String>) -> Self {
57        Self {
58            prefix: prefix.into(),
59        }
60    }
61}
62
63impl GreetingEngine for StaticGreetingEngine {
64    fn greet(&self, name: &str) -> MeldResult<String> {
65        if name.trim().is_empty() {
66            return Err(MeldError::Validation("name must not be empty".to_string()));
67        }
68        Ok(format!("{}, {}!", self.prefix, name))
69    }
70}
71
72#[derive(Clone)]
73pub struct AppState {
74    pub config: AppConfig,
75    pub greeter: Arc<dyn GreetingEngine>,
76    pub metrics: Arc<dyn MetricsSink>,
77}
78
79impl AppState {
80    pub fn new(
81        config: AppConfig,
82        greeter: Arc<dyn GreetingEngine>,
83        metrics: Arc<dyn MetricsSink>,
84    ) -> Self {
85        Self {
86            config,
87            greeter,
88            metrics,
89        }
90    }
91
92    pub fn local(service_name: impl Into<String>) -> Self {
93        Self {
94            config: AppConfig::local(service_name),
95            greeter: Arc::new(StaticGreetingEngine::new("Hello")),
96            metrics: Arc::new(NoopMetrics),
97        }
98    }
99
100    pub fn greet(&self, name: &str) -> MeldResult<String> {
101        self.metrics.incr_counter("greet.requests");
102        self.greeter.greet(name)
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[test]
111    fn greet_rejects_empty_name() {
112        let state = AppState::local("meld-test");
113        let result = state.greet("  ");
114        assert!(matches!(result, Err(MeldError::Validation(_))));
115    }
116
117    #[test]
118    fn greet_returns_message() {
119        let state = AppState::local("meld-test");
120        let result = state.greet("Rust");
121        assert_eq!(result.expect("must greet"), "Hello, Rust!");
122    }
123}