Skip to content

Instantly share code, notes, and snippets.

@rust-play
Created December 6, 2021 03:43
Show Gist options
  • Select an option

  • Save rust-play/a91b14b9abe30998e226a9730e0e18b8 to your computer and use it in GitHub Desktop.

Select an option

Save rust-play/a91b14b9abe30998e226a9730e0e18b8 to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
use std::marker::PhantomData;
#[allow(dead_code)]
fn main() {
let go = Builder::up()
.left(One { _ghost: PhantomData::<Yes> })
.build();
println!("{:#?}", go);
}
#[derive(Debug, PartialEq, Clone)]
struct Yes;
#[derive(Debug, PartialEq, Clone)]
struct No;
#[derive(Debug, PartialEq, Clone, Default)]
struct One<MAYBE> {
_ghost: PhantomData<MAYBE>,
}
#[derive(Debug, PartialEq, Clone, Default)]
struct Two<MAYBE> {
_ghost: PhantomData<MAYBE>,
}
#[derive(Debug, PartialEq, Clone, Default)]
struct Three<MAYBE> {
_ghost: PhantomData<MAYBE>,
}
#[derive(Debug, Clone)]
enum Builder<LEFT, MIDDLE, RIGHT> {
Up,
On(On<LEFT, MIDDLE, RIGHT>),
}
#[derive(Debug, PartialEq, Clone)]
struct Up;
#[derive(Debug, PartialEq, Clone)]
struct On<LEFT, MIDDLE, RIGHT> {
one: Option<One<LEFT>>,
two: Option<Two<MIDDLE>>,
three: Option<Three<RIGHT>>,
}
#[derive(Debug, PartialEq, Clone)]
struct Go {
one: One<Yes>,
two: Option<Two<Yes>>,
three: Option<Three<Yes>>,
}
impl Builder<No, No, No> {
pub fn up() -> Self {
Builder::Up
}
}
#[allow(unreachable_patterns)]
impl<LEFT, MIDDLE> Builder<LEFT, MIDDLE, No> {
pub fn right(self, three: Three<Yes>) -> Builder<LEFT, MIDDLE, Yes> {
match self {
Builder::Up => Builder::On(On {
one: None,
two: None,
three: Some(three),
}),
Builder::On(on) => Builder::On(On {
one: on.one,
two: on.two,
three: Some(three),
}),
_ => unreachable!(),
}
}
}
#[allow(unreachable_patterns)]
impl<MIDDLE, RIGHT> Builder<No, MIDDLE, RIGHT> {
pub fn left(self, one: One<Yes>) -> Builder<Yes, MIDDLE, RIGHT> {
match self {
Builder::Up => Builder::On(On {
one: Some(one),
two: None,
three: None,
}),
Builder::On(on) => Builder::On(On {
one: Some(one),
two: on.two,
three: on.three,
}),
_ => unreachable!(),
}
}
}
#[allow(unreachable_patterns)]
impl<LEFT, RIGHT> Builder<LEFT, No, RIGHT> {
pub fn middle(self, two: Two<Yes>) -> Builder<LEFT, Yes, RIGHT> {
match self {
Builder::Up => Builder::On(On {
one: None,
two: Some(two),
three: None,
}),
Builder::On(on) => Builder::On(On {
one: on.one,
two: Some(two),
three: on.three,
}),
_ => unreachable!(),
}
}
}
#[allow(unreachable_patterns)]
impl Builder<Yes, Yes, Yes> {
pub fn build(self) -> Go {
match self {
Builder::On(On {
one: Some(one),
two,
three,
}) => Go { one, two, three },
_ => unreachable!(),
}
}
}
#[allow(unreachable_patterns)]
impl Builder<Yes, Yes, No> {
pub fn build(self) -> Go {
match self {
Builder::On(On {
one: Some(one),
two,
..
}) => Go {
one,
two,
three: None,
},
_ => unreachable!(),
}
}
}
#[allow(unreachable_patterns)]
impl Builder<Yes, No, Yes> {
pub fn build(self) -> Go {
match self {
Builder::On(On {
one: Some(one),
three,
..
}) => Go {
one,
two: None,
three,
},
_ => unreachable!(),
}
}
}
#[allow(unreachable_patterns)]
impl Builder<Yes, No, No> {
pub fn build(self) -> Go {
match self {
Builder::On(On { one: Some(one), .. }) => Go {
one,
two: None,
three: None,
},
_ => unreachable!(),
}
}
}
#[cfg(test)]
mod tests {
use std::marker::PhantomData;
use super::{Builder, One, Three, Two};
#[test]
fn build_only() {
Builder::up()
.left(One {
_ghost: PhantomData,
})
.right(Three {
_ghost: PhantomData,
})
.middle(Two {
_ghost: PhantomData,
})
.build();
}
#[test]
fn build_from_unknown_options() {
let maybe_one = Some(One {
_ghost: PhantomData,
});
let maybe_two = Some(Two {
_ghost: PhantomData,
});
let maybe_three = Some(Three {
_ghost: PhantomData,
});
///////////////////////////////////////
// following doesn't compile:
// "if and else have incompatible types"
///////////////////////////////////////
// let builder = Builder::up();
// let builder = if let Some(one) = maybe_one {
// builder.on.left(one)
// } else { builder };
// let builder = if let Some(two) = maybe_two {
// builder.right(two)
// } else { builder};
///////////////////////////////////////
let result = match (Builder::up(), maybe_one, maybe_two, maybe_three) {
(builder, Some(one), None, None) => builder.left(one).build(),
(builder, Some(one), Some(two), Some(three)) => {
builder.left(one).right(three).middle(two).build()
}
(_, None, None, None) => panic!("no arguments for builder"),
(_, _, _, _) => panic!("too few argument or invalid combinations (use either only left() or all: left(), right() and middle())"),
};
assert_eq!(
result.one,
One {
_ghost: PhantomData
}
);
assert_eq!(
result.two,
Some(Two {
_ghost: PhantomData
})
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment