add help page
This commit is contained in:
parent
27adb0f196
commit
51411e3d2b
@ -30,7 +30,7 @@ pub struct Card {
|
||||
|
||||
impl Display for Card {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, r#"<h2><a href="/{}">{}</a></h2><br/><em>"#, &self.id, &self.name)?;
|
||||
write!(f, r#"<h2><a href="/card/{}">{}</a></h2><br/><em>"#, &self.id, &self.name)?;
|
||||
if let Some(level) = self.level {
|
||||
if self.card_type.contains("XYZ") {
|
||||
f.write_str("Rank ")?;
|
||||
|
28
src/main.rs
28
src/main.rs
@ -28,7 +28,10 @@ async fn main() -> std::io::Result<()> {
|
||||
// tap these so they’re initialized
|
||||
let num_cards = (CARDS_BY_ID.len() + SEARCH_CARDS.len()) / 2;
|
||||
println!("Read {num_cards} cards in {:?}", now.elapsed());
|
||||
HttpServer::new(|| App::new().service(search).service(card_info)).bind((Ipv4Addr::from([127, 0, 0, 1]), 8080))?.run().await
|
||||
HttpServer::new(|| App::new().service(search).service(card_info).service(help))
|
||||
.bind((Ipv4Addr::from([127, 0, 0, 1]), 8080))?
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@ -37,6 +40,13 @@ struct Query {
|
||||
}
|
||||
|
||||
const HEADER: &str = include_str!("../static/header.html");
|
||||
const HELP_CONTENT: &str = include_str!("../static/help.html");
|
||||
const FOOTER: &str = r#"<div id="bottom">
|
||||
<a href="/">Home</a>
|
||||
|
||||
<a href="/help">Query Syntax</a>
|
||||
</div>
|
||||
</body></html>"#;
|
||||
|
||||
#[get("/")]
|
||||
async fn search(q: Option<Either<web::Query<Query>, web::Form<Query>>>) -> Result<HttpResponse, Box<dyn std::error::Error>> {
|
||||
@ -57,7 +67,7 @@ async fn search(q: Option<Either<web::Query<Query>, web::Form<Query>>>) -> Resul
|
||||
Ok(HttpResponse::Ok().insert_header(header::ContentType::html()).body(res))
|
||||
}
|
||||
|
||||
#[get("/{id}")]
|
||||
#[get("/card/{id}")]
|
||||
async fn card_info(card_id: web::Path<usize>) -> Result<HttpResponse, Box<dyn std::error::Error>> {
|
||||
let mut res = String::with_capacity(2_000);
|
||||
res.push_str(HEADER);
|
||||
@ -80,6 +90,16 @@ async fn card_info(card_id: web::Path<usize>) -> Result<HttpResponse, Box<dyn st
|
||||
Ok(HttpResponse::Ok().insert_header(header::ContentType::html()).body(res))
|
||||
}
|
||||
|
||||
#[get("/help")]
|
||||
async fn help() -> Result<HttpResponse, Box<dyn std::error::Error>> {
|
||||
let mut res = String::with_capacity(HEADER.len() + HELP_CONTENT.len() + FOOTER.len() + 250);
|
||||
res.push_str(HEADER);
|
||||
render_searchbox(&mut res, &None)?;
|
||||
res.push_str(HELP_CONTENT);
|
||||
res.push_str(FOOTER);
|
||||
Ok(HttpResponse::Ok().insert_header(header::ContentType::html()).body(res))
|
||||
}
|
||||
|
||||
fn render_searchbox(res: &mut String, query: &Option<String>) -> std::fmt::Result {
|
||||
write!(
|
||||
res,
|
||||
@ -123,7 +143,7 @@ fn render_results(res: &mut String, query: &str) -> Result<(), Box<dyn std::erro
|
||||
for card in matches {
|
||||
write!(
|
||||
res,
|
||||
r#"<tr><td>{card}</td><td><a href="/{}"><img src="http://localhost:80/img/{}.jpg" class="thumb"/></a></td></tr>"#,
|
||||
r#"<tr><td>{card}</td><td><a href="/card/{}"><img src="http://localhost:80/img/{}.jpg" class="thumb"/></a></td></tr>"#,
|
||||
card.id, card.id
|
||||
)?;
|
||||
}
|
||||
@ -131,5 +151,5 @@ fn render_results(res: &mut String, query: &str) -> Result<(), Box<dyn std::erro
|
||||
}
|
||||
|
||||
fn finish_document(res: &mut String) {
|
||||
res.push_str("</body></html>")
|
||||
res.push_str(FOOTER)
|
||||
}
|
||||
|
@ -101,6 +101,7 @@ impl FromStr for Field {
|
||||
"c" | "class" => Self::Class,
|
||||
"o" | "eff" | "text" | "effect" | "e" => Self::Text,
|
||||
"lr" | "linkrating" => Self::LinkRating,
|
||||
"name" => Self::Name,
|
||||
_ => Err(s.to_string())?,
|
||||
})
|
||||
}
|
||||
|
@ -27,6 +27,11 @@ body {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: "Hack", "Fira Code", "Courier New", monospace;
|
||||
background-color: #2a2a2a;
|
||||
}
|
||||
|
||||
.meta {
|
||||
font-size: 75%;
|
||||
color: var(--fg-dim);
|
||||
@ -76,7 +81,7 @@ td:nth-child(2) {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
h2 {
|
||||
margin-block-end: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
@ -109,6 +114,17 @@ h2 {
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* little floaty thing at the bottom with links to home and help */
|
||||
#bottom {
|
||||
position: fixed;
|
||||
bottom: 3em;
|
||||
right: 10%;
|
||||
background-color: var(--bg2);
|
||||
z-index: 2;
|
||||
padding: 1em;
|
||||
border-radius: 1em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
35
static/help.html
Normal file
35
static/help.html
Normal file
@ -0,0 +1,35 @@
|
||||
<h1>Query Syntax</h1>
|
||||
The syntax is heavily inspired by <a href="https://scryfall.com/docs/syntax">Scryfall</a> with some changes and a lot fewer features.<br/>
|
||||
You can filter different characteristics of a card and combine multiple filters into one search. See below for examples.
|
||||
|
||||
<h2>Search fields</h2>
|
||||
Currently supported search fields are:
|
||||
<ul>
|
||||
<li><code>atk</code> and <code>def</code>.</li>
|
||||
<li>The <code>level</code> (or <code>l</code>) of a monster. Note that the search does not distinguish between level and rank, so <a href="/?q=l%3A4"><code>l:4</code></a> will return all monsters that are either level 4 or rank 4.</li>
|
||||
<li>The <code>linkrating</code> (or <code>lr</code>) of a monster.</li>
|
||||
<li>The <code>class</code> (or <code>c</code>) which you might call card type. Since “type” already means something else, the search uses <code>class</code> for “Spell”, “Trap”, “Effect”, “XYZ”, etc., so <a href="/?q=c%3Alink"><code>c:link</code></a> will return all link monsters.</li>
|
||||
<li>The <code>type</code> (or <code>t</code>) of a card (this is “Warrior”, “Pyro”, “Insect”, etc. for monsters, but also “quick-play”, “counter”, or “normal” for Spells/Traps).</li>
|
||||
<li>The <code>attribute</code> (or <code>attr</code> or <code>a</code>) of a card. This is “Light”, “Dark”, “Earth”, etc.</li>
|
||||
<li>The <code>text</code> (or <code>effect</code>, <code>eff</code>, <code>e</code>, or <code>o</code>) of a card. This is either the effect or flavor text (for normal monsters). For pendulum cards, this searches in both pendulum and monster effects. The <code>o</code> alias is to help my muscle memory coming from Scryfall.</li>
|
||||
</ul>
|
||||
Anything not associated with a search field is interpreted as a search in the card name, so <a href="/?q=l%3A4+utopia"><code>l:4 utopia</code></a> will show all level/rank 4 monsters with “Utopia” in their name.<br/>
|
||||
If your search contains spaces (e.g. searching for an effect that says “destroy that target”), the text must be quoted like <code>effect:"destroy that target"</code>.
|
||||
<br/><br/>
|
||||
Note that all fields are case-insensitive, so <code>class:NORMAL</code> is the same as <code>class:Normal</code> or <code>class:normal</code>.
|
||||
|
||||
<h2>Search operators</h2>
|
||||
The following search operators are supported:
|
||||
<ul>
|
||||
<li>Equality (<code>:</code>, <code>=</code>, or <code>==</code>) checks if the value is equal to your search. For text fields, this checks if your search is contained in the field, so <a href="/?q=effect%3Abanish"><code>effect:banish</code></a> will show all cards that have the word “banish” anywhere in their text.</li>
|
||||
<li>Inequality (<code>!=</code>) checks if the value is not equal to your search. For text fields, this return cards that do not contain the word you searched.</li>
|
||||
<li>Comparisons (<code><</code>, <code>></code>, <code><=</code>, <code>>=</code>) check if the value is less than, greater than, less than or equal, and greater than or equal to your search. <a href="/?q=atk%3E%3D4000"><code>atk>=4000</code></a> will show all cards with an ATK of at least 4000. These operators do not work for text fields.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Examples</h2>
|
||||
<ul>
|
||||
<li>All Fire monsters with exactly 200 DEF: <a href="/?q=a%3Afire+def%3A200"><code>a:fire def:200</code></a></li>
|
||||
<li>All “Blue-eyes” fusion monsters except the ones that are level 12: <a href="/?q=c%3Afusion+l%21%3D12+blue-eyes"><code>c:fusion l!=12 blue-eyes</code></a></li>
|
||||
<li>All Synchro monsters that are Dark attribute, level 5 or higher, and have exactly 2200 ATK: <a href="/?q=c%3Asynchro+a%3Adark+l%3E%3D5+atk%3A2200"><code>c:synchro a:dark l>=5 atk:2200</code></a></li>
|
||||
<li>All counter traps that can negate summons: <a href="/?q=c%3Atrap+t%3Acounter+e%3A%22negate+the+summon%22"><code>c:trap t:counter e:"negate the summon"</code></a></li>
|
||||
</ul>
|
Loading…
Reference in New Issue
Block a user