Skip to main content

bi_consumer_observation_demo/
bi_consumer_observation_demo.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10
11//! BiConsumer observation demonstration
12//!
13//! This example demonstrates stateless BiConsumer types that observe two input
14//! values without mutating them.
15
16use qubit_function::{
17    ArcBiConsumer,
18    BiConsumer,
19    BoxBiConsumer,
20    RcBiConsumer,
21};
22use std::rc::Rc;
23use std::sync::{
24    Arc,
25    atomic::AtomicUsize,
26    atomic::Ordering,
27};
28use std::thread;
29
30fn main() {
31    println!("=== BiConsumer Observation Demo ===\n");
32
33    // 1. BoxBiConsumer - Single ownership
34    println!("1. BoxBiConsumer - Single ownership:");
35    let box_consumer = BoxBiConsumer::new(|x: &i32, y: &i32| {
36        println!("  Values: x={}, y={}, sum={}", x, y, x + y);
37    });
38    box_consumer.accept(&10, &5);
39    println!();
40
41    // 2. Method chaining with BoxBiConsumer
42    println!("2. BoxBiConsumer with method chaining:");
43    let chained = BoxBiConsumer::new(|x: &i32, y: &i32| {
44        println!("  First operation: x={}, y={}", x, y);
45    })
46    .and_then(|x: &i32, y: &i32| {
47        println!("  Second operation: sum={}", x + y);
48    })
49    .and_then(|x: &i32, y: &i32| {
50        println!("  Third operation: product={}", x * y);
51    });
52    chained.accept(&5, &3);
53    println!();
54
55    // 3. ArcBiConsumer - Thread-safe shared ownership
56    println!("3. ArcBiConsumer - Thread-safe shared ownership:");
57    let counter = Arc::new(AtomicUsize::new(0));
58    let c = counter.clone();
59    let arc_consumer = ArcBiConsumer::new(move |x: &i32, y: &i32| {
60        c.fetch_add(1, Ordering::SeqCst);
61        println!("  Thread {:?}: sum={}", thread::current().id(), x + y);
62    });
63
64    let consumer1 = arc_consumer.clone();
65    let consumer2 = arc_consumer.clone();
66
67    let handle1 = thread::spawn(move || {
68        consumer1.accept(&10, &5);
69    });
70
71    let handle2 = thread::spawn(move || {
72        consumer2.accept(&20, &8);
73    });
74
75    handle1.join().unwrap();
76    handle2.join().unwrap();
77    println!("  Total calls: {}\n", counter.load(Ordering::SeqCst));
78
79    // 4. RcBiConsumer - Single-threaded shared ownership
80    println!("4. RcBiConsumer - Single-threaded shared ownership:");
81    let counter = Rc::new(std::cell::Cell::new(0));
82    let c = counter.clone();
83    let rc_consumer = RcBiConsumer::new(move |x: &i32, y: &i32| {
84        c.set(c.get() + 1);
85        println!("  Call {}: sum={}", c.get(), x + y);
86    });
87
88    let clone1 = rc_consumer.clone();
89    let clone2 = rc_consumer.clone();
90
91    clone1.accept(&5, &3);
92    clone2.accept(&7, &2);
93    println!("  Total calls: {}\n", counter.get());
94
95    // 5. Working with closures directly
96    println!("5. Working with closures directly:");
97    let closure = |x: &i32, y: &i32| {
98        println!("  x={}, y={}, product={}", x, y, x * y);
99    };
100    closure.accept(&10, &20);
101    println!();
102
103    // 6. Pure observation - logging
104    println!("6. Pure observation - logging:");
105    let logger = BoxBiConsumer::new(|x: &i32, y: &i32| {
106        println!("  [LOG] Processing pair: ({}, {})", x, y);
107    });
108    logger.accept(&5, &3);
109    logger.accept(&10, &7);
110    println!();
111
112    // 7. Chaining observations
113    println!("7. Chaining observations:");
114    let log_input = BoxBiConsumer::new(|x: &i32, y: &i32| {
115        println!("  [INPUT] x={}, y={}", x, y);
116    });
117    let log_sum = BoxBiConsumer::new(|x: &i32, y: &i32| {
118        println!("  [SUM] {}", x + y);
119    });
120    let log_product = BoxBiConsumer::new(|x: &i32, y: &i32| {
121        println!("  [PRODUCT] {}", x * y);
122    });
123
124    let chained = log_input.and_then(log_sum).and_then(log_product);
125    chained.accept(&5, &3);
126    println!();
127
128    // 8. ArcBiConsumer - Reusability
129    println!("8. ArcBiConsumer - Reusability:");
130    let first = ArcBiConsumer::new(|x: &i32, y: &i32| {
131        println!("  First: x={}, y={}", x, y);
132    });
133    let second = ArcBiConsumer::new(|x: &i32, y: &i32| {
134        println!("  Second: sum={}", x + y);
135    });
136
137    // Both first and second can be reused after chaining
138    let chained1 = first.and_then(second.clone());
139    let chained2 = first.and_then(second.clone());
140
141    println!("  Using chained1:");
142    chained1.accept(&5, &3);
143
144    println!("  Using chained2:");
145    chained2.accept(&10, &2);
146    println!();
147
148    // 9. Name support
149    println!("9. Name support:");
150    let mut named_consumer = BoxBiConsumer::<i32, i32>::noop();
151    println!("  Initial name: {:?}", named_consumer.name());
152
153    named_consumer.set_name("sum_logger");
154    println!("  After setting name: {:?}", named_consumer.name());
155    println!("  Display: {}\n", named_consumer);
156
157    // 10. No-op consumer
158    println!("10. No-op consumer:");
159    let noop = BoxBiConsumer::<i32, i32>::noop();
160    noop.accept(&42, &10);
161    println!("  No-op completed (no output expected)\n");
162
163    println!("=== Demo Complete ===");
164}