use std::sync::Arc; pub trait Component { fn name(&self) -> String; fn validate(&self, _value: &serde_yaml::Value) -> anyhow::Result<()> { Ok(()) } fn render_value( &self, environment: &str, _value: &serde_yaml::Value, ) -> Option> { None } /// First return is name, second is contents fn render( &self, environment: &str, _value: &serde_yaml::Value, ) -> Option> { None } } #[derive(Clone)] pub struct ConcreteComponent { inner: Arc, } impl std::ops::Deref for ConcreteComponent { type Target = Arc; fn deref(&self) -> &Self::Target { &self.inner } } impl ConcreteComponent { pub fn new(t: T) -> Self { Self { inner: Arc::new(t) } } } pub trait IntoComponent { fn into_component(self) -> ConcreteComponent; } impl IntoComponent for ConcreteComponent { fn into_component(self) -> ConcreteComponent { self } } impl IntoComponent for T { fn into_component(self) -> ConcreteComponent { ConcreteComponent::new(self) } } #[cfg(test)] mod test { use similar_asserts::assert_eq; use super::*; pub struct Database {} impl Component for Database { fn name(&self) -> String { "cuddle/database".into() } } #[test] fn can_transform_into_concrete() { let database = Database {}; assert_eq!("cuddle/database", database.into_component().name()); } }