Skip to content

Commit

Permalink
feat(hydroflow_lang): allow demux_enum to have any number of outputs,
Browse files Browse the repository at this point in the history
fix #1329 (#1409)
  • Loading branch information
MingweiSamuel committed Aug 23, 2024
1 parent fa41720 commit 9e5f58e
Show file tree
Hide file tree
Showing 15 changed files with 335 additions and 98 deletions.
21 changes: 11 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 16 additions & 4 deletions hydroflow/src/util/demux_enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,26 @@ pub use hydroflow_macro::DemuxEnum;
/// The derive will implement this such that `Outputs` can be any tuple where each item is a
/// `Pusherator` that corresponds to each of the variants of the tuple, in alphabetic order.
#[diagnostic::on_unimplemented(
note = "Ensure there is exactly one output for each enum variant.",
note = "Ensure that the type for each output is a tuple of the field for the variant: `()`, `(a,)`, or `(a, b, ...)`."
note = "ensure there is exactly one output for each enum variant.",
note = "ensure that the type for each output is a tuple of the field for the variant: `()`, `(a,)`, or `(a, b, ...)`."
)]
pub trait DemuxEnum<Outputs>: DemuxEnumBase {
/// Pushes self into the corresponding output pusherator in `outputs`.
fn demux_enum(self, outputs: &mut Outputs);
}

/// Base implementation to constrain that `DemuxEnum<SOMETHING>` is implemented.
#[diagnostic::on_unimplemented(note = "Use `#[derive(hydroflow::DemuxEnum)]`")]
/// Special case of [`DemuxEnum`] for when there is only one variant.
#[diagnostic::on_unimplemented(
note = "requires that the enum have only one variant.",
note = "ensure there are no missing outputs; there must be exactly one output for each enum variant."
)]
pub trait SingleVariant: DemuxEnumBase {
/// Output tuple type.
type Output;
/// Convert self into it's single variant tuple Output.
fn single_variant(self) -> Self::Output;
}

/// Base implementation to constrain that [`DemuxEnum<SOMETHING>`] is implemented.
#[diagnostic::on_unimplemented(note = "use `#[derive(hydroflow::DemuxEnum)]`")]
pub trait DemuxEnumBase {}
6 changes: 3 additions & 3 deletions hydroflow/tests/compile-fail/surface_demuxenum_notenum.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ error[E0277]: the trait bound `Shape: DemuxEnumBase` is not satisfied
12 | ]) -> demux_enum::<Shape>();
| ^^^^^ the trait `DemuxEnumBase` is not implemented for `Shape`
|
= note: Use `#[derive(hydroflow::DemuxEnum)]`
= note: use `#[derive(hydroflow::DemuxEnum)]`
note: required by a bound in `check_impl_demux_enum`
--> tests/compile-fail/surface_demuxenum_notenum.rs:12:28
|
Expand Down Expand Up @@ -61,5 +61,5 @@ error[E0277]: the trait bound `Shape: DemuxEnum<_>` is not satisfied
12 | ]) -> demux_enum::<Shape>();
| ^^^^^^^^^^^^^^^^^^^^^ the trait `DemuxEnum<_>` is not implemented for `Shape`
|
= note: Ensure there is exactly one output for each enum variant.
= note: Ensure that the type for each output is a tuple of the field for the variant: `()`, `(a,)`, or `(a, b, ...)`.
= note: ensure there is exactly one output for each enum variant.
= note: ensure that the type for each output is a tuple of the field for the variant: `()`, `(a,)`, or `(a, b, ...)`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use hydroflow::hydroflow_syntax;
use hydroflow::util::demux_enum::DemuxEnum;

