Skip to content

Instantly share code, notes, and snippets.

@oscartbeaumont
Created July 21, 2023 16:48
Show Gist options
  • Save oscartbeaumont/6718282b6e4e17be25df12e7950cbaf6 to your computer and use it in GitHub Desktop.
Save oscartbeaumont/6718282b6e4e17be25df12e7950cbaf6 to your computer and use it in GitHub Desktop.
Generic-generic Linked List
use std::fmt::Debug;
pub struct Request(String);
pub trait ContentType: 'static {
type Result;
// `None` if not matched.
fn exec(&self, req: &Request) -> Option<Self::Result>;
}
pub struct One;
impl ContentType for One {
type Result = String;
fn exec(&self, req: &Request) -> Option<Self::Result> {
if req.0 == "A" {
return Some("A".into());
}
return None;
}
}
pub struct Two;
impl ContentType for Two {
type Result = i32;
fn exec(&self, req: &Request) -> Option<Self::Result> {
if req.0 == "B" {
return Some(5);
}
return None;
}
}
pub struct Three;
impl ContentType for Three {
type Result = bool;
fn exec(&self, req: &Request) -> Option<Self::Result> {
if req.0 == "C" {
return Some(true);
}
return None;
}
}
pub enum Either<L, R> {
Left(L),
Right(R),
}
impl<L: Debug, R: Debug> Debug for Either<L, R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Either::Left(l) => write!(f, "Left({:?})", l),
Either::Right(r) => write!(f, "Right({:?})", r),
}
}
}
pub struct Both<L, R> {
left: L,
right: R,
}
impl<L: Debug, R: Debug> Debug for Both<L, R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?} {:?}", self.left, self.right)
}
}
pub trait DynContentType<T> {
fn exec(&self, req: &Request) -> Option<T>;
}
impl<T, F: Fn(&Request) -> Option<T>> DynContentType<T> for F {
fn exec(&self, req: &Request) -> Option<T> {
self(req)
}
}
pub trait ChainedContentType: 'static {
type Next<N>;
type Result;
fn next<N>(self, v: N) -> Self::Next<N>;
fn build<R>(self) -> Vec<Box<dyn DynContentType<Self::Result>>>;
}
impl ChainedContentType for () {
type Next<N> = N;
type Result = ();
fn next<N>(self, v: N) -> Self::Next<N> {
v
}
fn build<R>(self) -> Vec<Box<dyn DynContentType<Self::Result>>> {
unreachable!();
}
}
impl<C: ContentType> ChainedContentType for C {
type Next<N> = Both<C, N>;
type Result = C::Result;
fn next<N>(self, v: N) -> Self::Next<N> {
Both {
left: self,
right: v,
}
}
fn build<R>(self) -> Vec<Box<dyn DynContentType<Self::Result>>> {
let a: Box<dyn DynContentType<Self::Result>> =
Box::new(move |req: &Request| self.exec(req));
vec![a]
}
}
impl<L: ChainedContentType, C: ContentType> ChainedContentType for Both<L, C> {
type Next<N> = Both<L::Next<C>, N>;
type Result = Either<L::Result, C::Result>;
fn next<N>(self, v: N) -> Self::Next<N> {
Both {
left: self.left.next(self.right),
right: v,
}
}
fn build<R>(self) -> Vec<Box<dyn DynContentType<Self::Result>>> {
let mut types: Vec<Box<dyn DynContentType<Self::Result>>> = self
.left
.build::<R>()
.into_iter()
.map(|i| {
let a: Box<dyn DynContentType<Self::Result>> = Box::new(move |req: &Request| {
if let Some(v) = i.exec(req) {
return Some(Either::Left(v));
}
None
});
a
})
.collect();
types.extend(&mut self.right.build::<R>().into_iter().map(|i| {
let a: Box<dyn DynContentType<Self::Result>> = Box::new(move |req: &Request| {
if let Some(v) = i.exec(req) {
return Some(Either::Right(v));
}
None
});
a
}));
types
}
}
pub struct Registry<B> {
b: B,
}
impl Registry<()> {
pub fn new() -> Self {
Self { b: () }
}
}
impl<T> Registry<T>
where
T: ChainedContentType,
{
pub fn register<C: ContentType>(self, content_type: C) -> Registry<T::Next<C>> {
Registry {
b: self.b.next(content_type),
}
}
pub fn build(self) -> BuiltRegistry<T::Result> {
BuiltRegistry(self.b.build::<T::Result>())
}
}
pub struct BuiltRegistry<B>(Vec<Box<dyn DynContentType<B>>>);
impl<B> BuiltRegistry<B> {
pub fn exec(self, req: Request) -> Option<B> {
for ty in self.0.iter() {
if let Some(v) = ty.exec(&req) {
return Some(v);
}
}
return None;
}
}
fn main() {
let v = Registry::new()
.register(One)
.register(Two)
.register(Three)
.build()
.exec(Request("A".into()));
println!("{:?}", v);
let v = Registry::new()
.register(One)
.register(Two)
.register(Three)
.build()
.exec(Request("B".into()));
println!("{:?}", v);
let v = Registry::new()
.register(One)
.register(Two)
.register(Three)
.build()
.exec(Request("C".into()));
println!("{:?}", v);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment