Skip to content

Instantly share code, notes, and snippets.

@absolutejam
Created November 22, 2023 12:30
Show Gist options
  • Save absolutejam/e6ed4baacfeaa7a75d4290ad3ecc5683 to your computer and use it in GitHub Desktop.
Save absolutejam/e6ed4baacfeaa7a75d4290ad3ecc5683 to your computer and use it in GitHub Desktop.
Generic loaders
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