214 lines
5.4 KiB
Rust
214 lines
5.4 KiB
Rust
#[cfg(test)]
|
|
#[allow(clippy::unwrap_used, reason = "these are tests. they may unwrap.")]
|
|
mod test;
|
|
|
|
use crate::{
|
|
error::{ErrorKind, Errors, SyntaxErrorKind},
|
|
syntax::CommandPart,
|
|
};
|
|
|
|
use super::{CommandPartKind, PipelineElement, PipelineElementKind};
|
|
|
|
pub fn check(syntax: &[PipelineElement]) -> Result<(), Vec<Errors>> {
|
|
let mut errs = Vec::new();
|
|
|
|
if let Err(e_span) = check_missing_streamer(syntax) {
|
|
errs.push(Errors::new_single(
|
|
ErrorKind::SyntaxError(SyntaxErrorKind::MissingStreamer),
|
|
e_span,
|
|
));
|
|
}
|
|
|
|
if let Err(err_locs) = check_missing_filters(syntax) {
|
|
errs.push(Errors::new(
|
|
ErrorKind::SyntaxError(SyntaxErrorKind::MissingFilter),
|
|
err_locs,
|
|
));
|
|
}
|
|
|
|
if let Err(e_span) = check_missing_sink(syntax) {
|
|
errs.push(Errors::new_single(
|
|
ErrorKind::SyntaxError(SyntaxErrorKind::MissingSink),
|
|
e_span,
|
|
));
|
|
}
|
|
|
|
if let Err(e_span) = check_literal_as_sink(syntax) {
|
|
errs.push(Errors::new_single(
|
|
ErrorKind::SyntaxError(SyntaxErrorKind::LiteralAsSink),
|
|
e_span,
|
|
));
|
|
}
|
|
|
|
if let Err(err_locs) = check_literal_as_filter(syntax) {
|
|
errs.push(Errors::new(
|
|
ErrorKind::SyntaxError(SyntaxErrorKind::LiteralAsFilter),
|
|
err_locs,
|
|
));
|
|
}
|
|
|
|
if let Err(e_span) = check_literal_with_args(syntax) {
|
|
errs.push(Errors::new_single(
|
|
ErrorKind::SyntaxError(SyntaxErrorKind::LiteralWithArgs),
|
|
e_span,
|
|
));
|
|
}
|
|
|
|
if errs.is_empty() {
|
|
Ok(())
|
|
} else {
|
|
Err(errs)
|
|
}
|
|
}
|
|
|
|
fn check_missing_streamer(syntax: &[PipelineElement]) -> Result<(), logos::Span> {
|
|
if let Some(&PipelineElement {
|
|
kind: PipelineElementKind::Pipe,
|
|
ref span,
|
|
}) = syntax.first()
|
|
{
|
|
Err(span.clone())
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn check_missing_filters(syntax: &[PipelineElement]) -> Result<(), Vec<logos::Span>> {
|
|
let mut missing_filter_locs = Vec::new();
|
|
|
|
for i in 0..syntax.len() {
|
|
if let (
|
|
Some(&PipelineElement {
|
|
kind: PipelineElementKind::Pipe,
|
|
ref span,
|
|
}),
|
|
Some(&PipelineElement {
|
|
kind: PipelineElementKind::Pipe,
|
|
span: ref span1,
|
|
}),
|
|
) = (syntax.get(i), syntax.get(i + 1))
|
|
{
|
|
missing_filter_locs.push(span.start..span1.end);
|
|
}
|
|
}
|
|
|
|
if missing_filter_locs.is_empty() {
|
|
Ok(())
|
|
} else {
|
|
Err(missing_filter_locs)
|
|
}
|
|
}
|
|
|
|
fn check_missing_sink(syntax: &[PipelineElement]) -> Result<(), logos::Span> {
|
|
if let Some(&PipelineElement {
|
|
kind: PipelineElementKind::Pipe,
|
|
ref span,
|
|
}) = syntax.last()
|
|
{
|
|
Err(span.clone())
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn check_literal_as_sink(syntax: &[PipelineElement]) -> Result<(), logos::Span> {
|
|
let last_block: Option<&[PipelineElement]> = syntax
|
|
.split(|PipelineElement { kind, .. }| kind == &PipelineElementKind::Pipe)
|
|
.last();
|
|
|
|
// there HAS to be a better way to do this... this is HORRIBLE
|
|
if let Some(last_block) = last_block {
|
|
if let Some(PipelineElement {
|
|
kind: PipelineElementKind::Command(parts),
|
|
span,
|
|
}) = last_block.first()
|
|
{
|
|
if let Some(first_part) = parts.first() {
|
|
if matches!(
|
|
first_part,
|
|
CommandPart {
|
|
kind: CommandPartKind::Word(_),
|
|
..
|
|
}
|
|
) {
|
|
Ok(())
|
|
} else {
|
|
Err(span.clone())
|
|
}
|
|
} else {
|
|
Ok(())
|
|
}
|
|
} else {
|
|
Ok(())
|
|
}
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn check_literal_as_filter(syntax: &[PipelineElement]) -> Result<(), Vec<logos::Span>> {
|
|
let errs = syntax
|
|
.iter()
|
|
.take(syntax.len() - 1)
|
|
.skip(1)
|
|
.filter(|element| {
|
|
!matches!(
|
|
element,
|
|
PipelineElement {
|
|
kind: PipelineElementKind::Pipe,
|
|
..
|
|
}
|
|
)
|
|
})
|
|
.map(|item| {
|
|
let PipelineElement {
|
|
kind: PipelineElementKind::Command(c),
|
|
span,
|
|
} = item
|
|
else {
|
|
return None;
|
|
};
|
|
|
|
let Some(CommandPart { kind, .. }) = c.first() else {
|
|
return None;
|
|
};
|
|
|
|
if matches!(kind, CommandPartKind::Word(_)) {
|
|
None
|
|
} else {
|
|
Some(span)
|
|
}
|
|
})
|
|
.filter_map(|err| err.map(Clone::clone))
|
|
.collect::<Vec<logos::Span>>();
|
|
|
|
if errs.is_empty() {
|
|
Ok(())
|
|
} else {
|
|
Err(errs)
|
|
}
|
|
}
|
|
|
|
fn check_literal_with_args(syntax: &[PipelineElement]) -> Result<(), logos::Span> {
|
|
if let Some(PipelineElement {
|
|
kind: PipelineElementKind::Command(c),
|
|
span,
|
|
}) = syntax.first()
|
|
{
|
|
if c.len() > 1
|
|
&& !matches!(
|
|
c.first(),
|
|
Some(CommandPart {
|
|
kind: CommandPartKind::Word(_),
|
|
..
|
|
})
|
|
)
|
|
{
|
|
Err(span.clone())
|
|
} else {
|
|
Ok(())
|
|
}
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|