redirect to detail if there’s only a single result

This commit is contained in:
kageru 2023-07-28 18:42:35 +02:00
parent 8691d3c685
commit 2056b9b9f2
Signed by: kageru
GPG Key ID: 8282A2BEA4ADA3D2
4 changed files with 66 additions and 52 deletions

36
Cargo.lock generated

@ -57,12 +57,12 @@ dependencies = [
[[package]] [[package]]
name = "actix-macros" name = "actix-macros"
version = "0.2.3" version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
dependencies = [ dependencies = [
"quote", "quote",
"syn 1.0.109", "syn 2.0.27",
] ]
[[package]] [[package]]
@ -359,9 +359,9 @@ dependencies = [
[[package]] [[package]]
name = "either" name = "either"
version = "1.8.1" version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
@ -741,9 +741,9 @@ dependencies = [
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.31" version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -839,9 +839,9 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.1.0" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "semver" name = "semver"
@ -851,29 +851,29 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.171" version = "1.0.177"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" checksum = "63ba2516aa6bf82e0b19ca8b50019d52df58455d3cf9bdaf6315225fdd0c560a"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.171" version = "1.0.177"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" checksum = "401797fe7833d72109fedec6bfcbe67c0eed9b99772f26eb8afd261f0abc6fd3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.26", "syn 2.0.27",
] ]
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.103" version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b" checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@ -950,9 +950,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.26" version = "2.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970" checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

@ -6,11 +6,11 @@ edition = "2021"
[dependencies] [dependencies]
serde_json = "1.0" serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
nom = "7.1.3" nom = "7.1"
actix-web = { version = "4.3.0", default_features = false, features = ["macros"] } actix-web = { version = "4.3", default_features = false, features = ["macros"] }
itertools = "0.11.0" itertools = "0.11"
time = { version = "0.3.17", features = ["serde", "serde-human-readable"] } time = { version = "0.3", features = ["serde", "serde-human-readable"] }
regex = { version = "1.7.1", default_features = false, features = ["std"] } regex = { version = "1.7", default_features = false, features = ["std"] }
[dev-dependencies] [dev-dependencies]
test-case = "3.1" test-case = "3.1"

@ -66,7 +66,7 @@ pub struct Set {
impl Card { impl Card {
pub fn extended_info(&self) -> Result<String, fmt::Error> { pub fn extended_info(&self) -> Result<String, fmt::Error> {
let mut s = String::with_capacity(1000); let mut s = String::with_capacity(1000);
write!(s, "<p><a href=\"https://db.ygorganization.com/search#card:{}\">Rulings</a> – <a href=\"https://yugipedia.com/wiki/{}\">Yugipedia</a></p>", &self.name, &self.name.replace(' ', "_"))?; write!(s, "<p><a href=\"https://db.ygorganization.com/search#card:{}\">Rulings</a> – <a href=\"https://yugipedia.com/wiki/{}\">Yugipedia</a></p>", &self.name, &self.id)?;
s.push_str("<h3>Printings:</h3>"); s.push_str("<h3>Printings:</h3>");
for printing in &self.card_sets { for printing in &self.card_sets {
write!(s, "{}: {} ({})", printing.set_name, printing.set_code, printing.set_rarity)?; write!(s, "{}: {} ({})", printing.set_name, printing.set_code, printing.set_rarity)?;

@ -82,6 +82,12 @@ struct Query {
q: String, q: String,
} }
#[derive(Debug)]
enum TargetPage {
Data(PageData),
Redirect(String),
}
#[derive(Debug)] #[derive(Debug)]
struct PageData { struct PageData {
description: String, description: String,
@ -118,7 +124,7 @@ async fn search(q: Option<Either<web::Query<Query>, web::Form<Query>>>) -> AnyRe
let mut res = String::with_capacity(10_000); let mut res = String::with_capacity(10_000);
let data = match q { let data = match q {
Some(q) => compute_results(q)?, Some(q) => compute_results(q)?,
None => PageData { None => TargetPage::Data(PageData {
title: "YGO card search".to_owned(), title: "YGO card search".to_owned(),
description: "Enter a query above to search".to_owned(), description: "Enter a query above to search".to_owned(),
query: None, query: None,
@ -126,10 +132,15 @@ async fn search(q: Option<Either<web::Query<Query>, web::Form<Query>>>) -> AnyRe
<p>Enter a query above to search or read the <a href=\"/help\">query syntax</a> for more information.</p>\ <p>Enter a query above to search or read the <a href=\"/help\">query syntax</a> for more information.</p>\
<p>The source code is available <a href=\"https://git.kageru.moe/kageru/aro\">on Gitea</a>.</p>" <p>The source code is available <a href=\"https://git.kageru.moe/kageru/aro\">on Gitea</a>.</p>"
.to_owned(), .to_owned(),
}, }),
}; };
add_data(&mut res, &data)?; match data {
Ok(HttpResponse::Ok().insert_header(header::ContentType::html()).body(res)) TargetPage::Data(data) => {
add_data(&mut res, &data)?;
Ok(HttpResponse::Ok().insert_header(header::ContentType::html()).body(res))
}
TargetPage::Redirect(target) => Ok(HttpResponse::Found().insert_header((header::LOCATION, target)).finish()),
}
} }
#[get("/card/{id}")] #[get("/card/{id}")]
@ -187,18 +198,18 @@ fn add_searchbox(res: &mut String, query: &Option<String>) -> std::fmt::Result {
) )
} }
fn compute_results(raw_query: String) -> AnyResult<PageData> { fn compute_results(raw_query: String) -> AnyResult<TargetPage> {
let mut body = String::with_capacity(10_000); let mut body = String::with_capacity(10_000);
let (raw_filters, query) = match parser::parse_filters(raw_query.trim()) { let (raw_filters, query) = match parser::parse_filters(raw_query.trim()) {
Ok(q) => q, Ok(q) => q,
Err(e) => { Err(e) => {
let s = format!("Could not parse query: {e:?}"); let s = format!("Could not parse query: {e:?}");
return Ok(PageData { return Ok(TargetPage::Data(PageData {
description: s.clone(), description: s.clone(),
query: Some(raw_query), query: Some(raw_query),
body: s, body: s,
title: "YGO Card Database".to_owned(), title: "YGO Card Database".to_owned(),
}); }));
} }
}; };
let now = Instant::now(); let now = Instant::now();
@ -210,32 +221,35 @@ fn compute_results(raw_query: String) -> AnyResult<PageData> {
.collect(); .collect();
let readable_query = format!("Showing {} results where {}", matches.len(), raw_filters.iter().map(|f| f.to_string()).join(" and "),); let readable_query = format!("Showing {} results where {}", matches.len(), raw_filters.iter().map(|f| f.to_string()).join(" and "),);
write!(body, "<span class=\"meta\">{readable_query} (took {:?})</span>", now.elapsed())?; write!(body, "<span class=\"meta\">{readable_query} (took {:?})</span>", now.elapsed())?;
if matches.is_empty() { match matches[..] {
return Ok(PageData { [] => Ok(TargetPage::Data(PageData {
description: readable_query, description: readable_query,
query: Some(raw_query), query: Some(raw_query),
body, body,
title: "No results - YGO Card Database".to_owned(), title: "No results - YGO Card Database".to_owned(),
}); })),
[card] => Ok(TargetPage::Redirect(format!("/card/{}", card.id))),
ref cards => {
body.push_str("<div style=\"display: flex; flex-wrap: wrap;\">");
for card in cards {
write!(
body,
r#"<a class="cardresult" href="/card/{}"><img alt="Card Image: {}" src="{}/static/thumb/{}.jpg" class="thumb"/>{card}</a>"#,
card.id,
card.name,
IMG_HOST.as_str(),
card.id
)?;
}
body.push_str("</div>");
Ok(TargetPage::Data(PageData {
description: readable_query,
query: Some(raw_query),
body,
title: format!("{} results - YGO Card Database", cards.len()),
}))
}
} }
body.push_str("<div style=\"display: flex; flex-wrap: wrap;\">");
for card in &matches {
write!(
body,
r#"<a class="cardresult" href="/card/{}"><img alt="Card Image: {}" src="{}/static/thumb/{}.jpg" class="thumb"/>{card}</a>"#,
card.id,
card.name,
IMG_HOST.as_str(),
card.id
)?;
}
body.push_str("</div>");
Ok(PageData {
description: readable_query,
query: Some(raw_query),
body,
title: format!("{} results - YGO Card Database", matches.len()),
})
} }
fn add_data(res: &mut String, pd: &PageData) -> AnyResult<()> { fn add_data(res: &mut String, pd: &PageData) -> AnyResult<()> {