From a6454fb2892bd9522f417e7ee4f6ad7d50bacb09 Mon Sep 17 00:00:00 2001
From: Michiel De Muynck
Date: Sun, 10 Nov 2024 20:58:21 +0100
Subject: [PATCH] Refactor ui into components
---
README.md | 9 ++
rust-toolchain.toml | 2 +
src/data.rs | 66 +++++++++
src/main.rs | 281 +--------------------------------------
src/ui/common.rs | 85 ++++++++++++
src/{ => ui}/markdown.rs | 0
src/ui/mod.rs | 24 ++++
src/ui/support_matrix.rs | 159 ++++++++++++++++++++++
8 files changed, 351 insertions(+), 275 deletions(-)
create mode 100644 rust-toolchain.toml
create mode 100644 src/data.rs
create mode 100644 src/ui/common.rs
rename src/{ => ui}/markdown.rs (100%)
create mode 100644 src/ui/mod.rs
create mode 100644 src/ui/support_matrix.rs
diff --git a/README.md b/README.md
index 5c991e7..892f0f7 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,16 @@
To visit the site, go to [https://datamindedbe.github.io/playground-engine-query/](https://datamindedbe.github.io/playground-engine-query/).
Feel free to contribute by making a PR:
+
* Info about query engines is in [query_engines.yaml](https://github.com/datamindedbe/playground-engine-query/blob/main/query_engines.yaml)
* Info about integrations, i.e., places to read or write to, is in [integrations.yaml](https://github.com/datamindedbe/playground-engine-query/blob/main/query_engines.yaml)
* Which query engines support which integrations is in [support_matrix.yaml](https://github.com/datamindedbe/playground-engine-query/blob/main/support_matrix.yaml)
* The site itself is Rust code, using the Leptos framework.
+
+## Running locally
+
+To run this locally,
+
+* Install Rust nightly (see https://rustup.rs/)
+* Install Trunk (`cargo install trunk`)
+* Run `trunk serve`
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
new file mode 100644
index 0000000..271800c
--- /dev/null
+++ b/rust-toolchain.toml
@@ -0,0 +1,2 @@
+[toolchain]
+channel = "nightly"
\ No newline at end of file
diff --git a/src/data.rs b/src/data.rs
new file mode 100644
index 0000000..b5e6812
--- /dev/null
+++ b/src/data.rs
@@ -0,0 +1,66 @@
+//! This file contains all logic related to parsing the YAML files
+//! and querying the resulting data structures.
+
+use std::collections::HashMap;
+use serde::Deserialize;
+
+#[derive(Debug, Deserialize, Clone)]
+pub struct QueryEngine {
+ pub id: String,
+ pub short_name: String,
+ pub description: String,
+ pub url: String,
+ pub logo: String
+}
+
+#[derive(Debug, Deserialize, Clone)]
+pub struct Integration {
+ pub id: String,
+ pub short_name: String,
+ pub description: String,
+ pub logo: String
+}
+
+#[derive(Debug, Deserialize, Clone)]
+pub struct Feature {
+ pub supported: bool,
+ pub evidence: String,
+ pub caveats: Option
+}
+
+#[derive(Debug, Deserialize, Clone)]
+pub struct IntegrationSupport {
+ pub import: Feature,
+ pub export: Feature,
+}
+
+#[derive(Debug, Clone)]
+pub struct Data {
+ pub engines: Vec,
+ pub integrations: Vec,
+ pub support_matrix: HashMap>
+}
+
+impl Data {
+ pub fn get_support(&self, engine: &str, integration: &str) -> Option<&IntegrationSupport> {
+ self.support_matrix.get(engine)?.get(integration)
+ }
+}
+
+const ENGINES_YAML: &str = include_str!("../query_engines.yaml");
+const INTEGRATIONS_YAML: &str = include_str!("../integrations.yaml");
+const SUPPORT_MATRIX_YAML: &str = include_str!("../support_matrix.yaml");
+
+/**
+ * Get the data parsed from the YAML files included statically in the binary.
+ * Panics if the YAML files are not parsable.
+ */
+pub fn parse_data_from_static_yamls() -> &'static Data {
+ static STATIC_DATA: std::sync::OnceLock = std::sync::OnceLock::new();
+ STATIC_DATA.get_or_init(|| {
+ let engines: Vec = serde_yaml::from_str(ENGINES_YAML).expect("Unable to parse YAML");
+ let integrations: Vec = serde_yaml::from_str(INTEGRATIONS_YAML).expect("Unable to parse YAML");
+ let support_matrix: HashMap> = serde_yaml::from_str(SUPPORT_MATRIX_YAML).expect("Unable to parse YAML");
+ Data { engines, integrations, support_matrix }
+ })
+}
diff --git a/src/main.rs b/src/main.rs
index 80681af..ef2b4ee 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,283 +1,14 @@
-mod markdown;
+#![allow(dead_code)]
-use std::{collections::HashMap, panic};
+mod data;
+mod ui;
-use leptos::*;
-use markdown::Markdown;
-use serde::Deserialize;
-
-#[derive(Debug, Deserialize, Clone)]
-struct QueryEngine {
- id: String,
- short_name: String,
- description: String,
- url: String,
- logo: String
-}
-
-#[derive(Debug, Deserialize, Clone)]
-struct Integration {
- id: String,
- short_name: String,
- description: String,
- logo: String
-}
-
-#[derive(Debug, Deserialize, Clone)]
-struct Feature {
- supported: bool,
- evidence: String,
- caveats: Option
-}
-
-#[derive(Debug, Deserialize, Clone)]
-struct IntegrationSupport {
- import: Feature,
- export: Feature,
-}
-
-#[derive(Debug, Clone)]
-struct ClickedCell {
- query_engine: QueryEngine,
- integration: Integration,
- mouse_x: i32,
- mouse_y: i32
-}
-
-const ENGINES: &str = include_str!("../query_engines.yaml");
-const INTEGRATIONS: &str = include_str!("../integrations.yaml");
-const SUPPORT_MATRIX: &str = include_str!("../support_matrix.yaml");
+use std::panic;
fn main() {
panic::set_hook(Box::new(console_error_panic_hook::hook));
- let query_engines: Vec =
- serde_yaml::from_str(ENGINES).expect("Unable to parse YAML");
-
- let integrations: Vec =
- serde_yaml::from_str(INTEGRATIONS).expect("Unable to parse YAML");
-
- let support_matrix: HashMap> =
- serde_yaml::from_str(SUPPORT_MATRIX).expect("Failed to parse YAML");
-
- let (
- clicked_on_cell,
- set_clicked_on_cell
- ) = leptos::create_signal(None);
-
- let (support_matrix, _) = leptos::create_signal(support_matrix);
- let (filtered_integrations, _set_filtered_integrations) = leptos::create_signal(integrations.clone());
- let (filtered_query_engines, _set_filtered_query_engines) = leptos::create_signal(query_engines.clone());
+ let data = data::parse_data_from_static_yamls();
- mount_to_body(move || {
- view! {
-
-
"Engine Query"
-
- Answer all your queries about query engines.
-
-
-
-
- |
-
-
-
-
-
- {&integration.short_name}
-
-
- |
-
-
-
-
-
-
-
-
- {&qe.short_name}
- |
-
-
- {
- let support = support_matrix.get().get(&qe.id).and_then(|qe_support_map| qe_support_map.get(&integration.id)).cloned();
- let support_text = if let Some(support) = support {
- let support_text = match (support.import.supported, support.export.supported) {
- (true, true) => "ā
",
- (true, false) => "š",
- (false, true) => "āļø",
- (false, false) => "ā",
- };
- let has_caveats = support.import.caveats.is_some() || support.export.caveats.is_some();
- let caveats_text = if has_caveats {
- "*"
- } else {
- ""
- };
- format!("{}{}", support_text, caveats_text)
- } else {
- "ā".to_string()
- };
- view! {
- { support_text }
- }
- }
- |
-
-
-
-
-
-
- Legend:
-
- - "ā
= Can read & write"
- - "š = Can read but not write"
- - "āļø = Can write but not read"
- - "ā = Not supported"
- - "ā = Unknown (""please contribute!"")"
-
-
- {
- move || {
- if let Some(c) = clicked_on_cell.get() {
- let support = support_matrix.get().get(&c.query_engine.id).and_then(|qe_support_map| qe_support_map.get(&c.integration.id)).cloned();
- view! {
-
- }
- } else {
- view! {
-
- }
- }
- }
- }
-
- }
- })
+ ui::mount_ui_to_body(&data);
}
diff --git a/src/ui/common.rs b/src/ui/common.rs
new file mode 100644
index 0000000..f54417e
--- /dev/null
+++ b/src/ui/common.rs
@@ -0,0 +1,85 @@
+//! This file contains several common components reused throughout the UI.
+
+use leptos::*;
+
+use crate::data::{QueryEngine, Integration, IntegrationSupport};
+use crate::ui::markdown::Markdown;
+
+#[component]
+pub fn QueryEngineName<'qe>(qe: &'qe QueryEngine) -> impl IntoView {
+ view! {
+
+ {qe.short_name.clone()}
+ }
+}
+
+#[component]
+pub fn IntegrationName<'i>(integration: &'i Integration) -> impl IntoView {
+ view! {
+
+ {integration.short_name.clone()}
+ }
+}
+
+#[component]
+pub fn PleaseContributeLink() -> impl IntoView {
+ view! {
+ "please contribute!"
+ }
+}
+
+#[component]
+pub fn IntegrationDetails<'a>(
+ query_engine: &'a QueryEngine,
+ integration: &'a Integration,
+ support: Option<&'a IntegrationSupport>,
+ read: bool,
+) -> impl IntoView {
+ let read_or_write_str = if read { "read" } else { "write" };
+ let to_or_from_str = if read { "from" } else { "to" };
+ view! {
+
+
+ {
+ if let Some(support) = support {
+ let feature = if read { &support.import } else { &support.export };
+ let evidence_str = if let Some(caveats) = &feature.caveats {
+ format!("{}\n\nā ļø {}", feature.evidence, caveats)
+ } else {
+ feature.evidence.clone()
+ };
+ if feature.supported {
+ view! {
+
+ }
+ } else {
+ view! {
+
+ }
+ }
+ } else {
+ view! {
+
+ }
+ }
+ }
+
+ }
+}
diff --git a/src/markdown.rs b/src/ui/markdown.rs
similarity index 100%
rename from src/markdown.rs
rename to src/ui/markdown.rs
diff --git a/src/ui/mod.rs b/src/ui/mod.rs
new file mode 100644
index 0000000..7d9bf38
--- /dev/null
+++ b/src/ui/mod.rs
@@ -0,0 +1,24 @@
+mod markdown;
+mod support_matrix;
+mod common;
+
+use leptos::*;
+use support_matrix::SupportMatrix;
+use crate::data::Data;
+
+/**
+ * The main function of the UI, draws the UI and mounts it to the HTML body.
+ */
+pub fn mount_ui_to_body(data: &'static Data) {
+ mount_to_body(move || {
+ view! {
+
+
"Engine Query"
+
+ Answer all your queries about query engines.
+
+
+
+ }
+ })
+}
\ No newline at end of file
diff --git a/src/ui/support_matrix.rs b/src/ui/support_matrix.rs
new file mode 100644
index 0000000..aa23c14
--- /dev/null
+++ b/src/ui/support_matrix.rs
@@ -0,0 +1,159 @@
+use leptos::*;
+
+use crate::data::{Data, Integration, QueryEngine};
+use crate::ui::common::{
+ QueryEngineName,
+ PleaseContributeLink,
+ IntegrationDetails,
+};
+
+#[derive(Debug, Clone)]
+struct ClickedCell {
+ query_engine: QueryEngine,
+ integration: Integration,
+ mouse_x: i32,
+ mouse_y: i32
+}
+
+#[component]
+pub fn SupportMatrix(
+ data: &'static Data
+) -> impl IntoView {
+ let (filtered_integrations, _set_filtered_integrations) = leptos::create_signal(data.integrations.clone());
+ let (filtered_query_engines, _set_filtered_query_engines) = leptos::create_signal(data.engines.clone());
+
+ let (
+ clicked_on_cell,
+ set_clicked_on_cell
+ ) = leptos::create_signal(None);
+ view! {
+
+
+
+ |
+
+
+
+
+
+ {&integration.short_name}
+
+
+ |
+
+
+
+
+
+
+
+
+ |
+
+
+ {
+ let support = data.get_support(&qe.id, &integration.id);
+ let support_text = if let Some(support) = support {
+ let support_text = match (support.import.supported, support.export.supported) {
+ (true, true) => "ā
",
+ (true, false) => "š",
+ (false, true) => "āļø",
+ (false, false) => "ā",
+ };
+ let has_caveats = support.import.caveats.is_some() || support.export.caveats.is_some();
+ let caveats_text = if has_caveats {
+ "*"
+ } else {
+ ""
+ };
+ format!("{}{}", support_text, caveats_text)
+ } else {
+ "ā".to_string()
+ };
+ view! {
+ { support_text }
+ }
+ }
+ |
+
+
+
+
+
+
+ Legend:
+
+ - "ā
= Can read & write"
+ - "š = Can read but not write"
+ - "āļø = Can write but not read"
+ - "ā = Not supported"
+ - "ā = Unknown ("")"
+
+
+ {
+ move || {
+ if let Some(c) = clicked_on_cell.get() {
+ let support = data.get_support(&c.query_engine.id, &c.integration.id);
+ view! {
+
+ }
+ } else {
+ view! {
+
+ }
+ }
+ }
+ }
+ }
+}