1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
//! ## Graph Execution
//! Wingfoil abstracts away the details of how to co-ordinate the calculation of your
//! application, parts of which may be executing at different frequencies.
//! Only the nodes that actually require cycling are executed which allows wingfoil to
//! efficiently scale to very large graphs.
//! Consider the following example:
//! ```rust
//! use wingfoil::*;
//! use std::time::Duration;
//!
//! fn main() {
//! let period = Duration::from_millis(10);
//! let source = ticker(period).count(); // 1, 2, 3 etc
//! let is_even = source.map(|i| i % 2 == 0);
//! let odds = source
//! .filter(is_even.not())
//! .map(|i| format!("{:} is odd", i));
//! let evens = source
//! .filter(is_even)
//! .map(|i| format!("{:} is even", i));
//! merge(vec![odds, evens])
//! .print()
//! .run(
//! RunMode::HistoricalFrom(NanoTime::ZERO),
//! RunFor::Duration(period * 5),
//! );
//! }
//! ```
//! This output is produced:
//! ```pre
//! 1 is odd
//! 2 is even
//! 3 is odd
//! 4 is even
//! 5 is odd
//! 6 is even
//! ````
//! We can visualise the graph like this:
//! <div align="center">
//! <img alt="diagram" src="https://raw.githubusercontent.com/wingfoil-io/wingfoil/refs/heads/main/wingfoil/diagrams/odds_evens.png" width="400"/>
//! </div>
//! The input and output nodes tick 6 times each and the evens and odds nodes tick 3 times.
//!
//! ## Historical vs RealTime
//! Time is a first-class citizen in wingfoil. Engine time is measured in nanoseconds from the
//! [UNIX epoch](https://en.wikipedia.org/wiki/Unix_time) and represented by a [NanoTime].
//!
//! In this example we compare and contrast RealTime vs Historical RunMode. RealTime is used for
//! production deployment. Historical is used for development, unit-testing, integration-testing
//! and back-testing.
//!
//! ```rust
//! use wingfoil::*;
//! use std::time::Duration;
//! use log::Level::Info;
//!
//! pub fn main() {
//! env_logger::init();
//! for run_mode in vec![
//! RunMode::RealTime,
//! RunMode::HistoricalFrom(NanoTime::ZERO)
//! ] {
//! println!("\nUsing RunMode::{:?}", run_mode);
//! ticker(Duration::from_secs(1))
//! .count()
//! .logged("tick", Info)
//! .run(run_mode, RunFor::Cycles(3));
//! }
//! }
//! ```
//! This output is produced:
//! <pre>
//! Using RunMode::RealTime
//! [17:34:4<span style=color:red>6</span>Z INFO wingfoil] <span style=color:red>0</span>.000_001 tick 1
//! [17:34:4<span style=color:red>7</span>Z INFO wingfoil] <span style=color:red>1</span>.000_131 tick 2
//! [17:34:4<span style=color:red>8</span>Z INFO wingfoil] <span style=color:red>2</span>.000_381 tick 3
//!
//! Using RunMode::HistoricalFrom(NanoTime(0))
//! [17:34:4<span style=color:red>8</span>Z INFO wingfoil] <span style=color:red>0</span>.000_000 tick 1
//! [17:34:4<span style=color:red>8</span>Z INFO wingfoil] <span style=color:red>1</span>.000_000 tick 2
//! [17:34:4<span style=color:red>8</span>Z INFO wingfoil] <span style=color:red>2</span>.000_000 tick 3
//! </pre>
//! In Realtime mode the log statements are written every second. In Historical mode the log statements are written
//! immediately. In both cases engine time advances by 1 second between each tick.
//!
//! See the [order book example](https://github.com/wingfoil-io/wingfoil/blob/main/wingfoil/examples/order_book/) for more details.
//! See the [async example](https://github.com/wingfoil-io/wingfoil/blob/main/wingfoil/examples/async/) for more details.
//! See the [breadth first example](https://github.com/wingfoil-io/wingfoil/blob/main/wingfoil/examples/breadth_first/) for more details.
//! See the [dynamic examples](https://github.com/wingfoil-io/wingfoil/blob/main/wingfoil/examples/dynamic/) for more details.
//!
//! ## Multithreading
//!
//! Wingfoil supports multi-threading to distribute workloads across cores. The approach
//! is to compose sub-graphs, each running in its own dedicated thread.
//!
//! ## Messaging
//!
//! Wingfoil is agnostic to messaging and we plan to add support for shared memory IPC adapters such as Aeron
//! for ultra-low latency use cases, ZeroMq for low latency server to server communication and Kafka for high latency,
//! fault tolerant messaging.
//!
//! ## Graph Dynamism
//!
//! Some use cases require graph-dynamism - consider for example Request for Quote (RFQ) markets, where
//! the graph needs to be able to adapt to an incoming RFQ. Conceptually this could be done by changing the
//! shape of the graph at run time. However, we take a simpler approach by virtualising this, following a
//! demux pattern.
//!
//! ## Performance
//!
//! We take performance seriously, and ongoing work is focused on making **Wingfoil** even closer to a **zero-cost abstraction**,
//! Currently, the overhead of cycling a node in the graph is less than **10 nanoseconds**.
//!
//! For best performance we recommend using **cheaply cloneable** types:
//!
//! - For small strings: [`arraystring`](https://crates.io/crates/arraystring)
//! - For small vectors: [`tinyvec`](https://crates.io/crates/tinyvec)
//! - For larger or heap-allocated types:
//! - Use [`Rc<T>`](https://doc.rust-lang.org/std/rc/struct.Rc.html) for single threaded contexts.
//! - Use [`Arc<T>`](https://doc.rust-lang.org/std/sync/struct.Arc.html) for multithreaded contexts.
//!
//! ## Observability
//!
//! Wingfoil supports both the [`log`](https://docs.rs/log) and [`tracing`](https://docs.rs/tracing)
//! ecosystems via cargo feature flags.
//!
//! ### Feature flags
//!
//! | Feature | Effect |
//! |---|---|
//! | *(none)* | [`logged()`](StreamExt::logged) and [`GraphState::log()`] emit via the `log` crate — wire up any `log`-compatible backend (e.g. `env_logger`). |
//! | `tracing` | Events are emitted via `tracing` instead. A `tracing` subscriber is required; the `log` bridge ensures events still reach `env_logger` if none is installed. |
//! | `instrument-run` | Adds a tracing span around [`Graph::run()`] (the full setup→run→teardown lifecycle). |
//! | `instrument-cycle` | Adds a tracing span around each engine cycle (one span per dirty-node batch). |
//! | `instrument-apply-nodes` | Adds a tracing span around each lifecycle phase (setup / start / stop / teardown), recording the phase name. |
//! | `instrument-initialise` | Adds a tracing span around graph initialisation. |
//! | `instrument-cycle-node` | Adds a tracing span per node execution, recording the node index and type name. High frequency — opt in deliberately. |
//! | `instrument-default` | Enables `instrument-run`, `instrument-cycle`, `instrument-apply-nodes`, and `instrument-initialise`. |
//! | `instrument-all` | Enables `instrument-default` plus `instrument-cycle-node`. |
//!
//! All `instrument-*` features imply `tracing`.
//!
//! ### Example
//!
//! ```rust,no_run
//! use log::Level::Info;
//! use std::time::Duration;
//! use wingfoil::*;
//!
//! // With the `tracing` feature and a subscriber installed:
//! // tracing_subscriber::fmt::init();
//!
//! ticker(Duration::from_secs(1))
//! .count()
//! .logged("tick", Info) // emits a tracing event per tick when feature = "tracing"
//! .run(RunMode::HistoricalFrom(NanoTime::ZERO), RunFor::Cycles(3))
//! .unwrap();
//! ```
//!
//! See the [tracing example](https://github.com/wingfoil-io/wingfoil/blob/main/wingfoil/examples/tracing/)
//! for a runnable demonstration.
extern crate log;
extern crate derive_new;
// Dispatch a `log::Level` runtime value to the matching `tracing` event macro.
//
// Two forms:
// - `tracing_log!(level, <tracing args>)` — generic passthrough
// - `tracing_log!(level; time, label, value)` — wingfoil stream event; accepts a
// `NanoTime` and only calls `.pretty()` inside the enabled arm.
//
// Only available with the `tracing` feature.
// Check whether a `log::Level` is enabled in the current `tracing` subscriber.
// Only available with the `tracing` feature.
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;