iowo/src/syntax/check.rs

215 lines
5.4 KiB
Rust
Raw Normal View History

#[cfg(test)]
2023-11-20 10:27:47 +01:00
#[allow(clippy::unwrap_used, reason = "these are tests. they may unwrap.")]
mod test;
2023-11-15 21:13:04 +01:00
use crate::{
error::{ErrorKind, Errors, SyntaxErrorKind},
syntax::CommandPart,
};
2023-11-18 21:11:43 +01:00
use super::{CommandPartKind, PipelineElement, PipelineElementKind};
pub fn check(syntax: &[PipelineElement]) -> Result<(), Vec<Errors>> {
2023-11-15 21:13:04 +01:00
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,
));
2023-11-15 21:13:04 +01:00
}
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,
));
}
2023-11-18 21:11:43 +01:00
if let Err(e_span) = check_literal_as_sink(syntax) {
errs.push(Errors::new_single(
ErrorKind::SyntaxError(SyntaxErrorKind::LiteralAsSink),
e_span,
));
2023-11-18 21:11:43 +01:00
}
if let Err(err_locs) = check_literal_as_filter(syntax) {
errs.push(Errors::new(
ErrorKind::SyntaxError(SyntaxErrorKind::LiteralAsFilter),
err_locs,
));
2023-11-18 21:11:43 +01:00
}
if let Err(e_span) = check_literal_with_args(syntax) {
errs.push(Errors::new_single(
ErrorKind::SyntaxError(SyntaxErrorKind::LiteralWithArgs),
e_span,
));
2023-11-18 21:11:43 +01:00
}
2023-11-15 21:13:04 +01:00
if errs.is_empty() {
Ok(())
2023-11-15 21:13:04 +01:00
} else {
Err(errs)
}
}
fn check_missing_streamer(syntax: &[PipelineElement]) -> Result<(), logos::Span> {
2023-11-15 21:13:04 +01:00
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))
{
2023-11-20 10:29:08 +01:00
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(())
}
}
2023-11-18 21:11:43 +01:00
fn check_literal_as_sink(syntax: &[PipelineElement]) -> Result<(), logos::Span> {
let last_block: Option<&[PipelineElement]> = syntax
.split(|PipelineElement { kind, span: _ }| 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(_),
span: _
}
) {
Err(span.clone())
} else {
Ok(())
}
} 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)
2023-11-18 21:11:43 +01:00
.filter(|element| {
!matches!(
element,
PipelineElement {
kind: PipelineElementKind::Pipe,
span: _
}
)
})
.map(|item| {
let PipelineElement {
kind: PipelineElementKind::Command(c),
span,
} = item
else {
return None;
};
let Some(CommandPart { kind, span: _ }) = c.first() else {
return None;
};
if !matches!(kind, CommandPartKind::Word(_)) {
Some(span)
} else {
None
}
})
.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(_),
span: _
})
)
{
Err(span.clone())
} else {
Ok(())
}
} else {
Ok(())
}
}