pub struct Pipeline<C, S> { /* private fields */ }Expand description
An end-to-end driver: a chain of Stages whose types line up, run against a
Session.
A Pipeline is how stages become a compiler. You start from one stage with
Pipeline::new and extend it with then, which appends a
stage whose Input matches the current
Output. That match is checked at compile time: a pipeline
that tries to feed tokens into a stage expecting an AST does not build. The
finished pipeline is driven with run, which threads an input
through every stage in order and returns the final artifact.
On the first stage that returns a DriverError, the pipeline stops — later
stages do not run — and the error names the stage that failed. Diagnostics a
stage emitted before failing remain in the session.
The whole pipeline is a single monomorphized type, so composing stages adds no runtime indirection over calling them directly.
§Examples
A two-stage calculator driver — tokenize, then sum:
use driver_lang::{DriverError, Pipeline, Session, Stage};
struct Lex;
impl Stage<()> for Lex {
type Input = &'static str;
type Output = Vec<i64>;
fn name(&self) -> &'static str { "lex" }
fn run(&mut self, input: &'static str, _s: &mut Session<()>)
-> Result<Vec<i64>, DriverError>
{
input.split_whitespace()
.map(|w| w.parse::<i64>().map_err(|_| DriverError::new("not an integer")))
.collect()
}
}
struct Sum;
impl Stage<()> for Sum {
type Input = Vec<i64>;
type Output = i64;
fn name(&self) -> &'static str { "sum" }
fn run(&mut self, input: Vec<i64>, _s: &mut Session<()>)
-> Result<i64, DriverError>
{
Ok(input.iter().sum())
}
}
let mut driver = Pipeline::new(Lex).then(Sum);
let mut session = Session::new(());
assert_eq!(driver.run("1 2 3 4", &mut session).unwrap(), 10);The configuration type C is a phantom parameter: a pipeline threads a
&mut Session<C> through its stages but stores no C of its own. It is
inferred from the stages, so you write Pipeline::new(stage) and never name
C explicitly.
Implementations§
Source§impl<C, S> Pipeline<C, S>
impl<C, S> Pipeline<C, S>
Sourcepub fn new(stage: S) -> Self
pub fn new(stage: S) -> Self
Start a pipeline from a single stage.
§Examples
use driver_lang::{DriverError, Pipeline, Session, Stage};
struct Identity;
impl Stage<()> for Identity {
type Input = i64;
type Output = i64;
fn name(&self) -> &'static str { "identity" }
fn run(&mut self, input: i64, _s: &mut Session<()>) -> Result<i64, DriverError> {
Ok(input)
}
}
let driver = Pipeline::new(Identity);
assert_eq!(driver.name(), "identity");Source§impl<C, S: Stage<C>> Pipeline<C, S>
impl<C, S: Stage<C>> Pipeline<C, S>
Sourcepub fn then<N>(self, next: N) -> Pipeline<C, Then<S, N>>
pub fn then<N>(self, next: N) -> Pipeline<C, Then<S, N>>
Append a stage whose Input is this pipeline’s current
Output.
The type bound N: Stage<C, Input = S::Output> is the compile-time check
that the phases connect: the appended stage must accept exactly what the
pipeline currently produces. The result is a longer pipeline that produces
N’s output.
§Examples
use driver_lang::{DriverError, Pipeline, Session, Stage};
struct Parse;
impl Stage<()> for Parse {
type Input = &'static str;
type Output = usize;
fn name(&self) -> &'static str { "parse" }
fn run(&mut self, input: &'static str, _s: &mut Session<()>)
-> Result<usize, DriverError>
{ Ok(input.len()) }
}
struct Halve;
impl Stage<()> for Halve {
type Input = usize;
type Output = usize;
fn name(&self) -> &'static str { "halve" }
fn run(&mut self, input: usize, _s: &mut Session<()>)
-> Result<usize, DriverError>
{ Ok(input / 2) }
}
let mut driver = Pipeline::new(Parse).then(Halve);
let mut session = Session::new(());
assert_eq!(driver.run("abcdef", &mut session).unwrap(), 3);Sourcepub fn run(
&mut self,
input: S::Input,
session: &mut Session<C>,
) -> Result<S::Output, DriverError>
pub fn run( &mut self, input: S::Input, session: &mut Session<C>, ) -> Result<S::Output, DriverError>
Run the pipeline: thread input through every stage in order and return the
final output.
Stages run left to right, each receiving the previous stage’s output and the
shared Session. The run stops at the first stage that returns a
DriverError; that error names the failing stage.
§Errors
Returns the DriverError of the first stage that fails, with the stage’s
name stamped in.
§Examples
use driver_lang::{DriverError, Pipeline, Session, Stage};
struct Fail;
impl Stage<()> for Fail {
type Input = ();
type Output = ();
fn name(&self) -> &'static str { "fail" }
fn run(&mut self, _input: (), _s: &mut Session<()>) -> Result<(), DriverError> {
Err(DriverError::new("nope"))
}
}
let mut driver = Pipeline::new(Fail);
let mut session = Session::new(());
let err = driver.run((), &mut session).unwrap_err();
assert_eq!(err.stage(), "fail");
assert_eq!(err.message(), "nope");