fn main() {
#[derive(DemuxEnum)]
enum Shape {
Square(f64),
}

let mut df = hydroflow_syntax! {
my_demux = source_iter([
Shape::Square(9.0),
]) -> demux_enum::<Shape>();
my_demux[Square] -> for_each(std::mem::drop);
my_demux[Square] -> for_each(std::mem::drop);
};
df.run_available();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error: Output connection conflicts with below ($DIR/tests/compile-fail/surface_demuxenum_port_duplicate_one.rs:15:18) (1/2)
--> tests/compile-fail/surface_demuxenum_port_duplicate_one.rs:14:18
|
14 | my_demux[Square] -> for_each(std::mem::drop);
| ^^^^^^

error: Output connection conflicts with above ($DIR/tests/compile-fail/surface_demuxenum_port_duplicate_one.rs:14:18) (2/2)
--> tests/compile-fail/surface_demuxenum_port_duplicate_one.rs:15:18
|
15 | my_demux[Square] -> for_each(std::mem::drop);
| ^^^^^^
14 changes: 14 additions & 0 deletions hydroflow/tests/compile-fail/surface_demuxenum_port_extra_zero.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use hydroflow::util::demux_enum::DemuxEnum;
use hydroflow::hydroflow_syntax;

fn main() {
#[derive(DemuxEnum)]
enum Shape {
}

let mut df = hydroflow_syntax! {
my_demux = source_iter([]) -> demux_enum::<Shape>();
my_demux[Square] -> for_each(std::mem::drop);
};
df.run_available();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
error[E0599]: no variant named `Square` found for enum `Shape`
--> tests/compile-fail/surface_demuxenum_port_extra_zero.rs:11:18
|
6 | enum Shape {
| ---------- variant `Square` not found here
...
11 | my_demux[Square] -> for_each(std::mem::drop);
| ^^^^^^ variant not found in `Shape`

error[E0277]: the trait bound `Shape: SingleVariant` is not satisfied
--> tests/compile-fail/surface_demuxenum_port_extra_zero.rs:10:52
|
10 | my_demux = source_iter([]) -> demux_enum::<Shape>();
| ^^^^^ the trait `SingleVariant` is not implemented for `Shape`
|
= note: requires that the enum have only one variant.
= note: ensure there are no missing outputs; there must be exactly one output for each enum variant.

error[E0277]: the trait bound `Shape: SingleVariant` is not satisfied
--> tests/compile-fail/surface_demuxenum_port_extra_zero.rs:10:39
|
10 | my_demux = source_iter([]) -> demux_enum::<Shape>();
| ^^^^^^^^^^^^^^^^^^^^^ the trait `SingleVariant` is not implemented for `Shape`
|
= note: requires that the enum have only one variant.
= note: ensure there are no missing outputs; there must be exactly one output for each enum variant.

error[E0277]: the trait bound `Shape: SingleVariant` is not satisfied
--> tests/compile-fail/surface_demuxenum_port_extra_zero.rs:10:39
|
10 | my_demux = source_iter([]) -> demux_enum::<Shape>();
| _______________________________________^
11 | | my_demux[Square] -> for_each(std::mem::drop);
| |____________________________________________________^ the trait `SingleVariant` is not implemented for `Shape`
|
= note: requires that the enum have only one variant.
= note: ensure there are no missing outputs; there must be exactly one output for each enum variant.
19 changes: 19 additions & 0 deletions hydroflow/tests/compile-fail/surface_demuxenum_port_missing_one.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use hydroflow::util::demux_enum::DemuxEnum;
use hydroflow::hydroflow_syntax;

fn main() {
#[derive(DemuxEnum)]
enum Shape {
Square(f64),
Rectangle { w: f64, h: f64 },
}

let mut df = hydroflow_syntax! {
my_demux = source_iter([
Shape::Rectangle { w: 10.0, h: 8.0 },
Shape::Square(9.0),
]) -> demux_enum::<Shape>();
my_demux[Rectangle] -> for_each(std::mem::drop);
};
df.run_available();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
error[E0277]: the trait bound `Shape: SingleVariant` is not satisfied
--> tests/compile-fail/surface_demuxenum_port_missing_one.rs:15:28
|
15 | ]) -> demux_enum::<Shape>();
| ^^^^^ the trait `SingleVariant` is not implemented for `Shape`
|
= note: requires that the enum have only one variant.
= note: ensure there are no missing outputs; there must be exactly one output for each enum variant.

error[E0277]: the trait bound `Shape: SingleVariant` is not satisfied
--> tests/compile-fail/surface_demuxenum_port_missing_one.rs:15:15
|
15 | ]) -> demux_enum::<Shape>();
| ^^^^^^^^^^^^^^^^^^^^^ the trait `SingleVariant` is not implemented for `Shape`
|
= note: requires that the enum have only one variant.
= note: ensure there are no missing outputs; there must be exactly one output for each enum variant.

