mod_rewrite/
lib.rs

1//! Framework agnostic reimplementation of HTTPD's [mod_rewrite](https://httpd.apache.org/docs/current/mod/mod_rewrite.html).
2//!
3//! # Example
4//!
5//! ```
6//! use mod_rewrite::Engine;
7//!
8//! let mut engine = Engine::default();
9//! engine.add_rules(r#"
10//!   RewriteRule /file/(.*)     /tmp/$1      [L]
11//!   RewriteRule /redirect/(.*) /location/$1 [R=302]
12//!   RewriteRule /blocked/(.*)  -            [F]
13//! "#).expect("failed to process rules");
14//!
15//! let uri = "http://localhost/file/my/document.txt".to_owned();
16//! let result = engine.rewrite(uri).unwrap();
17//! println!("{result:?}");
18//! ```
19use std::str::FromStr;
20
21mod conditions;
22pub mod error;
23mod expr;
24mod extra;
25mod rule;
26
27use conditions::EngineCtx;
28use error::{EngineError, ExpressionError};
29use expr::ExpressionList;
30
31pub use conditions::{Condition, context};
32pub use expr::{ExprGroup, Expression, Rewrite};
33pub use extra::State;
34pub use rule::Rule;
35
36/// Expression Engine for Proccessing Rewrite Rules
37///
38/// Supports a subset of [official](https://httpd.apache.org/docs/current/mod/mod_rewrite.html)
39/// `mod_rewrite` expressions.
40///
41/// # Example
42///
43/// ```
44/// use mod_rewrite::Engine;
45///
46/// let mut engine = Engine::default();
47/// engine.add_rules(r#"
48///     RewriteRule /file/(.*)     /tmp/$1      [L]
49///     RewriteRule /redirect/(.*) /location/$1 [R=302]
50///     RewriteRule /blocked/(.*)  -            [F]
51/// "#).expect("failed to process rules");
52///
53/// let uri = "http://localhost/file/my/document.txt".to_owned();
54/// let result = engine.rewrite(uri).unwrap();
55/// println!("{result:?}");
56/// ```
57#[derive(Debug, Default)]
58pub struct Engine {
59    groups: Vec<ExprGroup>,
60}
61
62impl Engine {
63    /// Configure max number of loops over entire ruleset during
64    /// rewrite before error
65    ///
66    /// Default is 10
67    pub fn max_iterations(mut self, iterations: usize) -> Self {
68        self.groups = self
69            .groups
70            .into_iter()
71            .map(|g| g.max_iterations(iterations))
72            .collect();
73        self
74    }
75
76    /// Parse additonal [`Expression`]s to append as [`ExprGroup`]s to the
77    /// existing engine.
78    #[inline]
79    pub fn add_rules(&mut self, rules: &str) -> Result<(), ExpressionError> {
80        let groups = ExpressionList::from_str(rules)?.groups();
81        self.groups.extend(groups);
82        Ok(())
83    }
84
85    /// Evaluate the given URI against the configured [`ExprGroup`] instances
86    /// defined and generate a [`Rewrite`] response.
87    ///
88    /// This method skips using [`EngineCtx`] which is used to suppliment
89    /// [`Condition`] expressions. If you are NOT making use of `RewriteCond`
90    /// rules, this method may be simpler to use.
91    ///
92    /// See [`Engine::rewrite_ctx`] for more details.
93    #[inline]
94    pub fn rewrite(&self, uri: String) -> Result<Rewrite, EngineError> {
95        let mut ctx = EngineCtx::default();
96        self.rewrite_ctx(uri, &mut ctx)
97    }
98
99    /// Evaluate the given URI against the configured [`ExprGroup`] instances
100    /// defined and generate a [`Rewrite`] response.
101    ///
102    /// This method uses an additional [`EngineCtx`] which is used to suppliment
103    /// variables expanded in [`Condition`] expressions.
104    ///
105    /// If your engine is using `RewriteCond` rules, you will want to use this
106    /// method with a complete `EngineCtx`. See [`Engine::rewrite`] for a simpler
107    /// alternative.
108    pub fn rewrite_ctx(
109        &self,
110        mut uri: String,
111        ctx: &mut EngineCtx,
112    ) -> Result<Rewrite, EngineError> {
113        for group in self.groups.iter().filter(|g| g.match_conditions(ctx)) {
114            uri = match group.rewrite(uri)? {
115                Rewrite::Uri(uri) => uri,
116                status => return Ok(status),
117            };
118        }
119        Ok(Rewrite::Uri(uri))
120    }
121}
122
123impl FromStr for Engine {
124    type Err = ExpressionError;
125
126    fn from_str(s: &str) -> Result<Self, Self::Err> {
127        let groups = ExpressionList::from_str(s)?.groups();
128        Ok(Self { groups })
129    }
130}