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
//! ## 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 [rfq example](https://github.com/wingfoil-io/wingfoil/blob/main/wingfoil/examples/rfq/) 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.
extern crate log;
extern crate derive_new;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;