Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ce25f0a

Browse files
committedJun 5, 2025·
Expand rewriter docs
1 parent d7173d9 commit ce25f0a

File tree

12 files changed

+356
-218
lines changed

12 files changed

+356
-218
lines changed
 

‎crates/lang_handler/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ mod headers;
55
mod request;
66
mod response;
77
pub mod rewrite;
8+
mod test;
89

910
#[cfg(feature = "c")]
1011
pub use ffi::*;
1112
pub use handler::Handler;
1213
pub use headers::{Header, Headers};
1314
pub use request::{Request, RequestBuilder};
1415
pub use response::{Response, ResponseBuilder};
16+
pub use test::{MockRoot, MockRootBuilder};
1517
pub use url::Url;

‎crates/lang_handler/src/rewrite/condition/existence.rs

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ impl ExistenceCondition {
2626
}
2727

2828
impl Condition for ExistenceCondition {
29-
/// A NonExistenceCondition matches a request if the path segment of the
30-
/// request url does not exist in the provided base directory.
29+
/// An ExistenceCondition matches a request if the path segment of the
30+
/// request url exists in the provided base directory.
3131
///
3232
/// # Examples
3333
///
@@ -44,9 +44,10 @@ impl Condition for ExistenceCondition {
4444
/// assert_eq!(condition.matches(&request), false);
4545
/// ```
4646
fn matches(&self, request: &Request) -> bool {
47+
let path = request.url().path();
4748
self
4849
.0
49-
.join(request.url().path().strip_prefix("/").unwrap())
50+
.join(path.strip_prefix("/").unwrap_or(path))
5051
.canonicalize()
5152
.is_ok()
5253
}
@@ -93,9 +94,10 @@ impl Condition for NonExistenceCondition {
9394
/// assert!(condition.matches(&request));
9495
/// ```
9596
fn matches(&self, request: &Request) -> bool {
97+
let path = request.url().path();
9698
self
9799
.0
98-
.join(request.url().path().strip_prefix("/").unwrap())
100+
.join(path.strip_prefix("/").unwrap_or(path))
99101
.canonicalize()
100102
.is_err()
101103
}
@@ -104,36 +106,16 @@ impl Condition for NonExistenceCondition {
104106
#[cfg(test)]
105107
mod test {
106108
use super::*;
107-
108-
use std::{
109-
env::current_dir,
110-
fs::File,
111-
io::Write,
112-
path::{Path, PathBuf},
113-
};
114-
115-
struct TempFile(PathBuf);
116-
117-
impl TempFile {
118-
fn new<P: AsRef<Path>, S: Into<String>>(path: P, contents: S) -> Self {
119-
let mut file = File::create(path.as_ref()).unwrap();
120-
file.write_all(contents.into().as_bytes()).unwrap();
121-
Self(path.as_ref().to_owned())
122-
}
123-
}
124-
125-
impl Drop for TempFile {
126-
fn drop(&mut self) {
127-
std::fs::remove_file(&self.0).unwrap();
128-
}
129-
}
109+
use crate::MockRoot;
130110

131111
#[test]
132112
fn test_existence_condition() {
133-
let _temp = TempFile::new("exists.php", "<?php echo \"Hello, world!\"; ?>");
113+
let docroot = MockRoot::builder()
114+
.file("exists.php", "<?php echo \"Hello, world!\"; ?>")
115+
.build()
116+
.expect("should prepare docroot");
134117

135-
let cwd = current_dir().unwrap();
136-
let condition = ExistenceCondition::new(cwd);
118+
let condition = ExistenceCondition::new(docroot.clone());
137119

138120
let request = Request::builder()
139121
.url("http://example.com/exists.php")
@@ -145,8 +127,9 @@ mod test {
145127

146128
#[test]
147129
fn test_non_existence_condition() {
148-
let cwd = current_dir().unwrap();
149-
let condition = NonExistenceCondition::new(cwd);
130+
let docroot = MockRoot::builder().build().expect("should prepare docroot");
131+
132+
let condition = NonExistenceCondition::new(docroot.clone());
150133

151134
let request = Request::builder()
152135
.url("http://example.com/does_not_exist.php")
Lines changed: 2 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use super::{Condition, Request};
22

3+
// Tested via Condition::and(...) and Condition::or(...) doctests
4+
35
/// This provides logical grouping of conditions using either AND or OR
46
/// combination behaviours.
57
pub enum ConditionGroup<A, B>
@@ -37,77 +39,3 @@ where
3739
}
3840
}
3941
}
40-
41-
#[cfg(test)]
42-
mod test {
43-
use super::*;
44-
use crate::rewrite::{ConditionExt, HeaderCondition, PathCondition};
45-
46-
#[test]
47-
fn test_condition_group_and() {
48-
let header = HeaderCondition::new("TEST", "^foo$").expect("should be valid regex");
49-
50-
let path = PathCondition::new("^/index\\.php$").expect("should be valid regex");
51-
52-
let header_and_path = header.and(path);
53-
54-
// Check it matches when all conditions match
55-
let request = Request::builder()
56-
.url("http://example.com/index.php")
57-
.header("TEST", "foo")
58-
.build()
59-
.expect("request should build");
60-
61-
assert!(header_and_path.matches(&request));
62-
63-
// Check it _does not_ match if either condition does not match
64-
let only_header = Request::builder()
65-
.url("http://example.com/nope.php")
66-
.header("TEST", "foo")
67-
.build()
68-
.expect("request should build");
69-
70-
assert!(!header_and_path.matches(&only_header));
71-
72-
let only_url = Request::builder()
73-
.url("http://example.com/index.php")
74-
.build()
75-
.expect("request should build");
76-
77-
assert!(!header_and_path.matches(&only_url));
78-
}
79-
80-
#[test]
81-
fn test_condition_group_or() {
82-
let header = HeaderCondition::new("TEST", "^foo$").expect("should be valid regex");
83-
84-
let path = PathCondition::new("^/index\\.php$").expect("should be valid regex");
85-
86-
let header_or_path = header.or(path);
87-
88-
// Check it matches when either condition matches
89-
let request = Request::builder()
90-
.url("http://example.com/index.php")
91-
.header("TEST", "foo")
92-
.build()
93-
.expect("request should build");
94-
95-
assert!(header_or_path.matches(&request));
96-
97-
// Check it matches if either condition does not match
98-
let only_header = Request::builder()
99-
.url("http://example.com/nope.php")
100-
.header("TEST", "foo")
101-
.build()
102-
.expect("request should build");
103-
104-
assert!(header_or_path.matches(&only_header));
105-
106-
let only_url = Request::builder()
107-
.url("http://example.com/index.php")
108-
.build()
109-
.expect("request should build");
110-
111-
assert!(header_or_path.matches(&only_url));
112-
}
113-
}

‎crates/lang_handler/src/rewrite/condition/header.rs

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ pub struct HeaderCondition {
1515
impl HeaderCondition {
1616
/// Construct a new HeaderCondition matching the given header name and Regex
1717
/// pattern.
18+
///
19+
/// # Examples
20+
///
21+
/// ```
22+
/// # use lang_handler::rewrite::{Condition, HeaderCondition};
23+
/// # use lang_handler::Request;
24+
/// let condition = HeaderCondition::new("TEST", "^foo$")
25+
/// .expect("should be valid regex");
26+
/// ```
1827
pub fn new<S, R>(name: S, pattern: R) -> Result<Box<Self>, Error>
1928
where
2029
S: Into<String>,
@@ -30,6 +39,23 @@ impl HeaderCondition {
3039
impl Condition for HeaderCondition {
3140
/// A HeaderCondition matches a given request if the header specified in the
3241
/// constructor is both present and matches the given Regex pattern.
42+
///
43+
/// # Examples
44+
///
45+
/// ```
46+
/// # use lang_handler::rewrite::{Condition, HeaderCondition};
47+
/// # use lang_handler::Request;
48+
/// let condition = HeaderCondition::new("TEST", "^foo$")
49+
/// .expect("should be valid regex");
50+
///
51+
/// let request = Request::builder()
52+
/// .url("http://example.com/index.php")
53+
/// .header("TEST", "foo")
54+
/// .build()
55+
/// .expect("should build request");
56+
///
57+
/// assert!(condition.matches(&request));
58+
/// ```
3359
fn matches(&self, request: &Request) -> bool {
3460
request
3561
.headers()
@@ -38,21 +64,3 @@ impl Condition for HeaderCondition {
3864
.unwrap_or(false)
3965
}
4066
}
41-
42-
#[cfg(test)]
43-
mod test {
44-
use super::*;
45-
46-
#[test]
47-
fn test_header_condition() {
48-
let condition = HeaderCondition::new("TEST", "^foo$").expect("regex should be valid");
49-
50-
let request = Request::builder()
51-
.url("http://example.com/")
52-
.header("TEST", "foo")
53-
.build()
54-
.expect("request should build");
55-
56-
assert!(condition.matches(&request));
57-
}
58-
}

‎crates/lang_handler/src/rewrite/condition/mod.rs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,31 @@ pub trait ConditionExt: Condition {
3737
/// let header = HeaderCondition::new("TEST", "^foo$")
3838
/// .expect("should be valid regex");
3939
///
40-
/// let path_and_header = path.and(header);
40+
/// let condition = path.and(header);
4141
///
4242
/// let request = Request::builder()
4343
/// .url("http://example.com/index.php")
4444
/// .header("TEST", "foo")
4545
/// .build()
4646
/// .expect("should build request");
4747
///
48-
/// assert!(path_and_header.matches(&request));
48+
/// assert!(condition.matches(&request));
49+
/// #
50+
/// # // SHould _not_ match if either condition does not match
51+
/// # let only_header = Request::builder()
52+
/// # .url("http://example.com/nope.php")
53+
/// # .header("TEST", "foo")
54+
/// # .build()
55+
/// # .expect("request should build");
56+
/// #
57+
/// # assert!(!condition.matches(&only_header));
58+
/// #
59+
/// # let only_url = Request::builder()
60+
/// # .url("http://example.com/index.php")
61+
/// # .build()
62+
/// # .expect("request should build");
63+
/// #
64+
/// # assert!(!condition.matches(&only_url));
4965
/// ```
5066
fn and<C>(self: Box<Self>, other: Box<C>) -> Box<ConditionGroup<Self, C>>
5167
where
@@ -69,14 +85,30 @@ pub trait ConditionExt: Condition {
6985
/// let header = HeaderCondition::new("TEST", "^foo$")
7086
/// .expect("should be valid regex");
7187
///
72-
/// let path_or_header = path.or(header);
88+
/// let condition = path.or(header);
7389
///
7490
/// let request = Request::builder()
7591
/// .url("http://example.com/index.php")
7692
/// .build()
7793
/// .expect("should build request");
7894
///
79-
/// assert!(path_or_header.matches(&request));
95+
/// assert!(condition.matches(&request));
96+
/// #
97+
/// # // Should match if one condition does not
98+
/// # let only_header = Request::builder()
99+
/// # .url("http://example.com/nope.php")
100+
/// # .header("TEST", "foo")
101+
/// # .build()
102+
/// # .expect("request should build");
103+
/// #
104+
/// # assert!(condition.matches(&only_header));
105+
/// #
106+
/// # let only_url = Request::builder()
107+
/// # .url("http://example.com/index.php")
108+
/// # .build()
109+
/// # .expect("request should build");
110+
/// #
111+
/// # assert!(condition.matches(&only_url));
80112
/// ```
81113
fn or<C>(self: Box<Self>, other: Box<C>) -> Box<ConditionGroup<Self, C>>
82114
where

‎crates/lang_handler/src/rewrite/condition/path.rs

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,23 @@ impl PathCondition {
2626
impl Condition for PathCondition {
2727
/// A PathCondition matches a request if the path segment of the request url
2828
/// matches the pattern given when constructing the PathCondition.
29+
///
30+
/// # Examples
31+
///
32+
/// ```
33+
/// # use lang_handler::rewrite::{Condition, PathCondition};
34+
/// # use lang_handler::Request;
35+
/// let condition = PathCondition::new("^/index.php$")
36+
/// .expect("should be valid regex");
37+
///
38+
/// let request = Request::builder()
39+
/// .url("http://example.com/index.php")
40+
/// .build()
41+
/// .expect("should build request");
42+
///
43+
/// assert!(condition.matches(&request));
44+
/// ```
2945
fn matches(&self, request: &Request) -> bool {
3046
self.pattern.is_match(request.url().path())
3147
}
3248
}
33-
34-
#[cfg(test)]
35-
mod test {
36-
use super::*;
37-
38-
#[test]
39-
fn test_path_condition() {
40-
let condition = PathCondition::new("^/index.php$").expect("regex should be valid");
41-
42-
let request = Request::builder()
43-
.url("http://example.com/index.php")
44-
.build()
45-
.expect("request should build");
46-
47-
assert!(condition.matches(&request));
48-
}
49-
}

0 commit comments

Comments
 (0)