use std::str::FromStr;
mod conditions;
pub mod error;
mod expr;
mod extra;
mod rule;
use conditions::EngineCtx;
use error::{EngineError, ExpressionError};
use expr::ExpressionList;
pub use conditions::{Condition, context};
pub use expr::{ExprGroup, Expression, Rewrite};
pub use extra::State;
pub use rule::Rule;
#[derive(Debug, Default)]
pub struct Engine {
groups: Vec<ExprGroup>,
}
impl Engine {
pub fn max_iterations(mut self, iterations: usize) -> Self {
self.groups = self
.groups
.into_iter()
.map(|g| g.max_iterations(iterations))
.collect();
self
}
#[inline]
pub fn add_rules(&mut self, rules: &str) -> Result<&mut Self, ExpressionError> {
let groups = ExpressionList::from_str(rules)?.groups();
self.groups.extend(groups);
Ok(self)
}
#[inline]
pub fn rewrite(&self, uri: &str) -> Result<Rewrite, EngineError> {
let mut ctx = EngineCtx::default();
self.rewrite_ctx(uri, &mut ctx)
}
pub fn rewrite_ctx(&self, uri: &str, ctx: &mut EngineCtx) -> Result<Rewrite, EngineError> {
let (mut uri, query) = extra::split_query(uri);
for group in self.groups.iter().filter(|g| g.match_conditions(ctx)) {
uri = match group.rewrite(&uri)? {
Rewrite::Uri(uri) => uri,
status => return Ok(status.with_query(query)),
};
}
Ok(Rewrite::Uri(uri).with_query(query))
}
}
impl FromStr for Engine {
type Err = ExpressionError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let groups = ExpressionList::from_str(s)?.groups();
Ok(Self { groups })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_groups() {
let mut engine = Engine::default();
engine
.add_rules(
r#"
RewriteRule /static/(.*) /files/$1 [NE,L]
RewriteRule /(.*) /index?page=$1
"#,
)
.unwrap();
let r = engine.rewrite("/static/1/2").unwrap();
assert!(matches!(r, Rewrite::Uri(uri) if uri == "/index?page=files%2F1%2F2"));
let r = engine.rewrite("/1/2/3?a=b").unwrap();
println!("{r:?}");
assert!(matches!(r, Rewrite::Uri(uri) if uri == "/index?page=1%2F2%2F3&a=b"));
}
#[test]
fn test_query() {
let mut engine = Engine::default();
engine
.add_rules(
r#"
RewriteRule /static/(.*) /files/$1 [NE,END]
RewriteRule /(.*) /index?page=$1
"#,
)
.unwrap();
let r = engine.rewrite("/static/1/2?a=b").unwrap();
assert!(matches!(r, Rewrite::EndUri(uri) if uri == "/files/1/2?a=b"));
let r = engine.rewrite("/1/2/3?a=b").unwrap();
println!("{r:?}");
assert!(matches!(r, Rewrite::Uri(uri) if uri == "/index?page=1%2F2%2F3&a=b"));
}
}