Simplest Example
In this example we will cover:
- Modifying the DFIR template project
- How DFIR program specs are embedded inside Rust
- How to execute a simple DFIR program
- Two DFIR operators:
source_iter
andfor_each
Lets start out with the simplest possible DFIR program, which prints out
the numbers in 0..10
.
Create a clean template project:
cargo generate gh:hydro-project/hydroflow template/dfir
β οΈ Favorite `gh:hydro-project/hydroflow` not found in config, using it as a git repository: https://github.com/hydro-project/hydroflow.git
π€· Project Name: simple
π§ Destination: /Users/me/code/simple ...
π§ project-name: simple ...
π§ Generating template ...
[11/11] Done: src
π§ Moving generated files into: `<dir>/simple`...
π‘ Initializing a fresh Git repository
β¨ Done! New project created <dir>/simple
After cd
ing into the generated folder, ensure the correct nightly version of rust is installed, and test the generated project:
rustup update
cargo test
Then edit the src/main.rs
file, replacing
all of its contents with the following code:
//[use]//
use dfir_rs::dfir_syntax;
//[/use]//
//[macro_call]//
pub fn main() {
let mut flow = dfir_syntax! {
source_iter(0..10) -> for_each(|n| println!("Hello {}", n));
};
//[/macro_call]//
//[run]//
flow.run_available();
//[/run]//
}
And then run the program:
cargo run
<build output>
Hello 0
Hello 1
Hello 2
Hello 3
Hello 4
Hello 5
Hello 6
Hello 7
Hello 8
Hello 9
Understanding the Codeβ
Although this is a trivial program, it's useful to go through it line by line.
use dfir_rs::dfir_syntax;
This import gives you the macro you need from DFIR to write code in DFIR's surface syntax.
Next, inside the main method we specify a flow by calling the
dfir_syntax!
macro. We assign the resulting Dfir
instance to
a mutable variable flow
ββmutable because we will be changing its status when we run it.
pub fn main() {
let mut flow = dfir_syntax! {
source_iter(0..10) -> for_each(|n| println!("Hello {}", n));
};
DFIR surface syntax defines a "flow" consisting of operators connected via ->
arrows.
This simplest example uses a simple two-step linear flow.
It starts with a source_iter
operator that takes the Rust
iterator 0..10
and iterates it to emit the
numbers 0 through 9. That operator then passes those numbers along the ->
arrow downstream to a
for_each
operator that invokes its closure argument to print each
item passed in.
The DFIR surface syntax is merely a specification; it does not actually do anything
until we run it.
We can run this flow from within Rust via the run_available()
method.
flow.run_available();
Note that run_available()
runs the DFIR graph until no more work is immediately
available. In this example flow, running the graph drains the iterator completely, so no
more work will ever be available. In future examples we will use external inputs such as
network ingress, in which case more work might appear at any time.
In server applications that use network ingress for inputs, new work can appear at any time. In these application
we may need a different method than run_available()
, e.g. the run_async()
method, check the networking
examples for more.
A Note on Project Structureβ
The template project is intended to be a starting point for your own DFIR project, and you can add files and directories as you see fit. The only requirement is that the src/main.rs
file exists and contains a main()
function.
In this simplest example we did not use a number of the files in the template: notably everything in the src/
subdirectory other than src/main.rs
. If you'd like to delete those extraneous files you can do so, but it's not necessary, and we'll use them in subsequent examples.