Skip to main content

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 and for_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 cding 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.