Compare commits

...

2 Commits

Author SHA1 Message Date
R Tyler Croy 0b4821137d Implement the parser support for the parallel block
This was pretty simple, yay.

pipeline {
    parallel {
        stage {
            name = 'Foo'
            steps {
                sh 'pwd'
            }
        }
        stage {
            name = 'Foo'
            steps {
                sh 'pwd'
            }
        }
    }
}
2020-11-28 22:04:50 -08:00
R Tyler Croy 127d3b7aa4 Refactor the parser code into its own crate for future development
This will inform #46
2020-11-28 21:42:48 -08:00
19 changed files with 95 additions and 16 deletions

13
Cargo.lock generated
View File

@ -1652,11 +1652,22 @@ dependencies = [
name = "otto-parser"
version = "0.1.0"
dependencies = [
"async-std",
"log",
"otto-models",
"pest",
"pest_derive",
"serde_json",
"uuid",
]
[[package]]
name = "otto-parser-service"
version = "0.1.0"
dependencies = [
"async-std",
"log",
"otto-models",
"otto-parser",
"pretty_env_logger 0.4.0",
"serde_json",
"tide 0.15.0",

View File

@ -6,6 +6,7 @@ members = [
"crates/agent",
"crates/models",
"crates/parser",
"services/auctioneer",
"services/eventbus",

1
crates/parser/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
target/

13
crates/parser/Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "otto-parser"
version = "0.1.0"
authors = ["R. Tyler Croy <rtyler@brokenco.de>"]
edition = "2018"
[dependencies]
log = "~0.4.11"
otto-models = { path = "../models" }
pest = "~2.1.3"
pest_derive = "~2.1.0"
serde_json = "~1.0.59"
uuid = { version = "~0.8.1", features = ["v4", "serde"]}

View File

@ -0,0 +1,6 @@
= Otto Parser
The Otto Parser service is basically a parser engine that speaks HTTP. In the
`src/` directory you will find the `.pest` grammar definition which outlines
the Otto Pipeline syntax.

View File

@ -9,6 +9,9 @@ use pest::iterators::{Pair, Pairs};
use pest::Parser;
use uuid::Uuid;
pub use pest::error::ErrorVariant;
pub use pest::error::LineColLocation;
#[derive(Parser)]
#[grammar = "pipeline.pest"]
struct PipelineParser;
@ -44,6 +47,12 @@ pub fn parse_pipeline_string(buffer: &str) -> Result<Pipeline, PestError<Rule>>
contexts: vec![ctx],
});
}
Rule::parallel => {
pipeline.batches.push(Batch {
mode: BatchMode::Parallel,
contexts: parse_parallel(&mut parsed.into_inner()),
});
}
_ => {}
}
}
@ -55,7 +64,21 @@ pub fn parse_pipeline_string(buffer: &str) -> Result<Pipeline, PestError<Rule>>
Ok(pipeline)
}
fn parse_str(parser: &mut pest::iterators::Pair<Rule>) -> String {
fn parse_parallel(parser: &mut Pairs<Rule>) -> Vec<Context> {
let mut contexts = vec![];
while let Some(parsed) = parser.next() {
match parsed.as_rule() {
Rule::stage => {
let ctx = parse_stage(&mut parsed.into_inner());
contexts.push(ctx);
}
_ => {}
}
}
contexts
}
fn parse_str(parser: &mut Pair<Rule>) -> String {
// TODO: There's got to be a better way than cloning
let mut parser = parser.clone().into_inner();
while let Some(parsed) = parser.next() {
@ -391,4 +414,27 @@ mod tests {
}
}
}
#[test]
fn parse_parallel() {
let buf = r#"
pipeline {
parallel {
stage {
name = 'Test'
steps { sh 'ls' }
}
stage {
name = 'UAT'
steps { sh 'pwd' }
}
}
}"#;
let pipeline = parse_pipeline_string(&buf).expect("Failed to parse");
assert!(!pipeline.uuid.is_nil());
assert_eq!(pipeline.batches.len(), 1);
let batch = &pipeline.batches[0];
assert_eq!(batch.contexts.len(), 2);
}
}

View File

@ -1,18 +1,27 @@
// The pipeline PEG
// The pipeline PEG
pipeline = _{ SOI ~ "pipeline" ~
BLOCK_BEGIN ~
execBlocks ~
BLOCK_END ~ EOI }
execBlocks = { (stage | steps)* }
execBlocks = { (stage
| steps
| parallel)* }
stage = { "stage" ~
BLOCK_BEGIN ~
BLOCK_BEGIN ~
(property*) ~
steps ~
BLOCK_END }
// The parallel block can contain multiple stages which run in parallel
//
// inside the parser this should result in multiple contexts in the same batch
parallel = { "parallel" ~
BLOCK_BEGIN ~
(stage)* ~
BLOCK_END }
steps = { "steps" ~ BLOCK_BEGIN ~ step+ ~ BLOCK_END }
step = { IDENT ~ (

View File

@ -1,13 +1,9 @@
[package]
name = "otto-parser"
name = "otto-parser-service"
version = "0.1.0"
authors = ["R. Tyler Croy <rtyler@brokenco.de>"]
edition = "2018"
[lib]
name = "otto_parser"
path = "src/lib.rs"
[[bin]]
name = "otto-parser"
path = "src/main.rs"
@ -16,8 +12,7 @@ path = "src/main.rs"
async-std = { version = "1.6.5", features = ["attributes"]}
log = "~0.4.11"
otto-models = { path = "../../crates/models" }
pest = "~2.1.3"
pest_derive = "~2.1.0"
otto-parser = { path = "../../crates/parser" }
pretty_env_logger = "~0.4.0"
serde_json = "~1.0.59"
tide = "~0.15.0"

View File

@ -1,8 +1,6 @@
= Parser service
The Otto Parser service is basically a parser engine that speaks HTTP. In the
`src/` directory you will find the `.pest` grammar definition which outlines
the Otto Pipeline syntax.
The Otto Parser Service presents the parser crate as an HTTP service.
The `apispec.yml` is an link:https://en.wikipedia.org/wiki/Open_API[OpenAPI]
specification that describes the public HTTP endpoints that the parser service

View File

@ -16,7 +16,6 @@ async fn parse(mut req: Request<()>) -> tide::Result {
match parsed {
Err(e) => {
use pest::error::*;
error!("Failed to parse: {:?}", e);
let variant = match e.variant {