iowo/src/syntax/check.rs

193 lines
4.8 KiB
Rust
Raw Normal View History

#[cfg(test)]
mod test;
2023-11-15 21:13:04 +01:00
2023-11-18 21:11:43 +01:00
use crate::syntax::CommandPart;
use super::{error::SyntaxError, CommandPartKind, PipelineElement, PipelineElementKind};
pub fn check(syntax: &[PipelineElement]) -> Result<(), Vec<SyntaxError>> {
2023-11-15 21:13:04 +01:00
let mut errs = Vec::new();
if let Err(e_span) = check_missing_streamer(syntax) {
2023-11-18 18:34:03 +01:00
errs.push(SyntaxError::MissingStreamer(vec![e_span]));
2023-11-15 21:13:04 +01:00
}
if let Err(err_locs) = check_missing_filters(syntax) {
2023-11-18 21:11:43 +01:00
errs.push(SyntaxError::MissingFilter(err_locs));
}
if let Err(e_span) = check_missing_sink(syntax) {
2023-11-18 18:34:03 +01:00
errs.push(SyntaxError::MissingSink(vec![e_span]));
}
2023-11-18 21:11:43 +01:00
if let Err(e_span) = check_literal_as_sink(syntax) {
errs.push(SyntaxError::LiteralAsSink(vec![e_span]));
}
if let Err(err_locs) = check_literal_as_filter(syntax) {
errs.push(SyntaxError::LiteralAsFilter(err_locs));
}
if let Err(e_span) = check_literal_with_args(syntax) {
errs.push(SyntaxError::LiteralWithArgs(vec![e_span]));
}
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))
{
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()
.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
}
})
.skip(1)
.take(syntax.len() - 1)
.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(())
}
}