Created
November 22, 2023 12:30
-
-
Save absolutejam/e6ed4baacfeaa7a75d4290ad3ecc5683 to your computer and use it in GitHub Desktop.
Generic loaders
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use serde::{de::DeserializeOwned, Deserialize, Deserializer}; | |
// | |
struct Metadata { | |
name: &'static str, | |
provider: &'static str, | |
} | |
// | |
trait ClusterProvider { | |
fn get_name(&self) -> &str; | |
} | |
// | |
trait Loader<Value> | |
where | |
Value: DeserializeOwned + for<'de> Deserializer<'de>, | |
{ | |
type Loaded; | |
fn metadata(&self) -> &'static Metadata; | |
fn load<'de>(&self, value: Value) -> Result<Self::Loaded, <Value as Deserializer<'de>>::Error>; | |
} | |
// | |
#[derive(Deserialize)] | |
struct HelmClusterProvider { | |
name: String, | |
} | |
impl ClusterProvider for HelmClusterProvider { | |
fn get_name(&self) -> &str { | |
&self.name | |
} | |
} | |
#[derive(Deserialize)] | |
struct ShellClusterProvider { | |
shell: String, | |
} | |
impl ClusterProvider for ShellClusterProvider { | |
fn get_name(&self) -> &str { | |
&self.shell | |
} | |
} | |
// | |
struct HelmLoader; | |
impl<Value> Loader<Value> for HelmLoader | |
where | |
Value: DeserializeOwned + for<'de> Deserializer<'de>, | |
{ | |
type Loaded = Box<dyn ClusterProvider>; | |
fn metadata(&self) -> &'static Metadata { | |
&Metadata { | |
name: "helm", | |
provider: "kube_kata", | |
} | |
} | |
fn load<'de>(&self, value: Value) -> Result<Self::Loaded, <Value as Deserializer<'de>>::Error> { | |
let x = HelmClusterProvider::deserialize(value)?; | |
Ok(Box::new(x)) | |
} | |
} | |
struct ShellLoader; | |
impl<Value> Loader<Value> for ShellLoader | |
where | |
Value: DeserializeOwned + for<'de> Deserializer<'de>, | |
{ | |
type Loaded = Box<dyn ClusterProvider>; | |
fn metadata(&self) -> &'static Metadata { | |
&Metadata { | |
name: "shell", | |
provider: "kube_kata", | |
} | |
} | |
fn load<'de>(&self, value: Value) -> Result<Self::Loaded, <Value as Deserializer<'de>>::Error> { | |
let x = ShellClusterProvider::deserialize(value)?; | |
Ok(Box::new(x)) | |
} | |
} | |
// | |
struct LoaderStore<T, Value> { | |
items: Vec<Box<dyn Loader<Value, Loaded = T>>>, | |
} | |
impl<T, Value> LoaderStore<T, Value> | |
where | |
Value: DeserializeOwned + for<'de> Deserializer<'de>, | |
{ | |
fn new() -> Self { | |
Self { | |
items: Default::default(), | |
} | |
} | |
fn register( | |
&mut self, | |
loader: impl Loader<Value, Loaded = T> + 'static, | |
) -> Result<(), anyhow::Error> | |
where | |
Value: DeserializeOwned + for<'de> Deserializer<'de>, | |
{ | |
self.items.push(Box::new(loader)); | |
Ok(()) | |
} | |
fn try_load(&self, value: Value) -> Result<T, anyhow::Error> | |
where | |
Value: Clone, | |
{ | |
for loader in self.items.iter() { | |
if let Ok(loaded) = loader.load(value.clone()) { | |
return Ok(loaded); | |
} | |
} | |
Err(anyhow::anyhow!("couldn't load")) | |
} | |
} | |
// | |
struct LoaderRegistry<Value> { | |
cluster_providers: LoaderStore<Box<dyn ClusterProvider>, Value>, | |
} | |
impl<Value> LoaderRegistry<Value> | |
where | |
Value: DeserializeOwned + for<'de> Deserializer<'de>, | |
{ | |
fn new() -> Self { | |
LoaderRegistry { | |
cluster_providers: LoaderStore::new(), | |
} | |
} | |
fn register_cluster_provider( | |
&mut self, | |
cluster_provider_loader: impl Loader<Value, Loaded = Box<dyn ClusterProvider>> + 'static, | |
) -> Result<&Self, anyhow::Error> | |
where | |
Value: DeserializeOwned + for<'de> Deserializer<'de>, | |
{ | |
self.cluster_providers.register(cluster_provider_loader)?; | |
Ok(self) | |
} | |
} | |
// | |
type ClusterProviderLoader<Value> = Box<dyn Loader<Value, Loaded = Box<dyn ClusterProvider>>>; | |
fn build_registry<Value>() -> LoaderRegistry<Value> | |
where | |
Value: DeserializeOwned + for<'de> Deserializer<'de>, | |
{ | |
let mut registry = LoaderRegistry::<Value>::new(); | |
registry.register_cluster_provider(HelmLoader).unwrap(); | |
registry.register_cluster_provider(ShellLoader).unwrap(); | |
registry | |
} | |
fn main() { | |
// NOTE: These are monomorphised when used by an Value implementation | |
let loaders: Vec<ClusterProviderLoader<_>> = vec![Box::new(HelmLoader), Box::new(ShellLoader)]; | |
let loaders_2: Vec<ClusterProviderLoader<_>> = | |
vec![Box::new(HelmLoader), Box::new(ShellLoader)]; | |
// Turn it into a value (YAML) | |
let yaml_value: serde_yaml::Value = serde_yaml::from_str("shell: lol").unwrap(); | |
let json_value: serde_json::Value = serde_json::from_str("{ \"shell\": \"lol\" }").unwrap(); | |
// Single loader | |
let loaded: Box<dyn ClusterProvider> = (loaders[1]).load(yaml_value.clone()).unwrap(); | |
println!("ClusterProvider: Name = {}", loaded.get_name()); | |
let loaded: Box<dyn ClusterProvider> = (loaders_2[1]).load(json_value.clone()).unwrap(); | |
println!("ClusterProvider: Name = {}", loaded.get_name()); | |
// Using the registry | |
let yaml_registry = build_registry::<serde_yaml::Value>(); | |
let loaded = yaml_registry | |
.cluster_providers | |
.try_load(yaml_value) | |
.unwrap(); | |
println!("ClusterProvider: Name = {}", loaded.get_name()); | |
let json_registry = build_registry::<serde_json::Value>(); | |
let loaded = json_registry | |
.cluster_providers | |
.try_load(json_value) | |
.unwrap(); | |
println!("ClusterProvider: Name = {}", loaded.get_name()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment