Skip to content

Commit d143ebe

Browse files
committed
UPDATE: basics working
1 parent c4aded6 commit d143ebe

File tree

4 files changed

+253
-8
lines changed

4 files changed

+253
-8
lines changed

Cargo.lock

Lines changed: 114 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ edition = "2021"
55

66
[dependencies]
77
axum = "0.7.5"
8+
axum-extra = { version = "0.9.3", features = ["form"] }
89
base64 = "0.22.1"
910
chrono = "0.4.38"
11+
html-escape = "0.2.13"
12+
regex = "1.10.6"
1013
rustc_version_runtime = "0.3.0"
1114
serde = { version = "1.0.208", features = ["derive"] }
1215
serde_json = "1.0.125"

bin/test.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env bash
2+
#
3+
# test locally running instance
4+
#
5+
6+
set -o errexit
7+
set -o pipefail
8+
set -o nounset
9+
10+
curl \
11+
--data 'callback=cb&regex=^([a-z]*)$&replacement=X&input=ab&input=ab1' \
12+
--request POST \
13+
--verbose \
14+
http://localhost:4000/test.json

src/main.rs

Lines changed: 122 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,28 @@
11

22
use axum::{
3-
body::Body,
4-
extract::{DefaultBodyLimit, Query},
5-
response::{IntoResponse, Redirect, Response},
6-
routing::get,
7-
Json, Router,
3+
body::Body, extract::{DefaultBodyLimit, Query}, response::{IntoResponse, Response}, routing::{get, post}, Json, Router
84
};
5+
use axum_extra::extract::Form;
96

107
use rustc_version_runtime::version;
118
use serde::{Deserialize, Serialize};
129
use tower_http::{limit::RequestBodyLimitLayer, services::ServeFile};
10+
use html_escape::encode_text;
1311

