mod display;
use crate::lexer::Span;
use crate::{Lexer, ParseError, Parser};
use std::fmt::Write;
use std::net::IpAddr;
use std::str::FromStr;
#[derive(Debug, Clone, PartialEq)]
pub struct Config {
pub statements: Vec<Statement>,
}
impl Config {
pub fn find_directives(&self, name: &str) -> Vec<&Directive> {
self.statements
.iter()
.filter_map(|stmt| {
if let Statement::Directive(directive) = stmt {
if directive.name == name {
Some(directive)
} else {
None
}
} else {
None
}
})
.collect()
}
pub fn find_host_blocks(&self) -> Vec<&HostBlock> {
self.statements
.iter()
.filter_map(|stmt| {
if let Statement::HostBlock(host_block) = stmt {
Some(host_block)
} else {
None
}
})
.collect()
}
pub fn find_match_blocks(&self) -> Vec<&MatchBlock> {
self.statements
.iter()
.filter_map(|stmt| {
if let Statement::MatchBlock(match_block) = stmt {
Some(match_block)
} else {
None
}
})
.collect()
}
}
impl FromStr for Config {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let lexer = Lexer::new(s);
let mut parser = Parser::new(lexer.collect());
parser.parse_config()
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Statement {
Directive(Directive),
HostBlock(HostBlock),
MatchBlock(MatchBlock),
GlobalBlock(Block),
SnippetBlock(SnippetBlock),
}
impl Statement {
pub fn span(&self) -> Span {
match self {
Statement::Directive(d) => d.span,
Statement::HostBlock(h) => h.span,
Statement::MatchBlock(m) => m.span,
Statement::GlobalBlock(g) => g.span,
Statement::SnippetBlock(s) => s.span,
}
}
pub fn is_directive(&self) -> bool {
matches!(self, Statement::Directive(_))
}
pub fn is_host_block(&self) -> bool {
matches!(self, Statement::HostBlock(_))
}
pub fn is_match_block(&self) -> bool {
matches!(self, Statement::MatchBlock(_))
}
pub fn is_global_block(&self) -> bool {
matches!(self, Statement::GlobalBlock(_))
}
pub fn is_snippet_block(&self) -> bool {
matches!(self, Statement::SnippetBlock(_))
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Directive {
pub name: String,
pub args: Vec<Value>,
pub block: Option<Block>,
pub span: Span,
}
impl Directive {
pub fn get_string_arg(&self, index: usize) -> Option<&str> {
self.args.get(index).and_then(|arg| {
if let Value::String(s, _) = arg {
Some(s.as_str())
} else {
None
}
})
}
pub fn get_integer_arg(&self, index: usize) -> Option<i64> {
self.args.get(index).and_then(|arg| {
if let Value::Integer(i, _) = arg {
Some(*i)
} else {
None
}
})
}
pub fn get_boolean_arg(&self, index: usize) -> Option<bool> {
self.args.get(index).and_then(|arg| {
if let Value::Boolean(b, _) = arg {
Some(*b)
} else {
None
}
})
}
pub fn has_block(&self) -> bool {
self.block.is_some()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct MatchBlock {
pub matcher: String,
pub expr: Vec<MatcherExpression>,
pub span: Span,
}
impl MatchBlock {
pub fn has_expressions(&self) -> bool {
!self.expr.is_empty()
}
pub fn get_expressions(&self) -> &[MatcherExpression] {
&self.expr
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct HostBlock {
pub hosts: Vec<HostPattern>,
pub block: Block,
pub span: Span,
}
impl HostBlock {
pub fn get_host_patterns(&self) -> Vec<String> {
self.hosts.iter().map(|hp| hp.as_full_str()).collect()
}
pub fn matches_host(&self, host: &str) -> bool {
self.hosts.iter().any(|hp| hp.as_str() == host)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SnippetBlock {
pub name: String,
pub block: Block,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Block {
pub statements: Vec<Statement>,
pub span: Span,
}
impl Block {
pub fn span(&self) -> Span {
self.span
}
pub fn find_directives(&self, name: &str) -> Vec<&Directive> {
self.statements
.iter()
.filter_map(|stmt| {
if let Statement::Directive(directive) = stmt {
if directive.name == name {
Some(directive)
} else {
None
}
} else {
None
}
})
.collect()
}
pub fn find_directive(&self, name: &str) -> Option<&Directive> {
self.statements.iter().find_map(|stmt| {
if let Statement::Directive(directive) = stmt {
if directive.name == name {
Some(directive)
} else {
None
}
} else {
None
}
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
String(String, Span),
Integer(i64, Span),
Float(f64, Span),
Boolean(bool, Span),
InterpolatedString(Vec<StringPart>, Span),
}
impl Value {
pub fn span(&self) -> Span {
match self {
Value::String(_, span) => *span,
Value::Integer(_, span) => *span,
Value::Float(_, span) => *span,
Value::Boolean(_, span) => *span,
Value::InterpolatedString(_, span) => *span,
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
Value::String(s, _) => Some(s),
_ => None,
}
}
pub fn as_interpolated_string(&self) -> Option<&[StringPart]> {
match self {
Value::InterpolatedString(s, _) => Some(s),
_ => None,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
Value::Integer(i, _) => Some(*i),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
Value::Float(f, _) => Some(*f),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
Value::Boolean(b, _) => Some(*b),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum StringPart {
Literal(String),
Expression(Vec<String>),
}
impl StringPart {
pub fn as_str(&self) -> String {
match self {
StringPart::Literal(s) => s.clone(),
StringPart::Expression(v) => {
let mut expr = String::new();
expr.push_str("{{");
expr.push_str(&v.join("."));
expr.push_str("}}");
expr
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum HostLabels {
Hostname(Vec<String>),
IpAddr(IpAddr),
Wildcard,
}
impl HostLabels {
pub fn as_str(&self) -> String {
match self {
HostLabels::Hostname(labels) => labels.join("."),
HostLabels::IpAddr(ip) => ip.to_string(),
HostLabels::Wildcard => "*".to_string(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct HostPattern {
pub labels: HostLabels,
pub port: Option<u16>,
pub protocol: Option<String>,
pub span: Span,
}
impl HostPattern {
pub fn as_str(&self) -> String {
let mut pattern = self.labels.as_str();
if let Some(port) = self.port {
pattern.push(':');
pattern.push_str(&port.to_string());
}
pattern
}
pub fn as_full_str(&self) -> String {
let mut pattern = String::new();
if let Some(protocol) = &self.protocol {
pattern.push_str(protocol);
pattern.push(' ');
}
pattern.push_str(&self.as_str());
pattern
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct MatcherExpression {
pub left: Operand,
pub op: Operator,
pub right: Operand,
pub span: Span,
}
impl MatcherExpression {
pub fn span(&self) -> Span {
self.span
}
pub fn is_equality(&self) -> bool {
matches!(self.op, Operator::Eq)
}
pub fn is_inequality(&self) -> bool {
matches!(self.op, Operator::NotEq)
}
pub fn is_regex(&self) -> bool {
matches!(self.op, Operator::Regex | Operator::NotRegex)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Operand {
Identifier(Vec<String>, Span),
String(String, Span),
Integer(i64, Span),
Float(f64, Span),
}
impl Operand {
pub fn span(&self) -> Span {
match self {
Operand::Identifier(_, span) => *span,
Operand::String(_, span) => *span,
Operand::Integer(_, span) => *span,
Operand::Float(_, span) => *span,
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
Operand::String(s, _) => Some(s),
_ => None,
}
}
pub fn as_identifier(&self) -> Option<&[String]> {
match self {
Operand::Identifier(parts, _) => Some(parts),
_ => None,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
Operand::Integer(i, _) => Some(*i),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
Operand::Float(f, _) => Some(*f),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Operator {
Eq,
NotEq,
Regex,
NotRegex,
In,
}
impl Operator {
pub fn as_str(&self) -> &'static str {
match self {
Operator::Eq => "==",
Operator::NotEq => "!=",
Operator::Regex => "~",
Operator::NotRegex => "!~",
Operator::In => "in",
}
}
pub fn is_comparison(&self) -> bool {
matches!(self, Operator::Eq | Operator::NotEq)
}
pub fn is_regex(&self) -> bool {
matches!(self, Operator::Regex | Operator::NotRegex)
}
}