pub fn decode(url: &str) -> Option<String>
Expand description
Decode a url encoded string.
Supports +
and %
encoding.
If the decode fails for any reason, None
is returned.
Examples found in repository?
examples/application_quote_book.rs (line 53)
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
fn main() {
set_log_level(Level::Trace);
let app = App::new(PathBuf::from("quotes.txt"));
app.load();
let mut server = Server::new(Ipv4Addr::LOCALHOST, 8080).state(app);
// Route to serve the homepage (page that has add quote form)
server.route(Method::GET, "/", |_| {
Response::new()
.text(String::new() + HEADER + HOME)
.content(Content::HTML)
});
// Route to handle creating new quotes.
// After successful creation the user will be redirected to the new quotes page.
server.stateful_route(Method::POST, "/api/new", |app, req| {
let form = Query::from_body(&String::from_utf8_lossy(&req.body));
let name =
url::decode(form.get("author").expect("No author supplied")).expect("Invalid author");
let body =
url::decode(form.get("quote").expect("No quote supplied")).expect("Invalid quote");
let quote = Quote {
name,
value: body,
date: now(),
};
let mut quotes = app.quotes.write().unwrap();
let id = quotes.len();
quotes.insert(id.to_string(), quote);
drop(quotes);
trace!(Level::Trace, "Added new quote #{id}");
app.save();
Response::new()
.status(Status::SeeOther)
.header(HeaderType::Location, format!("/quote/{id}"))
.text("Redirecting to quote page.")
});
server.stateful_route(Method::GET, "/quote/{id}", |app, req| {
let id = req.param("id").unwrap();
if id == "undefined" {
return Response::new();
}
let id = id.parse::<usize>().expect("ID is not a valid integer");
let quotes = app.quotes.read().unwrap();
if id >= quotes.len() {
return Response::new()
.status(Status::NotFound)
.text(format!("No quote with the id {id} was found."));
}
let quote = quotes.get(&id.to_string()).unwrap();
Response::new().content(Content::HTML).text(
String::new()
+ HEADER
+ "E
.replace("{QUOTE}", "e.value)
.replace("{AUTHOR}", "e.name)
.replace("{TIME}", &imp_date(quote.date)),
)
});
server.stateful_route(Method::GET, "/quotes", |app, _req| {
let mut out = String::from(HEADER);
out.push_str("<ul>");
for i in app.quotes.read().unwrap().iter() {
out.push_str(&format!(
"<li><a href=\"/quote/{}\">\"{}\" - {}</a></li>\n",
i.0, i.1.name, i.1.value
));
}
Response::new().text(out + "</ul>").content(Content::HTML)
});
// Note: In a production application you may want to multithread the server with the Server::start_threaded method.
server.start().unwrap();
}
fn now() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_secs()
}
impl App {
fn new(path: PathBuf) -> Self {
Self {
path,
quotes: RwLock::new(HashMap::new()),
}
}
fn load(&self) {
if !self.path.exists() {
trace!(Level::Trace, "No save file found. Skipping loading.");
return;
}
let data = fs::read_to_string(&self.path).unwrap();
let mut quotes = self.quotes.write().unwrap();
quotes.clear();
for i in data.lines() {
let (name, quote) = i.split_once(':').unwrap();
if let Some(i) = Quote::load(quote) {
quotes.insert(name.to_owned(), i);
continue;
}
trace!(Level::Error, "Error loading entry");
}
trace!("Loaded {} entries", quotes.len());
}
fn save(&self) {
trace!(Level::Trace, "Saving quotes");
let mut out = String::new();
for i in self.quotes.read().unwrap().iter() {
out.push_str(&format!("{}:{}\n", i.0, i.1.save()));
}
fs::write(&self.path, out).unwrap();
}
}
impl Quote {
fn save(&self) -> String {
format!(
"{}:{}:{}",
url::encode(&self.name),
url::encode(&self.value),
self.date
)
}
fn load(line: &str) -> Option<Self> {
let mut parts = line.split(':');
let name = url::decode(parts.next()?).unwrap();
let value = url::decode(parts.next()?).unwrap();
let date = parts.next()?.parse().ok()?;
Some(Self { name, value, date })
}
More examples
examples/application_paste_bin.rs (line 77)
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
fn main() {
// Create Server
let mut server = Server::new("localhost", 8080).state(RwLock::new(Vec::new()));
// New paste interface
server.route(Method::GET, "/", |_req| {
Response::new().content(Content::HTML).text(
r#"
<form action="/new-form" method="post">
<input type="text" name="name" id="name" placeholder="Title">
<br />
<textarea id="body" name="body" rows="5" cols="33"></textarea>
<input type="submit" value="Submit" />
</form>
"#,
)
});
// New paste API handler
server.stateful_route(Method::POST, "/new", move |app, req| {
// Make sure paste data isn't too long
if req.body.len() > DATA_LIMIT {
return Response::new()
.status(Status::NotFound)
.text("Data too big!");
}
// Get the data as string
let body_str = String::from_utf8_lossy(&req.body).to_string();
// Get the name from the Name header
let name = req.headers.get("Name").unwrap_or("Untitled");
let paste = Paste {
name: name.to_owned(),
body: body_str,
time: Instant::now(),
};
// Push this paste to the pastes vector
let mut pastes = app.write().unwrap();
let id = pastes.len();
pastes.push(paste);
// Send Redirect response
Response::new()
.status(Status::MovedPermanently)
.header(HeaderType::Location, format!("/p/{id}"))
.text(format!("Redirecting to /p/{id}."))
});
// New paste form handler
server.stateful_route(Method::POST, "/new-form", |app, req| {
// Get data from response
let query = Query::from_body(String::from_utf8_lossy(&req.body).borrow());
let name = url::decode(query.get("name").unwrap_or("Untitled")).expect("Invalid name");
let body = url::decode(query.get("body").expect("No body supplied")).expect("Invalid body");
// Make sure paste data isn't too long
if body.len() > DATA_LIMIT {
return Response::new()
.status(Status::NotFound)
.text("Data too big!");
}
let paste = Paste {
name,
body,
time: Instant::now(),
};
// Push this paste to the pastes vector
let mut pastes = app.write().unwrap();
let id = pastes.len();
pastes.push(paste);
// Send Redirect response
Response::new()
.status(Status::MovedPermanently)
.text("Ok")
.header(HeaderType::Location, format!("/p/{}", id))
});
// Get pate handler
server.stateful_route(Method::GET, "/p/{id}", move |app, req| {
// Get is from path param
let id = req.param("id").unwrap().parse::<usize>().unwrap();
// Get the paste by id
let paste = &app.read().unwrap()[id];
// Send paste
Response::new().text(&paste.body)
});
// View all pastes
server.stateful_route(Method::GET, "/pastes", move |app, _req| {
// Starter HTML
let mut out = String::from(
r#"<a href="/">New Paste</a><meta charset="UTF-8"><table><tr><th>Name</th><th>Date</th><th>Link</th></tr>"#,
);
// Add a table row for each paste
for (i, e) in app.read().unwrap().iter().enumerate() {
out.push_str(&format!(
"<tr><td>{}</td><td>{}</td><td><a href=\"/p/{}\">🔗</a></td></tr>",
e.name,
fmt_relative_time(e.time.elapsed().as_secs()),
i
));
}
// Send HTML
Response::new()
.text(format!("{}</table>", out))
.content(Content::HTML)
});
server.start().unwrap();
}