1412
#[tokio::main]
1513
async fn main() {
1614
// build our application with a single route
1715
let app = Router::new()
18-
//.route_service("/", get(|| async { Redirect::temporary("https://www.regexplanet.com/advanced/rust/index.html") }))
16+
//.route_service("/", get(|| async { axum::Redirect::temporary("https://www.regexplanet.com/advanced/rust/index.html") }))
1917
.route_service("/", get(root_handler))
18+
.route_service("/test.json", post(test_handler))
2019
.route_service("/favicon.ico", ServeFile::new("static/favicon.ico"))
2120
.route_service("/favicon.svg", ServeFile::new("static/favicon.svg"))
2221
.route_service("/robots.txt", ServeFile::new("static/robots.txt"))
2322
.route_service("/status.json", get(get_status))
2423
.layer(DefaultBodyLimit::disable())
2524
.layer(RequestBodyLimitLayer::new(10 * 1024 * 1024 /* 10mb */));
2625

27-
// run our app with hyper, listening globally on port 3000
28-
2926
// get address from environment variable
3027
let address = std::env::var("ADDRESS").unwrap_or_else(|_| "0.0.0.0".to_string());
3128

@@ -89,4 +86,121 @@ async fn root_handler() -> Response<Body> {
8986
.header("Content-Type", "text/plain; charset=utf-8")
9087
.body(Body::from("Dev server running!"))
9188
.unwrap();
89+
}
90+
91+
#[derive(Deserialize, Serialize, Debug)]
92+
struct TestInput {
93+
regex: String,
94+
#[serde(default)]
95+
replacement: String,
96+
#[serde(default)]
97+
callback: String,
98+
#[serde(default)]
99+
options: Vec<String>,
100+
#[serde(default)]
101+
#[serde(rename(deserialize = "input"))]
102+
inputs: Vec<String>,
103+
}
104+
105+
#[derive(Serialize)]
106+
struct TestOutput {
107+
success: bool,
108+
message: String,
109+
html: String,
110+
}
111+
112+
async fn test_handler(Form(test_input): Form<TestInput>) -> Response<Body> {
113+
114+
let mut html = String::new();
115+
116+
html.push_str("<table class=\"table table-bordered table-striped\" style=\"width: auto;\">\n");
117+
html.push_str("\t<tbody>\n");
118+
html.push_str("\t\t<tr>\n");
119+
html.push_str("\t\t\t<td>Regular Expression</td>\n");
120+
html.push_str(&format!("\t\t\t<td><code>{}</code></td>\n", encode_text(&test_input.regex)));
121+
html.push_str("\t\t</tr>\n");
122+
123+
if test_input.replacement != "" {
124+
html.push_str("\t\t<tr>\n");
125+
html.push_str("\t\t\t<td>Replacement</td>\n");
126+
html.push_str(&format!("\t\t\t<td><code>{}</code></td>\n", encode_text(&test_input.replacement)));
127+
html.push_str("\t\t</tr>\n");
128+
}
129+
130+
html.push_str("\t</tbody>\n");
131+
html.push_str("</table>");
132+
133+
let the_regex = regex::RegexBuilder::new(&test_input.regex)
134+
.case_insensitive(test_input.options.contains(&"i".to_string()))
135+
.multi_line(test_input.options.contains(&"m".to_string()))
136+
.dot_matches_new_line(test_input.options.contains(&"s".to_string()))
137+
.build();
138+
139+
if the_regex.is_err() {
140+
let err_msg = the_regex.unwrap_err().to_string();
141+
142+
html.push_str("<div class=\"alert alert-danger\" role=\"alert\">\n");
143+
html.push_str(&format!("<strong>Error:</strong> {}<br>\n", encode_text(&err_msg)));
144+
html.push_str("</div>\n");
145+
146+
return handle_jsonp(&test_input.callback, html);
147+
}
148+
149+
if test_input.inputs.len() == 0 {
150+
html.push_str("<div class=\"alert alert-danger\" role=\"alert\">\n");
151+
html.push_str("No inputs to test!\n");
152+
html.push_str("</div>\n");
153+
154+
return handle_jsonp(&test_input.callback, html);
155+
}
156+
157+
let the_regex = the_regex.unwrap();
158+
159+
html.push_str("<table class=\"table table-bordered table-striped\" style=\"width: auto;\">\n");
160+
html.push_str("\t<thead>\n");
161+
html.push_str("\t\t<tr>\n");
162+
html.push_str("\t\t\t<th>Input</th>\n");
163+
html.push_str("\t\t\t<th>is_match</th>\n");
164+
html.push_str("\t\t</tr>\n");
165+
html.push_str("\t</thead>\n");
166+
html.push_str("\t<tbody>\n");
167+
168+
for input in test_input.inputs {
169+
//let output = the_regex.as_ref().unwrap().replace_all(&input, &test_input.replacement);
170+
html.push_str("\t\t<tr>\n");
171+
html.push_str(&format!("\t\t\t<td>{}</td>\n", encode_text(&input)));
172+
let is_match = if the_regex.is_match(&input) { "true" } else { "false" };
173+
html.push_str(&format!("\t\t\t<td>{}</td>\n", is_match));
174+
html.push_str("\t\t</tr>\n");
175+
}
176+
177+
html.push_str("\t</tbody>\n");
178+
html.push_str("</table>");
179+
180+
return handle_jsonp(&test_input.callback, html);
181+
}
182+
183+
fn handle_jsonp(callback: &str, html: String) -> Response<Body> {
184+
185+
186+
let test_output = TestOutput {
187+
success: true,
188+
message: "OK".to_string(),
189+
html: html,
190+
};
191+
192+
let json_output = serde_json::to_string(&test_output).unwrap();
193+
194+
if callback == "" {
195+
return Response::builder()
196+
.header("Content-Type", "application/json; charset=utf-8")
197+
.body(Body::from(json_output))
198+
.unwrap();
199+
} else {
200+
let jsonp = format!("{}({})", callback, json_output);
201+
return Response::builder()
202+
.header("Content-Type", "text/html; charset=utf-8")
203+
.body(Body::from(jsonp))
204+
.unwrap();
205+
}
92206
}

0 commit comments

Comments
 (0)