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! { -
- - -

- { - if let Some(support) = &support { - let evidence_str = if let Some(caveats) = &support.import.caveats { - format!("{}\n\nāš ļø {}", support.import.evidence, caveats) - } else { - support.import.evidence.clone() - }; - if support.import.supported { - view! { -

-

"Yes."

-

-
- } - } else { - view! { -
-

"No."

-

-
- } - } - } else { - view! { -
-

"Unknown (""please contribute!"")"

-
- } - } - } -

- -

- { - if let Some(support) = &support { - let evidence_str = if let Some(caveats) = &support.export.caveats { - format!("{}\n\nāš ļø {}", support.export.evidence, caveats) - } else { - support.export.evidence.clone() - }; - if support.export.supported { - view! { -

-

"Yes."

-

-
- } - } else { - view! { -
-

"No."

-

-
- } - } - } else { - view! { -
-

"Unknown (""please contribute!"")"

-
- } - } - } -

-
- } - } 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! { +

+

"Yes."

+

+
+ } + } else { + view! { +
+

"No."

+

+
+ } + } + } else { + view! { +
+

"Unknown ("")"

+
+ } + } + } +

+ } +} 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! { +
+ } + } + } + } + } +}