From 440b582e5c5b718d7aa21c95b8ee61a010ca36d7 Mon Sep 17 00:00:00 2001 From: LucaCappelletti94 Date: Sun, 21 Jun 2026 07:43:54 +0200 Subject: [PATCH] Add per-parser README rank and score badges with parser-page previews and CI SVG validation --- .github/workflows/ci.yml | 20 +-- .github/workflows/pages.yml | 1 + CONTRIBUTING.md | 8 ++ Cargo.toml | 2 +- badgegen/Cargo.toml | 13 ++ badgegen/src/main.rs | 27 ++++ src/bin/sqlbench.rs | 19 ++- timemachine/src/run.rs | 4 + viz/src/badge.rs | 94 ++++++++++++++ viz/src/lib.rs | 1 + web/assets/main.css | 20 +++ web/src/badges.rs | 119 ++++++++++++++++++ web/src/components.rs | 53 +++++++- web/src/lib.rs | 57 +++++++++ web/src/main.rs | 62 +-------- .../badges/databend_common_ast-overall.svg | 1 + .../badges/databend_common_ast-score.svg | 1 + web/static/badges/orql-overall.svg | 1 + web/static/badges/orql-score.svg | 1 + web/static/badges/pg_query_rs-overall.svg | 1 + web/static/badges/pg_query_rs-score.svg | 1 + .../badges/pg_query_summary-overall.svg | 1 + web/static/badges/pg_query_summary-score.svg | 1 + web/static/badges/polyglot_sql-overall.svg | 1 + web/static/badges/polyglot_sql-score.svg | 1 + web/static/badges/qusql_parse-overall.svg | 1 + web/static/badges/qusql_parse-score.svg | 1 + web/static/badges/sqlglot_rust-overall.svg | 1 + web/static/badges/sqlglot_rust-score.svg | 1 + web/static/badges/sqlite3_parser-overall.svg | 1 + web/static/badges/sqlite3_parser-score.svg | 1 + web/static/badges/sqlparser_rs-overall.svg | 1 + web/static/badges/sqlparser_rs-score.svg | 1 + web/static/badges/turso_parser-overall.svg | 1 + web/static/badges/turso_parser-score.svg | 1 + 35 files changed, 447 insertions(+), 73 deletions(-) create mode 100644 badgegen/Cargo.toml create mode 100644 badgegen/src/main.rs create mode 100644 viz/src/badge.rs create mode 100644 web/src/badges.rs create mode 100644 web/src/lib.rs create mode 100644 web/static/badges/databend_common_ast-overall.svg create mode 100644 web/static/badges/databend_common_ast-score.svg create mode 100644 web/static/badges/orql-overall.svg create mode 100644 web/static/badges/orql-score.svg create mode 100644 web/static/badges/pg_query_rs-overall.svg create mode 100644 web/static/badges/pg_query_rs-score.svg create mode 100644 web/static/badges/pg_query_summary-overall.svg create mode 100644 web/static/badges/pg_query_summary-score.svg create mode 100644 web/static/badges/polyglot_sql-overall.svg create mode 100644 web/static/badges/polyglot_sql-score.svg create mode 100644 web/static/badges/qusql_parse-overall.svg create mode 100644 web/static/badges/qusql_parse-score.svg create mode 100644 web/static/badges/sqlglot_rust-overall.svg create mode 100644 web/static/badges/sqlglot_rust-score.svg create mode 100644 web/static/badges/sqlite3_parser-overall.svg create mode 100644 web/static/badges/sqlite3_parser-score.svg create mode 100644 web/static/badges/sqlparser_rs-overall.svg create mode 100644 web/static/badges/sqlparser_rs-score.svg create mode 100644 web/static/badges/turso_parser-overall.svg create mode 100644 web/static/badges/turso_parser-score.svg diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c63d196..c21dc52 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,17 +54,17 @@ jobs: run: sudo apt-get update && sudo apt-get install -y libxml2-utils - name: Validate SVG files run: | - shopt -s nullglob - svg_files=(*.svg **/*.svg) - if [ ${#svg_files[@]} -eq 0 ]; then - echo "No SVG files found" - exit 0 + set -euo pipefail + # Validate every tracked SVG (the prior glob missed nested files such as + # the per-parser badges under web/static/badges/). Tracked-only, so build + # output and the dataset cache are never picked up. + mapfile -t svgs < <(git ls-files '*.svg') + if [ ${#svgs[@]} -eq 0 ]; then + echo "No tracked SVG files found" + exit 1 fi - echo "Validating ${#svg_files[@]} SVG file(s)..." - for svg in "${svg_files[@]}"; do - echo " Checking $svg" - xmllint --noout "$svg" - done + echo "Validating ${#svgs[@]} SVG file(s)..." + xmllint --noout "${svgs[@]}" echo "All SVG files are valid XML" test: diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index b4c8bc8..38d2018 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -50,6 +50,7 @@ jobs: cp web/static/robots.txt _site/robots.txt # crawler directives + sitemap pointer cp web/static/sitemap.xml _site/sitemap.xml # sitemap for search engines cp -r web/static/failures _site/failures # rejected-statement TSV downloads + cp -r web/static/badges _site/badges # per-parser README badge SVGs - uses: actions/upload-pages-artifact@v3 with: path: _site diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4ebaf19..2b65a95 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,6 +34,14 @@ cargo run --bin sqlbench -- export # read all of the above, write web/asse The charts are rendered in the browser from the JSON by the shared `viz` crate (plotters, SVG backend), so no chart images are committed. +The per-parser README badges under `web/static/badges/` are derived from the same scoring, so refresh them whenever the snapshot changes: + +```bash +cargo run -p badgegen # rewrite web/static/badges/*.svg from the embedded snapshot +``` + +`badgegen` reads the snapshot the `web` crate embeds, so run it after `export`. The shields-style SVG renderer lives in `viz::badge` and is shared with the parser page, which previews the same badges and shows their copy-paste Markdown. + ## Contentious constructs A contentious construct is one the reference engine accepts but a parser may reasonably decline to support (a niche engine quirk, a non-standard extension, a lossy or deprecated form). The benchmark keeps strict, oracle-graded recall as the headline number and adds a secondary "recall excluding contentious" beside it, plus a per-statement badge on the failures view. The design is written up in [docs/contentious-constructs.md](docs/contentious-constructs.md). diff --git a/Cargo.toml b/Cargo.toml index 2d7ada6..6de4bc9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = [".", "viz", "web", "membench", "oracle", "timemachine", "featurescan"] +members = [".", "viz", "web", "badgegen", "membench", "oracle", "timemachine", "featurescan"] default-members = ["."] resolver = "2" diff --git a/badgegen/Cargo.toml b/badgegen/Cargo.toml new file mode 100644 index 0000000..1d91048 --- /dev/null +++ b/badgegen/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "badgegen" +version = "0.1.0" +edition = "2021" +description = "Writes the static per-parser README badges into web/static/badges" +license = "MIT" +publish = false + +[lints.clippy] +all = "warn" + +[dependencies] +sql_ast_benchmark_web = { path = "../web" } diff --git a/badgegen/src/main.rs b/badgegen/src/main.rs new file mode 100644 index 0000000..0c33e77 --- /dev/null +++ b/badgegen/src/main.rs @@ -0,0 +1,27 @@ +//! Writes the static per-parser README badges into `web/static/badges/`, one SVG +//! per parser per variant, from the same scoring the website uses. Run after +//! `sqlbench export` refreshes the snapshot the `web` crate embeds. + +use sql_ast_benchmark_web::badges; +use std::{fs, path::Path}; + +fn main() -> std::io::Result<()> { + let dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("../web/static/badges"); + fs::create_dir_all(&dir)?; + for entry in fs::read_dir(&dir)? { + let path = entry?.path(); + if path.extension().is_some_and(|e| e == "svg") { + fs::remove_file(path)?; + } + } + + let mut written = 0; + for (_parser, variants) in badges::all() { + for v in variants { + fs::write(dir.join(&v.file), v.svg)?; + written += 1; + } + } + println!("wrote {written} badge SVGs to {}", dir.display()); + Ok(()) +} diff --git a/src/bin/sqlbench.rs b/src/bin/sqlbench.rs index 3b24282..b83d945 100644 --- a/src/bin/sqlbench.rs +++ b/src/bin/sqlbench.rs @@ -259,7 +259,7 @@ fn run_regen() { ], ), // web/assets/history.json.zst ]; - let total = steps.len() + 1; + let total = steps.len() + 2; for (i, (cmd, args)) in steps.iter().enumerate() { eprintln!("\n[regen {}/{total}] {cmd} {}", i + 1, args.join(" ")); let status = std::process::Command::new(cmd) @@ -274,11 +274,26 @@ fn run_regen() { std::process::exit(1); } } - eprintln!("\n[regen {total}/{total}] export"); + eprintln!("\n[regen {}/{total}] export", total - 1); if let Err(e) = export::run() { eprintln!("ERROR: {e}"); std::process::exit(1); } + + // Badges read the snapshot the web crate embeds, so regenerate them last, + // after export has rewritten it. + eprintln!("\n[regen {total}/{total}] cargo run -p badgegen"); + let status = std::process::Command::new("cargo") + .args(["run", "-p", "badgegen"]) + .status() + .unwrap_or_else(|e| { + eprintln!("ERROR: could not launch `cargo run -p badgegen`: {e}"); + std::process::exit(1); + }); + if !status.success() { + eprintln!("ERROR: step failed: `cargo run -p badgegen`"); + std::process::exit(1); + } } fn usage() -> ! { diff --git a/timemachine/src/run.rs b/timemachine/src/run.rs index 1219fbb..c1439ed 100644 --- a/timemachine/src/run.rs +++ b/timemachine/src/run.rs @@ -154,6 +154,10 @@ fn metrics_of(report: &report::DialectReport) -> ParserMetrics { } else { pct(s.accepted_valid, report.valid_total) }, + // The time machine does not classify contentious constructs, so it reports + // no excluding-contentious recall and counts none accepted. + accepted_valid_contentious: 0, + recall_excl_contentious_pct: None, // The time machine does not measure the empirical panic rate (only the // current build does, via BenchParser's panic-detecting parse_outcome), so // it is left unmeasured rather than reported as a misleading zero. diff --git a/viz/src/badge.rs b/viz/src/badge.rs new file mode 100644 index 0000000..0d4642b --- /dev/null +++ b/viz/src/badge.rs @@ -0,0 +1,94 @@ +//! Pure renderer for shields-style flat SVG badges, shared by the website and +//! the static `/badges/*.svg` generator so both emit identical output. Text is +//! sized from a Verdana width table and carries an SVG `textLength`, so the +//! browser scales each string to the computed width regardless of the font. + +/// Approximate Verdana advance width (px at font-size 11) for one character. +fn char_width(c: char) -> f64 { + match c { + ' ' | '!' | '\'' | '.' | ',' => 3.8, + '#' => 8.9, + '$' | '0'..='9' => 7.0, + '(' | ')' | '-' | '/' | ':' => 4.6, + 'i' | 'j' | 'l' => 3.0, + 'f' | 't' | 'r' => 4.4, + 'm' => 10.6, + 'w' => 9.0, + 'M' | 'W' => 10.6, + 'I' | 'J' => 4.6, + 'a'..='z' => 6.6, + 'A'..='Z' => 8.2, + _ => 6.8, + } +} + +fn text_width(s: &str) -> f64 { + s.chars().map(char_width).sum() +} + +fn esc(s: &str) -> String { + s.replace('&', "&") + .replace('<', "<") + .replace('>', ">") + .replace('"', """) +} + +/// A flat badge: dark grey `label`, then `message` filled with `color` (e.g. +/// `#4c1`). Returns a self-contained 20px-tall SVG. +#[must_use] +pub fn render(label: &str, message: &str, color: &str) -> String { + let lw = (text_width(label) + 10.0).round() as i64; + let mw = (text_width(message) + 10.0).round() as i64; + let total = lw + mw; + let lcx = lw * 5; + let mcx = lw * 10 + mw * 5; + let ltl = (text_width(label) * 10.0).round() as i64; + let mtl = (text_width(message) * 10.0).round() as i64; + let (l, m, c) = (esc(label), esc(message), esc(color)); + format!( + "{l}: {m}\ +\ +\ +\ +\ +\ +\ +\ +{l}\ +{l}\ +{m}\ +{m}" + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn render_contains_both_segments() { + let svg = render("sql ast benchmark", "#1 SQLite", "#4c1"); + assert!(svg.starts_with("")); + assert!(svg.contains("sql ast benchmark") && svg.contains("#1 SQLite")); + assert!(svg.contains("fill=\"#4c1\"")); + } + + #[test] + fn wider_message_yields_wider_badge() { + let w = |s: &str| { + let a = s.find("width=\"").unwrap() + 7; + s[a..a + s[a..].find('"').unwrap()].parse::().unwrap() + }; + assert!( + w(&render("bench", "#1 of 99 parsers", "#4c1")) > w(&render("bench", "#1", "#4c1")) + ); + } + + #[test] + fn escapes_xml() { + let svg = render("a&b", "x", "#4c1"); + assert!(svg.contains("a&b") && svg.contains("x<y>")); + } +} diff --git a/viz/src/lib.rs b/viz/src/lib.rs index 3569316..541d152 100644 --- a/viz/src/lib.rs +++ b/viz/src/lib.rs @@ -7,6 +7,7 @@ //! so no font rasterization or fontconfig is needed). It deliberately depends on //! nothing but `serde` and `plotters` so it stays wasm-clean. +pub mod badge; pub mod chart; pub mod color; pub mod marker; diff --git a/web/assets/main.css b/web/assets/main.css index c7079de..7ab6d6a 100644 --- a/web/assets/main.css +++ b/web/assets/main.css @@ -514,6 +514,26 @@ main { display: block; } .report-propose:hover { color: #b06e1f; border-color: #e3c08a; background: #fdf7ee; } .report-dispute { color: #1c4e80; } .report-dispute:hover { color: #1c4e80; border-color: #a9c6e6; background: #eef4fb; } +/* README badges on the parser page: an inline SVG preview, the copy-paste + Markdown, and a copy button, one row per variant. */ +.badge-list { display: flex; flex-direction: column; gap: 0.5rem; } +.badge-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; } +.badge-preview { flex: none; display: inline-flex; } +.badge-preview svg { display: block; height: 20px; } +.badge-md { + flex: 1 1 16rem; + min-width: 0; + padding: 0.4rem 0.6rem; + border: 1px solid var(--line); + border-radius: 8px; + background: #fbfbfb; + color: #1f2328; + font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; + font-size: 0.78rem; + overflow-x: auto; + white-space: pre; +} +.badge-row .copy-btn { flex: none; } /* The parser's error message for a rejected statement, under its preview. */ .fail-reason { margin: 0.2rem 0 0; diff --git a/web/src/badges.rs b/web/src/badges.rs new file mode 100644 index 0000000..c6cad3d --- /dev/null +++ b/web/src/badges.rs @@ -0,0 +1,119 @@ +//! Per-parser README badges, in two variants (overall rank and composite score). +//! Used by the parser page (inline previews and copy snippets) and by the +//! `badgegen` crate that writes the static `/badges/*.svg`. + +use crate::components::slug; +use crate::data::bundle; +use crate::score::{parser_score, rank}; + +/// Left-hand label, shared by every badge. +pub const LABEL: &str = "sql ast benchmark"; +/// Deployed site root, used to build the embed and link URLs. +pub const BASE: &str = "https://sql-ast-benchmark.luca.phd"; + +/// One renderable badge for a parser. +pub struct Variant { + /// Stable id, also the file suffix: `dialect`, `overall`, or `score`. + pub id: &'static str, + /// Static file name under `/badges/`, e.g. `sqlparser_rs-overall.svg`. + pub file: String, + /// Right-hand text, e.g. `#1 SQLite`. + pub message: String, + /// Fill color for the message segment. + pub color: &'static str, + /// Self-contained SVG, identical to the committed static file. + pub svg: String, + /// Copy-paste Markdown linking the badge to the parser page. + pub markdown: String, +} + +/// Shields palette, brightest (best) to warmest (worst). +const PALETTE: [&str; 5] = ["#4c1", "#97ca00", "#a4a61d", "#dfb317", "#fe7d37"]; + +/// Color for a 1-based rank within a field: first place is brightest. +fn rank_color(rank: usize, total: usize) -> &'static str { + if rank <= 1 { + return PALETTE[0]; + } + let f = (rank - 1) as f64 / total.saturating_sub(1).max(1) as f64; + PALETTE[((f * 4.0).ceil() as usize).clamp(1, 4)] +} + +/// Color for a 0 to 100 composite score. +fn score_color(v: f64) -> &'static str { + match v { + x if x >= 85.0 => PALETTE[0], + x if x >= 70.0 => PALETTE[1], + x if x >= 55.0 => PALETTE[2], + x if x >= 40.0 => PALETTE[3], + _ => PALETTE[4], + } +} + +/// Build one variant from its parts. +fn variant(parser: &str, id: &'static str, message: String, color: &'static str) -> Variant { + let s = slug(parser); + Variant { + id, + file: format!("{s}-{id}.svg"), + markdown: format!("[![{LABEL}]({BASE}/badges/{s}-{id}.svg)]({BASE}/parser/{s})"), + svg: viz::badge::render(LABEL, &message, color), + message, + color, + } +} + +/// The badge variants available for a parser, in display order. +#[must_use] +pub fn variants(parser: &str) -> Vec { + let mut out = Vec::new(); + if let Some((r, n)) = rank(parser, |s| Some(s.overall)) { + out.push(variant( + parser, + "overall", + format!("#{r} of {n}"), + rank_color(r, n), + )); + } + if let Some(s) = parser_score(parser) { + out.push(variant( + parser, + "score", + format!("{:.0}/100", s.overall), + score_color(s.overall), + )); + } + out +} + +/// Every parser paired with its badge variants, for the static generator. +#[must_use] +pub fn all() -> Vec<(String, Vec)> { + bundle() + .parsers + .iter() + .map(|p| (p.clone(), variants(p))) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn every_parser_has_overall_and_score_variants() { + for p in &bundle().parsers { + let v = variants(p); + assert_eq!(v.len(), 2, "{p} should have overall and score badges"); + assert!(v.iter().all(|x| x.svg.starts_with(" Element { } /// URL-safe slug for a parser display name (matches the route param). -fn slug(s: &str) -> String { +pub(crate) fn slug(s: &str) -> String { let mut out = String::with_capacity(s.len()); let mut prev_us = false; for ch in s.chars() { @@ -740,6 +740,8 @@ pub fn ParserView(name: String) -> Element { {parser_score_section(&parser)} + {parser_badges_section(&parser)} + if has_charts { section { class: "block", h2 { @@ -1757,6 +1759,55 @@ fn parser_score_section(parser: &str) -> Element { } } +/// README badges for this parser: an inline preview of each variant next to the +/// copy-paste Markdown that embeds it and links back to this page. +fn parser_badges_section(parser: &str) -> Element { + let pslug = slug(parser); + // Precompute (svg, dom id, markdown, copy script) so the rsx loop stays flat. + let items: Vec<(String, String, String, String)> = crate::badges::variants(parser) + .into_iter() + .map(|v| { + let dom_id = format!("badge-md-{pslug}-{}", v.id); + let js = format!( + "{{ const el = document.getElementById('{dom_id}'); if (el) {{ navigator.clipboard.writeText(el.textContent); }} }}" + ); + (v.svg, dom_id, v.markdown, js) + }) + .collect(); + if items.is_empty() { + return rsx! {}; + } + rsx! { + section { class: "block", + h2 { + Icon { width: 17, height: 17, fill: "currentColor".to_string(), class: "h2-ico".to_string(), icon: FaTag } + "Badges" + } + p { class: "table-cap", + "Show this parser's standing in your README: copy a variant below and the badge refreshes each time the benchmark is republished. The benchmark is open to suggestions and exists to share which parser ideas work best and how they evolve." + } + div { class: "badge-list", + for (vi , (svg , dom_id , md , js)) in items.into_iter().enumerate() { + div { class: "badge-row", key: "{vi}", + span { class: "badge-preview", "aria-hidden": "true", dangerous_inner_html: "{svg}" } + code { id: "{dom_id}", class: "badge-md", "{md}" } + button { + class: "copy-btn", + r#type: "button", + aria_label: "Copy the Markdown for this badge", + title: "Copy Markdown", + onclick: move |_| { + document::eval(&js); + }, + Icon { width: 13, height: 13, fill: "currentColor".to_string(), icon: FaCopy } + } + } + } + } + } + } +} + /// Format a sub-score as a rounded number, or "n/a" when it does not apply. fn fmt_score(v: Option) -> String { v.map_or_else(|| "n/a".to_string(), |x| format!("{x:.0}")) diff --git a/web/src/lib.rs b/web/src/lib.rs new file mode 100644 index 0000000..5ada9f0 --- /dev/null +++ b/web/src/lib.rs @@ -0,0 +1,57 @@ +//! Dioxus -> WASM viewer for the sql_ast_benchmark results. Reads the committed +//! `assets/bench.json.zst` snapshot and renders interactive per-dialect pages. +//! Exposed as a library so the badge generator can reuse the scoring and +//! metadata; `main.rs` is the thin wasm entry point. + +use dioxus::prelude::*; +use manganis::AssetOptions; + +pub mod badges; +pub mod brand; +pub mod cadence; +pub mod components; +pub mod data; +pub mod descriptions; +pub mod dialect_meta; +pub mod logos; +pub mod metadata; +pub mod score; + +use components::{DialectView, Overview, ParserView, Shell}; + +/// The site stylesheet, emitted into the static `index.html` `` at build +/// time so it is present on first paint (avoids a flash of unstyled content). +const MAIN_CSS: Asset = asset!( + "/assets/main.css", + AssetOptions::css().with_static_head(true) +); + +/// The site favicon: an abstract-syntax-tree mark in the accent color. +pub const FAVICON: Asset = asset!("/assets/favicon.svg"); + +/// Client-side routes. `Shell` wraps every page with the shared chrome. +#[derive(Clone, PartialEq, Routable)] +pub enum Route { + #[layout(Shell)] + #[route("/")] + Overview {}, + #[route("/dialect/:dir")] + DialectView { dir: String }, + #[route("/parser/:name")] + ParserView { name: String }, +} + +/// Launch the viewer (called from the wasm entry point). +pub fn launch() { + dioxus::launch(App); +} + +#[component] +fn App() -> Element { + let _ = MAIN_CSS; + rsx! { + document::Link { rel: "icon", r#type: "image/svg+xml", href: FAVICON } + document::Link { rel: "icon", r#type: "image/x-icon", href: "/favicon.ico" } + Router:: {} + } +} diff --git a/web/src/main.rs b/web/src/main.rs index 7366c74..cc03ac9 100644 --- a/web/src/main.rs +++ b/web/src/main.rs @@ -1,62 +1,6 @@ -//! Dioxus -> WASM viewer for the sql_ast_benchmark results. Reads the committed -//! `assets/bench.json.zst` snapshot and renders interactive per-dialect pages: the -//! eCDF and box charts (rendered in-browser by `viz` via plotters) plus the -//! correctness, perf, and per-file coverage tables. - -use dioxus::prelude::*; -use manganis::AssetOptions; - -mod brand; -mod cadence; -mod components; -mod data; -mod descriptions; -mod dialect_meta; -mod logos; -mod metadata; -mod score; - -use components::{DialectView, Overview, ParserView, Shell}; - -/// The site stylesheet, emitted into the static `index.html` `` at build -/// time (`with_static_head`) so it is present on first paint. Injecting it via -/// `document::Stylesheet` from a component instead links it only after wasm -/// boots and first-renders, which flashes unstyled content (FOUC). -const MAIN_CSS: Asset = asset!( - "/assets/main.css", - AssetOptions::css().with_static_head(true) -); - -/// The site favicon: an abstract-syntax-tree mark in the accent color. -pub const FAVICON: Asset = asset!("/assets/favicon.svg"); - -/// Client-side routes. Variant names map to the components of the same name. -/// `Shell` wraps every page with the shared header, skip link, and footer. -#[derive(Clone, PartialEq, Routable)] -enum Route { - #[layout(Shell)] - #[route("/")] - Overview {}, - #[route("/dialect/:dir")] - DialectView { dir: String }, - #[route("/parser/:name")] - ParserView { name: String }, -} +//! Thin wasm entry point. The viewer lives in the library crate so the badge +//! generator can reuse its scoring and metadata. fn main() { - dioxus::launch(App); -} - -#[component] -fn App() -> Element { - // MAIN_CSS is force-referenced so the linker keeps the asset. The actual - // is emitted into the static via with_static_head above. - let _ = MAIN_CSS; - rsx! { - // SVG icon for modern browsers, plus the root .ico (copied to the site - // root by the Pages workflow) for the /favicon.ico browsers auto-probe. - document::Link { rel: "icon", r#type: "image/svg+xml", href: FAVICON } - document::Link { rel: "icon", r#type: "image/x-icon", href: "/favicon.ico" } - Router:: {} - } + sql_ast_benchmark_web::launch(); } diff --git a/web/static/badges/databend_common_ast-overall.svg b/web/static/badges/databend_common_ast-overall.svg new file mode 100644 index 0000000..8a1834c --- /dev/null +++ b/web/static/badges/databend_common_ast-overall.svg @@ -0,0 +1 @@ +sql ast benchmark: #9 of 10sql ast benchmark#9 of 10 \ No newline at end of file diff --git a/web/static/badges/databend_common_ast-score.svg b/web/static/badges/databend_common_ast-score.svg new file mode 100644 index 0000000..35da936 --- /dev/null +++ b/web/static/badges/databend_common_ast-score.svg @@ -0,0 +1 @@ +sql ast benchmark: 60/100sql ast benchmark60/100 \ No newline at end of file diff --git a/web/static/badges/orql-overall.svg b/web/static/badges/orql-overall.svg new file mode 100644 index 0000000..a97187f --- /dev/null +++ b/web/static/badges/orql-overall.svg @@ -0,0 +1 @@ +sql ast benchmark: #10 of 10sql ast benchmark#10 of 10 \ No newline at end of file diff --git a/web/static/badges/orql-score.svg b/web/static/badges/orql-score.svg new file mode 100644 index 0000000..9673eec --- /dev/null +++ b/web/static/badges/orql-score.svg @@ -0,0 +1 @@ +sql ast benchmark: 49/100sql ast benchmark49/100 \ No newline at end of file diff --git a/web/static/badges/pg_query_rs-overall.svg b/web/static/badges/pg_query_rs-overall.svg new file mode 100644 index 0000000..ae56958 --- /dev/null +++ b/web/static/badges/pg_query_rs-overall.svg @@ -0,0 +1 @@ +sql ast benchmark: #7 of 10sql ast benchmark#7 of 10 \ No newline at end of file diff --git a/web/static/badges/pg_query_rs-score.svg b/web/static/badges/pg_query_rs-score.svg new file mode 100644 index 0000000..194bac0 --- /dev/null +++ b/web/static/badges/pg_query_rs-score.svg @@ -0,0 +1 @@ +sql ast benchmark: 71/100sql ast benchmark71/100 \ No newline at end of file diff --git a/web/static/badges/pg_query_summary-overall.svg b/web/static/badges/pg_query_summary-overall.svg new file mode 100644 index 0000000..e52d79a --- /dev/null +++ b/web/static/badges/pg_query_summary-overall.svg @@ -0,0 +1 @@ +sql ast benchmark: #3 of 10sql ast benchmark#3 of 10 \ No newline at end of file diff --git a/web/static/badges/pg_query_summary-score.svg b/web/static/badges/pg_query_summary-score.svg new file mode 100644 index 0000000..1bb078b --- /dev/null +++ b/web/static/badges/pg_query_summary-score.svg @@ -0,0 +1 @@ +sql ast benchmark: 79/100sql ast benchmark79/100 \ No newline at end of file diff --git a/web/static/badges/polyglot_sql-overall.svg b/web/static/badges/polyglot_sql-overall.svg new file mode 100644 index 0000000..39e75f4 --- /dev/null +++ b/web/static/badges/polyglot_sql-overall.svg @@ -0,0 +1 @@ +sql ast benchmark: #8 of 10sql ast benchmark#8 of 10 \ No newline at end of file diff --git a/web/static/badges/polyglot_sql-score.svg b/web/static/badges/polyglot_sql-score.svg new file mode 100644 index 0000000..35da936 --- /dev/null +++ b/web/static/badges/polyglot_sql-score.svg @@ -0,0 +1 @@ +sql ast benchmark: 60/100sql ast benchmark60/100 \ No newline at end of file diff --git a/web/static/badges/qusql_parse-overall.svg b/web/static/badges/qusql_parse-overall.svg new file mode 100644 index 0000000..ef91849 --- /dev/null +++ b/web/static/badges/qusql_parse-overall.svg @@ -0,0 +1 @@ +sql ast benchmark: #1 of 10sql ast benchmark#1 of 10 \ No newline at end of file diff --git a/web/static/badges/qusql_parse-score.svg b/web/static/badges/qusql_parse-score.svg new file mode 100644 index 0000000..0097586 --- /dev/null +++ b/web/static/badges/qusql_parse-score.svg @@ -0,0 +1 @@ +sql ast benchmark: 82/100sql ast benchmark82/100 \ No newline at end of file diff --git a/web/static/badges/sqlglot_rust-overall.svg b/web/static/badges/sqlglot_rust-overall.svg new file mode 100644 index 0000000..932fcf4 --- /dev/null +++ b/web/static/badges/sqlglot_rust-overall.svg @@ -0,0 +1 @@ +sql ast benchmark: #5 of 10sql ast benchmark#5 of 10 \ No newline at end of file diff --git a/web/static/badges/sqlglot_rust-score.svg b/web/static/badges/sqlglot_rust-score.svg new file mode 100644 index 0000000..f184f96 --- /dev/null +++ b/web/static/badges/sqlglot_rust-score.svg @@ -0,0 +1 @@ +sql ast benchmark: 78/100sql ast benchmark78/100 \ No newline at end of file diff --git a/web/static/badges/sqlite3_parser-overall.svg b/web/static/badges/sqlite3_parser-overall.svg new file mode 100644 index 0000000..612fa89 --- /dev/null +++ b/web/static/badges/sqlite3_parser-overall.svg @@ -0,0 +1 @@ +sql ast benchmark: #4 of 10sql ast benchmark#4 of 10 \ No newline at end of file diff --git a/web/static/badges/sqlite3_parser-score.svg b/web/static/badges/sqlite3_parser-score.svg new file mode 100644 index 0000000..f184f96 --- /dev/null +++ b/web/static/badges/sqlite3_parser-score.svg @@ -0,0 +1 @@ +sql ast benchmark: 78/100sql ast benchmark78/100 \ No newline at end of file diff --git a/web/static/badges/sqlparser_rs-overall.svg b/web/static/badges/sqlparser_rs-overall.svg new file mode 100644 index 0000000..7d0b603 --- /dev/null +++ b/web/static/badges/sqlparser_rs-overall.svg @@ -0,0 +1 @@ +sql ast benchmark: #6 of 10sql ast benchmark#6 of 10 \ No newline at end of file diff --git a/web/static/badges/sqlparser_rs-score.svg b/web/static/badges/sqlparser_rs-score.svg new file mode 100644 index 0000000..1b7de68 --- /dev/null +++ b/web/static/badges/sqlparser_rs-score.svg @@ -0,0 +1 @@ +sql ast benchmark: 76/100sql ast benchmark76/100 \ No newline at end of file diff --git a/web/static/badges/turso_parser-overall.svg b/web/static/badges/turso_parser-overall.svg new file mode 100644 index 0000000..a1527bd --- /dev/null +++ b/web/static/badges/turso_parser-overall.svg @@ -0,0 +1 @@ +sql ast benchmark: #2 of 10sql ast benchmark#2 of 10 \ No newline at end of file diff --git a/web/static/badges/turso_parser-score.svg b/web/static/badges/turso_parser-score.svg new file mode 100644 index 0000000..7b46378 --- /dev/null +++ b/web/static/badges/turso_parser-score.svg @@ -0,0 +1 @@ +sql ast benchmark: 80/100sql ast benchmark80/100 \ No newline at end of file