Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/wingfoil/src/fluent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -887,7 +887,7 @@ pub trait StreamOps<T>: Sized {
/// true))` ticks `v`; `Ok((_, false))` means no value this tick and the
/// run continues; `Err(e)` aborts the run with `e` as context — `false`
/// and `Err` are not interchangeable.
fn try_filter_map<B, F>(&self, f: F) -> Stream<B>
fn try_map_filter<B, F>(&self, f: F) -> Stream<B>
where
B: Clone + Default + 'static,
F: Fn(&T) -> Result<(B, bool)> + 'static;
Expand Down Expand Up @@ -1302,7 +1302,7 @@ impl<T: 'static> StreamOps<T> for Stream<T> {

__wf_fluent_map_filter!(T);

__wf_fluent_try_filter_map!(T);
__wf_fluent_try_map_filter!(T);

fn with_time(&self) -> Stream<(NanoTime, T)>
where
Expand Down
6 changes: 3 additions & 3 deletions crates/wingfoil/src/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,10 @@ where
/// `false` and `Err` are not interchangeable: `Ok((_, false))` means "no
/// value this tick" and the run continues; `Err(e)` means the run is broken
/// and aborts with `e` as context.
pub struct TryFilterMap<A, B, F>(PhantomData<(A, B, F)>);
pub struct TryMapFilter<A, B, F>(PhantomData<(A, B, F)>);

#[op(build = try_filter_map, fluent)]
impl<A, B, F> Op for TryFilterMap<A, B, F>
#[op(build = try_map_filter, fluent)]
impl<A, B, F> Op for TryMapFilter<A, B, F>
where
A: 'static,
B: Clone + 'static,
Expand Down
7 changes: 6 additions & 1 deletion crates/wingfoil/src/signal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ impl<T: 'static> Signal<T> {
__wf_signal_map!(T);
__wf_signal_try_map!(T);
__wf_signal_map_filter!(T);
__wf_signal_try_filter_map!(T);
__wf_signal_try_map_filter!(T);
__wf_signal_fold!(T);
__wf_signal_scan!(T);
__wf_signal_for_each!(T);
Expand Down Expand Up @@ -178,6 +178,11 @@ impl<T: 'static> Signal<T> {
/// Map and filter with an `Option` (the legacy `filter_map`): tick the
/// returned `Some`, drop `None`. Delegates to the fluent
/// [`map_filter`](StreamOps::map_filter).
///
/// Not to be confused with
/// [`try_map_filter`](StreamOps::try_map_filter), which is the fallible
/// twin of `map_filter` and keeps its `(value, emit?)` shape rather than
/// this `Option` one.
pub fn filter_map<B, F>(&self, f: F) -> Signal<B>
where
B: Clone + Default + 'static,
Expand Down
8 changes: 4 additions & 4 deletions crates/wingfoil/tests/catalog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,17 +279,17 @@ fn map_filter_maps_and_filters() {
assert_eq!(vec![1, 9, 25], r.value(&acc));
}

/// `try_filter_map` is `map_filter`'s fallible twin: squares of odds, same
/// `try_map_filter` is `map_filter`'s fallible twin: squares of odds, same
/// as the test above, but through a closure that returns `Result<(B, bool)>`
/// and never actually fails here — the abort path is
/// `try_filter_map_err_aborts_run` in `fallibility.rs`. Each surviving value
/// `try_map_filter_err_aborts_run` in `fallibility.rs`. Each surviving value
/// keeps its original tick time, since the op suppresses ticks, not time.
#[test]
fn try_filter_map_maps_and_filters_with_tick_times() {
fn try_map_filter_maps_and_filters_with_tick_times() {
let g = GraphBuilder::new();
let count = g.ticker(Duration::from_nanos(10)).count(); // 1..=6
let acc = count
.try_filter_map(|i| Ok((i * i, i % 2 == 1))) // squares of odds: 1, 9, 25
.try_map_filter(|i| Ok((i * i, i % 2 == 1))) // squares of odds: 1, 9, 25
.with_time()
.accumulate();
let mut r = g.build();
Expand Down
10 changes: 5 additions & 5 deletions crates/wingfoil/tests/fallibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,17 @@ fn cycle_error_aborts_with_context_and_runs_teardown() {
assert_eq!(Some(2), *torn_down.borrow());
}

/// `try_filter_map` is `try_map`'s map-and-filter twin: an `Err` aborts the
/// `try_map_filter` is `try_map`'s map-and-filter twin: an `Err` aborts the
/// run with context naming the node, same as `try_map` above — the
/// `Ok((_, false))` filtering path is not a substitute for propagating a
/// real error, and this pins that they behave differently.
#[test]
fn try_filter_map_err_aborts_run() {
fn try_map_filter_err_aborts_run() {
let g = GraphBuilder::new();
let count = g.ticker(Duration::from_nanos(10)).count();
// Cycles 1 and 2 succeed (odd values kept, even ones filtered); cycle 3
// errors instead of filtering.
let filtered = count.try_filter_map(|i: &u64| {
let filtered = count.try_map_filter(|i: &u64| {
if *i >= 3 {
bail!("boom at count {i}");
}
Expand All @@ -79,10 +79,10 @@ fn try_filter_map_err_aborts_run() {
let mut r = g.build();
let result = r.run(HISTORICAL, RunFor::Cycles(10));

let err = result.expect_err("the run must abort when try_filter_map fails");
let err = result.expect_err("the run must abort when try_map_filter fails");
let msg = format!("{err:#}");
assert!(
msg.contains("TryFilterMap"),
msg.contains("TryMapFilter"),
"error should name the node: {msg}"
);
assert!(
Expand Down
4 changes: 2 additions & 2 deletions crates/wingfoil/tests/op_completeness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,12 +283,12 @@ wingfoil::nitro! {
}
}

// Fallible active surface: `try_map`, `try_filter_map`, `try_join`.
// Fallible active surface: `try_map`, `try_map_filter`, `try_join`.
wingfoil::nitro! {
fn surface_fallible(g: &GraphBuilder) -> Stream<Vec<u64>> {
let count = g.ticker(P).count();
let tried = count.try_map(|i| Ok(i + 1));
let filtered = tried.try_filter_map(|i: &u64| Ok((*i, i.is_multiple_of(2))));
let filtered = tried.try_map_filter(|i: &u64| Ok((*i, i.is_multiple_of(2))));
let other = count.map(|i| i * 2);
let joined = filtered.try_join(&other, |x: &u64, y: &u64| Ok(x + y));
let out = joined.accumulate();
Expand Down
13 changes: 13 additions & 0 deletions crates/wingfoil/tests/signal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,19 @@ fn legacy_try_map_transforms_values() {
assert_eq!(vec![2, 4, 6], doubled.peek_value());
}

/// `try_map_filter` is `map_filter`'s fallible twin on the facade: the `Ok`
/// path maps and drops in one pass. Distinct from `filter_map` below, which
/// is `Option`-shaped rather than `(value, emit?)`-shaped.
#[test]
fn legacy_try_map_filter_maps_and_filters() {
let odds = ticker(Duration::from_nanos(100))
.count()
.try_map_filter(|i: &u64| Ok((i * i, i % 2 == 1)))
.accumulate();
odds.run(HISTORICAL, RunFor::Cycles(6)).unwrap();
assert_eq!(vec![1, 9, 25], odds.peek_value());
}

/// `map_filter` maps and drops in one pass, preserving values **and** the tick
/// times of the values it keeps.
#[test]
Expand Down
Loading