error[E0277]: the trait bound `Shape: SingleVariant` is not satisfied
--> tests/compile-fail/surface_demuxenum_port_missing_one.rs:15:15
|
15 | ]) -> demux_enum::<Shape>();
| _______________^
16 | | my_demux[Rectangle] -> for_each(std::mem::drop);
| |_______________________________________________________^ the trait `SingleVariant` is not implemented for `Shape`
|
= note: requires that the enum have only one variant.
= note: ensure there are no missing outputs; there must be exactly one output for each enum variant.
17 changes: 17 additions & 0 deletions hydroflow/tests/compile-fail/surface_demuxenum_port_wrong_one.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use hydroflow::util::demux_enum::DemuxEnum;
use hydroflow::hydroflow_syntax;

fn main() {
#[derive(DemuxEnum)]
enum Shape {
Square(f64),
}

let mut df = hydroflow_syntax! {
my_demux = source_iter([
Shape::Square(9.0),
]) -> demux_enum::<Shape>();
my_demux[Circle] -> for_each(std::mem::drop);
};
df.run_available();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error[E0599]: no variant named `Circle` found for enum `Shape`
--> tests/compile-fail/surface_demuxenum_port_wrong_one.rs:14:18
|
6 | enum Shape {
| ---------- variant `Circle` not found here
...
14 | my_demux[Circle] -> for_each(std::mem::drop);
| ^^^^^^ variant not found in `Shape`
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ error[E0277]: the trait bound `std::option::Option<()>: DemuxEnumBase` is not sa
17 | ]) -> demux_enum::<Option<()>>();
| ^^^^^^^^^^ the trait `DemuxEnumBase` is not implemented for `std::option::Option<()>`
|
= note: Use `#[derive(hydroflow::DemuxEnum)]`
= note: use `#[derive(hydroflow::DemuxEnum)]`
= help: the trait `DemuxEnumBase` is implemented for `Shape`
note: required by a bound in `check_impl_demux_enum`
--> tests/compile-fail/surface_demuxenum_wrongenum.rs:17:28
Expand Down Expand Up @@ -36,8 +36,8 @@ error[E0277]: the trait bound `std::option::Option<()>: DemuxEnum<_>` is not sat
17 | ]) -> demux_enum::<Option<()>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `DemuxEnum<_>` is not implemented for `std::option::Option<()>`
|
= note: Ensure there is exactly one output for each enum variant.
= note: Ensure that the type for each output is a tuple of the field for the variant: `()`, `(a,)`, or `(a, b, ...)`.
= note: ensure there is exactly one output for each enum variant.
= note: ensure that the type for each output is a tuple of the field for the variant: `()`, `(a,)`, or `(a, b, ...)`.
= help: the trait `DemuxEnum<(__PusheratorCircle, __PusheratorRectangle, __PusheratorSquare)>` is implemented for `Shape`

error[E0271]: type mismatch resolving `<impl Pusherator<Item = Option<()>> as Pusherator>::Item == Shape`
Expand Down
27 changes: 27 additions & 0 deletions hydroflow/tests/surface_demux_enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,30 @@ pub fn test_demux_enum_generic() {
test::<f32>(9., 10., 8., 5.);
test::<u32>(9, 10, 8, 5);
}

#[multiplatform_test]
fn test_zero_variants() {
#[derive(DemuxEnum)]
enum Never {}
let (_tx, rx) = hydroflow::util::unbounded_channel::<Never>();

let mut df = hydroflow_syntax! {
source_stream(rx)
-> demux_enum::<Never>();
};
df.run_available();
}

#[multiplatform_test]
fn test_one_variant() {
#[derive(DemuxEnum)]
enum Request<T> {
OnlyMessage(T),
}

let mut df = hydroflow_syntax! {
input = source_iter([Request::OnlyMessage("hi")]) -> demux_enum::<Request<&'static str>>();
input[OnlyMessage] -> assert_eq([("hi",)]);
};
df.run_available();
}
Loading

0 comments on commit 9e5f58e

Please sign in to comment.