From 75e5945d2a48e3e8944299af9b8d382d14257851 Mon Sep 17 00:00:00 2001 From: Montana Low Date: Mon, 8 Jan 2024 21:10:48 -0800 Subject: [PATCH] fmt --- pgml-dashboard/build.rs | 8 +- pgml-dashboard/rustfmt.toml | 1 + pgml-dashboard/src/api/chatbot.rs | 142 +++++++------- pgml-dashboard/src/api/cms.rs | 143 +++++++++----- pgml-dashboard/src/components/chatbot/mod.rs | 19 +- pgml-dashboard/src/components/dropdown/mod.rs | 5 +- .../src/components/inputs/select/mod.rs | 6 +- .../src/components/layouts/head/mod.rs | 6 + .../src/components/layouts/head/template.html | 6 +- .../src/components/postgres_logo/mod.rs | 4 +- pgml-dashboard/src/components/star/mod.rs | 10 +- .../stimulus/stimulus_target/mod.rs | 8 +- pgml-dashboard/src/fairings.rs | 4 +- pgml-dashboard/src/guards.rs | 101 +++------- pgml-dashboard/src/lib.rs | 171 ++++------------ pgml-dashboard/src/main.rs | 26 +-- pgml-dashboard/src/models.rs | 61 ++---- pgml-dashboard/src/responses.rs | 6 +- pgml-dashboard/src/templates/mod.rs | 14 +- pgml-dashboard/src/utils/config.rs | 3 +- pgml-dashboard/src/utils/cookies.rs | 5 +- pgml-dashboard/src/utils/datadog.rs | 6 +- pgml-dashboard/src/utils/markdown.rs | 185 ++++++++---------- pgml-dashboard/src/utils/tabs.rs | 18 +- 24 files changed, 362 insertions(+), 596 deletions(-) create mode 100644 pgml-dashboard/rustfmt.toml diff --git a/pgml-dashboard/build.rs b/pgml-dashboard/build.rs index 236a78d8b..89143fd57 100644 --- a/pgml-dashboard/build.rs +++ b/pgml-dashboard/build.rs @@ -4,10 +4,7 @@ use std::process::Command; fn main() { println!("cargo:rerun-if-changed=migrations"); - let output = Command::new("git") - .args(["rev-parse", "HEAD"]) - .output() - .unwrap(); + let output = Command::new("git").args(["rev-parse", "HEAD"]).output().unwrap(); let git_hash = String::from_utf8(output.stdout).unwrap(); println!("cargo:rustc-env=GIT_SHA={}", git_hash); @@ -28,8 +25,7 @@ fn main() { } } - let css_version = - read_to_string("static/css/.pgml-bundle").expect("failed to read .pgml-bundle"); + let css_version = read_to_string("static/css/.pgml-bundle").expect("failed to read .pgml-bundle"); let css_version = css_version.trim(); let js_version = read_to_string("static/js/.pgml-bundle").expect("failed to read .pgml-bundle"); diff --git a/pgml-dashboard/rustfmt.toml b/pgml-dashboard/rustfmt.toml new file mode 100644 index 000000000..94ac875fa --- /dev/null +++ b/pgml-dashboard/rustfmt.toml @@ -0,0 +1 @@ +max_width=120 diff --git a/pgml-dashboard/src/api/chatbot.rs b/pgml-dashboard/src/api/chatbot.rs index 0b8978844..d5f439902 100644 --- a/pgml-dashboard/src/api/chatbot.rs +++ b/pgml-dashboard/src/api/chatbot.rs @@ -46,9 +46,9 @@ impl ChatRole { match self { ChatRole::User => "user", ChatRole::Bot => match brain { - ChatbotBrain::OpenAIGPT4 - | ChatbotBrain::TekniumOpenHermes25Mistral7B - | ChatbotBrain::Starling7b => "assistant", + ChatbotBrain::OpenAIGPT4 | ChatbotBrain::TekniumOpenHermes25Mistral7B | ChatbotBrain::Starling7b => { + "assistant" + } ChatbotBrain::GrypheMythoMaxL213b => "model", }, ChatRole::System => "system", @@ -69,11 +69,7 @@ impl ChatbotBrain { !matches!(self, Self::OpenAIGPT4) } - fn get_system_message( - &self, - knowledge_base: &KnowledgeBase, - context: &str, - ) -> anyhow::Result { + fn get_system_message(&self, knowledge_base: &KnowledgeBase, context: &str) -> anyhow::Result { match self { Self::OpenAIGPT4 => { let system_prompt = std::env::var("CHATBOT_CHATGPT_SYSTEM_PROMPT")?; @@ -242,10 +238,7 @@ impl Document { .take(32) .map(char::from) .collect(); - let timestamp = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_millis(); + let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis(); Document { id, text: text.to_string(), @@ -275,9 +268,7 @@ async fn get_openai_chatgpt_answer(messages: M) -> anyhow::Result< .json::() .await?; - let response = response["choices"] - .as_array() - .context("No data returned from OpenAI")?[0]["message"]["content"] + let response = response["choices"].as_array().context("No data returned from OpenAI")?[0]["message"]["content"] .as_str() .context("The reponse content from OpenAI was not a string")? .to_string(); @@ -449,12 +440,11 @@ async fn do_chatbot_get_history(user: &User, limit: usize) -> anyhow::Result>() - .join("\n"); + .query() + .vector_recall( + &data.question, + &pipeline, + Some( + json!({ + "instruction": "Represent the Wikipedia question for retrieving supporting documents: " + }) + .into(), + ), + ) + .limit(5) + .fetch_all() + .await? + .into_iter() + .map(|(_, context, metadata)| format!("\n\n#### Document {}: \n{}\n\n", metadata["id"], context)) + .collect::>() + .join("\n"); let history_collection = Collection::new( "ChatHistory", @@ -590,49 +587,47 @@ async fn process_message( .await?; messages.reverse(); - let (mut history, _) = - messages - .into_iter() - .fold((Vec::new(), None), |(mut new_history, role), value| { - let current_role: ChatRole = - serde_json::from_value(value["document"]["role"].to_owned()) - .expect("Error parsing chat role"); - if let Some(role) = role { - if role == current_role { - match role { - ChatRole::User => new_history.push( - serde_json::json!({ - "role": ChatRole::Bot.to_model_specific_role(&brain), - "content": "*no response due to error*" - }) - .into(), - ), - ChatRole::Bot => new_history.push( - serde_json::json!({ - "role": ChatRole::User.to_model_specific_role(&brain), - "content": "*no response due to error*" - }) - .into(), - ), - _ => panic!("Too many system messages"), - } + let (mut history, _) = messages + .into_iter() + .fold((Vec::new(), None), |(mut new_history, role), value| { + let current_role: ChatRole = + serde_json::from_value(value["document"]["role"].to_owned()).expect("Error parsing chat role"); + if let Some(role) = role { + if role == current_role { + match role { + ChatRole::User => new_history.push( + serde_json::json!({ + "role": ChatRole::Bot.to_model_specific_role(&brain), + "content": "*no response due to error*" + }) + .into(), + ), + ChatRole::Bot => new_history.push( + serde_json::json!({ + "role": ChatRole::User.to_model_specific_role(&brain), + "content": "*no response due to error*" + }) + .into(), + ), + _ => panic!("Too many system messages"), } - let new_message: pgml::types::Json = serde_json::json!({ - "role": current_role.to_model_specific_role(&brain), - "content": value["document"]["text"] - }) - .into(); - new_history.push(new_message); - } else if matches!(current_role, ChatRole::User) { - let new_message: pgml::types::Json = serde_json::json!({ - "role": current_role.to_model_specific_role(&brain), - "content": value["document"]["text"] - }) - .into(); - new_history.push(new_message); } - (new_history, Some(current_role)) - }); + let new_message: pgml::types::Json = serde_json::json!({ + "role": current_role.to_model_specific_role(&brain), + "content": value["document"]["text"] + }) + .into(); + new_history.push(new_message); + } else if matches!(current_role, ChatRole::User) { + let new_message: pgml::types::Json = serde_json::json!({ + "role": current_role.to_model_specific_role(&brain), + "content": value["document"]["text"] + }) + .into(); + new_history.push(new_message); + } + (new_history, Some(current_role)) + }); let system_message = brain.get_system_message(&knowledge_base, &context)?; history.insert(0, system_message.into()); @@ -657,8 +652,7 @@ async fn process_message( .into(), ); - let update_history = - UpdateHistory::new(history_collection, user_document, brain, knowledge_base); + let update_history = UpdateHistory::new(history_collection, user_document, brain, knowledge_base); if brain.is_open_source() { let op = OpenSourceAI::new(Some( diff --git a/pgml-dashboard/src/api/cms.rs b/pgml-dashboard/src/api/cms.rs index 1f606b9f9..756b6514f 100644 --- a/pgml-dashboard/src/api/cms.rs +++ b/pgml-dashboard/src/api/cms.rs @@ -1,4 +1,7 @@ -use std::path::{Path, PathBuf}; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; use comrak::{format_html_with_plugins, parse_document, Arena, ComrakPlugins}; use lazy_static::lazy_static; @@ -17,9 +20,45 @@ use crate::{ use serde::{Deserialize, Serialize}; lazy_static! { - static ref BLOG: Collection = Collection::new("Blog", true); - static ref CAREERS: Collection = Collection::new("Careers", true); - static ref DOCS: Collection = Collection::new("Docs", false); + static ref BLOG: Collection = Collection::new( + "Blog", + true, + HashMap::from([ + ("announcing-hnsw-support-in-our-sdk", "speeding-up-vector-recall-5x-with-hnsw"), + ("backwards-compatible-or-bust-python-inside-rust-inside-postgres/", "backwards-compatible-or-bust-python-inside-rust-inside-postgres"), + ("data-is-living-and-relational/", "data-is-living-and-relational"), + ("data-is-living-and-relational/", "data-is-living-and-relational"), + ("generating-llm-embeddings-with-open-source-models-in-postgresml/", "generating-llm-embeddings-with-open-source-models-in-postgresml"), + ("introducing-postgresml-python-sdk-build-end-to-end-vector-search-applications-without-openai-and-pinecone", "introducing-postgresml-python-sdk-build-end-to-end-vector-search-applications-without-openai-and-pin"), + ("llm-based-pipelines-with-postgresml-and-dbt", "llm-based-pipelines-with-postgresml-and-dbt-data-build-tool"), + ("oxidizing-machine-learning/", "oxidizing-machine-learning"), + ("personalize-embedding-vector-search-results-with-huggingface-and-pgvector", "personalize-embedding-results-with-application-data-in-your-database"), + ("pgml-chat-a-command-line-tool-for-deploying-low-latency-knowledge-based-chatbots-part-I", "pgml-chat-a-command-line-tool-for-deploying-low-latency-knowledge-based-chatbots-part-i"), + ("postgres-full-text-search-is-awesome/", "postgres-full-text-search-is-awesome"), + ("postgresml-is-8x-faster-than-python-http-microservices/", "postgresml-is-8-40x-faster-than-python-http-microservices"), + ("postgresml-is-8x-faster-than-python-http-microservices", "postgresml-is-8-40x-faster-than-python-http-microservices"), + ("postgresml-is-moving-to-rust-for-our-2.0-release/", "postgresml-is-moving-to-rust-for-our-2.0-release"), + ("postgresml-raises-4.7m-to-launch-serverless-ai-application-databases-based-on-postgres/", "postgresml-raises-usd4.7m-to-launch-serverless-ai-application-databases-based-on-postgres"), + ("postgresml-raises-4.7m-to-launch-serverless-ai-application-databases-based-on-postgres", "postgresml-raises-usd4.7m-to-launch-serverless-ai-application-databases-based-on-postgres"), + ("scaling-postgresml-to-one-million-requests-per-second/", "scaling-postgresml-to-1-million-requests-per-second"), + ("scaling-postgresml-to-one-million-requests-per-second", "scaling-postgresml-to-1-million-requests-per-second"), + ("which-database-that-is-the-question/", "which-database-that-is-the-question"), + ]) + ); + static ref CAREERS: Collection = Collection::new("Careers", true, HashMap::from([("a", "b")])); + static ref DOCS: Collection = Collection::new( + "Docs", + false, + HashMap::from([ + ("sdks/tutorials/semantic-search-using-instructor-model", "introduction/apis/client-sdks/tutorials/semantic-search-using-instructor-model"), + ("data-storage-and-retrieval/documents", "resources/data-storage-and-retrieval/documents"), + ("guides/setup/quick_start_with_docker", "resources/developer-docs/quick-start-with-docker"), + ("guides/transformers/setup", "resources/developer-docs/quick-start-with-docker"), + ("transformers/fine_tuning/", "introduction/apis/sql-extensions/pgml.tune"), + ("guides/predictions/overview", "introduction/apis/sql-extensions/pgml.predict/"), + ("machine-learning/supervised-learning/data-pre-processing", "introduction/apis/sql-extensions/pgml.train/data-pre-processing"), + ]) + ); } #[derive(Debug, Serialize, Deserialize)] @@ -35,6 +74,7 @@ pub struct Document { impl Document { pub async fn from_path(path: &PathBuf) -> anyhow::Result { + warn!("path: {:?}", path); let contents = tokio::fs::read_to_string(&path).await?; let parts = contents.split("---").collect::>(); @@ -45,8 +85,7 @@ impl Document { if meta.len() == 0 || meta[0].as_hash().is_none() { (None, contents) } else { - let description: Option = match meta[0]["description"].is_badvalue() - { + let description: Option = match meta[0]["description"].is_badvalue() { true => None, false => Some(meta[0]["description"].as_str().unwrap().to_string()), }; @@ -77,17 +116,10 @@ impl Document { let mut plugins = ComrakPlugins::default(); let headings = crate::utils::markdown::MarkdownHeadings::new(); plugins.render.heading_adapter = Some(&headings); - plugins.render.codefence_syntax_highlighter = - Some(&crate::utils::markdown::SyntaxHighlighter {}); + plugins.render.codefence_syntax_highlighter = Some(&crate::utils::markdown::SyntaxHighlighter {}); let mut html = vec![]; - format_html_with_plugins( - root, - &crate::utils::markdown::options(), - &mut html, - &plugins, - ) - .unwrap(); + format_html_with_plugins(root, &crate::utils::markdown::options(), &mut html, &plugins).unwrap(); let html = String::from_utf8(html).unwrap(); let document = Document { @@ -115,10 +147,12 @@ struct Collection { url_root: PathBuf, /// A hierarchical list of content in this collection index: Vec, + /// A list of old paths to new paths in this collection + redirects: HashMap<&'static str, &'static str>, } impl Collection { - pub fn new(name: &str, hide_root: bool) -> Collection { + pub fn new(name: &str, hide_root: bool, redirects: HashMap<&'static str, &'static str>) -> Collection { info!("Loading collection: {name}"); let name = name.to_owned(); let slug = name.to_lowercase(); @@ -131,6 +165,7 @@ impl Collection { root_dir, asset_dir, url_root, + redirects, ..Default::default() }; collection.build_index(hide_root); @@ -151,13 +186,30 @@ impl Collection { ) -> Result { info!("get_content: {} | {path:?}", self.name); - if origin.path().ends_with("/") { + let mut redirected = false; + match self + .redirects + .get(path.as_os_str().to_str().expect("needs to be a well formed path")) + { + Some(redirect) => { + warn!("found redirect: {} <- {:?}", redirect, path); + redirected = true; // reserved for some fallback path + path = PathBuf::from(redirect); + } + None => {} + }; + + let canonical = format!( + "https://postgresml.org{}/{}", + self.url_root.to_string_lossy(), + path.to_string_lossy() + ); + if origin.path().ends_with("/") && !redirected { path = path.join("README"); } - let path = self.root_dir.join(format!("{}.md", path.to_string_lossy())); - self.render(&path, cluster).await + self.render(&path, &canonical, cluster).await } /// Create an index of the Collection based on the SUMMARY.md from Gitbook. @@ -178,9 +230,9 @@ impl Collection { { match node { Node::List(list) => { - let mut links = self.get_sub_links(list).unwrap_or_else(|_| { - panic!("Could not parse list of index links: {summary_path:?}") - }); + let mut links = self + .get_sub_links(list) + .unwrap_or_else(|_| panic!("Could not parse list of index links: {summary_path:?}")); index.append(&mut links); } _ => { @@ -228,9 +280,8 @@ impl Collection { url = url.replace("README", ""); } let url = self.url_root.join(url); - let parent = - IndexLink::new(text.value.as_str()) - .href(&url.to_string_lossy()); + let parent = IndexLink::new(text.value.as_str()) + .href(&url.to_string_lossy()); links.push(parent); } _ => error!("unhandled link child: {node:?}"), @@ -268,6 +319,7 @@ impl Collection { async fn render<'a>( &self, path: &'a PathBuf, + canonical: &str, cluster: &Cluster, ) -> Result { let user = if cluster.context.user.is_anonymous() { @@ -292,6 +344,7 @@ impl Collection { } let layout = layout + .canonical(canonical) .nav_title(&self.name) .nav_links(&index) .toc_links(&doc.toc_links) @@ -385,6 +438,15 @@ async fn get_docs( DOCS.get_content(path, cluster, origin).await } +#[get("/user_guides/", rank = 5)] +async fn get_user_guides( + path: PathBuf, + cluster: &Cluster, + origin: &Origin<'_>, +) -> Result { + DOCS.get_content(path, cluster, origin).await +} + pub fn routes() -> Vec { routes![ get_blog, @@ -393,6 +455,7 @@ pub fn routes() -> Vec { get_careers_asset, get_docs, get_docs_asset, + get_user_guides, search ] } @@ -462,9 +525,7 @@ This is the end of the markdown format_html_with_plugins(root, &options(), &mut html, &plugins).unwrap(); let html = String::from_utf8(html).unwrap(); - assert!( - !html.contains(r#"
"#) || !html.contains(r#"
"#) - ); + assert!(!html.contains(r#"
"#) || !html.contains(r#"
"#)); } async fn rocket() -> Rocket { @@ -542,11 +603,7 @@ This is the end of the markdown let req = client.get("/docs/not_a_doc"); let rsp = req.dispatch().await; - assert!( - rsp.status() == Status::NotFound, - "Returned status {:?}", - rsp.status() - ); + assert!(rsp.status() == Status::NotFound, "Returned status {:?}", rsp.status()); } // Test backend for line highlights and line numbers added @@ -594,17 +651,10 @@ This is the end of the markdown let mut plugins = ComrakPlugins::default(); let headings = crate::utils::markdown::MarkdownHeadings::new(); plugins.render.heading_adapter = Some(&headings); - plugins.render.codefence_syntax_highlighter = - Some(&crate::utils::markdown::SyntaxHighlighter {}); + plugins.render.codefence_syntax_highlighter = Some(&crate::utils::markdown::SyntaxHighlighter {}); let mut html = vec![]; - format_html_with_plugins( - root, - &crate::utils::markdown::options(), - &mut html, - &plugins, - ) - .unwrap(); + format_html_with_plugins(root, &crate::utils::markdown::options(), &mut html, &plugins).unwrap(); let html = String::from_utf8(html).unwrap(); println!("expected: {}", expected); @@ -612,13 +662,8 @@ This is the end of the markdown println!("response: {}", html); assert!( - html.chars() - .filter(|c| !c.is_whitespace()) - .collect::() - == expected - .chars() - .filter(|c| !c.is_whitespace()) - .collect::() + html.chars().filter(|c| !c.is_whitespace()).collect::() + == expected.chars().filter(|c| !c.is_whitespace()).collect::() ) } } diff --git a/pgml-dashboard/src/components/chatbot/mod.rs b/pgml-dashboard/src/components/chatbot/mod.rs index 4cb2f872c..6c9b01b19 100644 --- a/pgml-dashboard/src/components/chatbot/mod.rs +++ b/pgml-dashboard/src/components/chatbot/mod.rs @@ -42,16 +42,8 @@ const EXAMPLE_QUESTIONS: ExampleQuestions = [ ]; const KNOWLEDGE_BASES_WITH_LOGO: [KnowledgeBaseWithLogo; 4] = [ - KnowledgeBaseWithLogo::new( - "postgresml", - "PostgresML", - "/dashboard/static/images/owl_gradient.svg", - ), - KnowledgeBaseWithLogo::new( - "pytorch", - "PyTorch", - "/dashboard/static/images/logos/pytorch.svg", - ), + KnowledgeBaseWithLogo::new("postgresml", "PostgresML", "/dashboard/static/images/owl_gradient.svg"), + KnowledgeBaseWithLogo::new("pytorch", "PyTorch", "/dashboard/static/images/logos/pytorch.svg"), KnowledgeBaseWithLogo::new("rust", "Rust", "/dashboard/static/images/logos/rust.svg"), KnowledgeBaseWithLogo::new( "postgresql", @@ -107,12 +99,7 @@ struct ChatbotBrain { } impl ChatbotBrain { - const fn new( - id: &'static str, - provider: &'static str, - model: &'static str, - logo: &'static str, - ) -> Self { + const fn new(id: &'static str, provider: &'static str, model: &'static str, logo: &'static str) -> Self { Self { id, provider, diff --git a/pgml-dashboard/src/components/dropdown/mod.rs b/pgml-dashboard/src/components/dropdown/mod.rs index 77f71b1ce..734b2eb8a 100644 --- a/pgml-dashboard/src/components/dropdown/mod.rs +++ b/pgml-dashboard/src/components/dropdown/mod.rs @@ -54,10 +54,7 @@ impl Dropdown { } pub fn nav(links: Vec) -> Self { - let binding = links - .iter() - .filter(|link| link.active) - .collect::>(); + let binding = links.iter().filter(|link| link.active).collect::>(); let active = binding.first(); let value = if let Some(active) = active { diff --git a/pgml-dashboard/src/components/inputs/select/mod.rs b/pgml-dashboard/src/components/inputs/select/mod.rs index 9e6d33c1e..7d6fdb5ce 100644 --- a/pgml-dashboard/src/components/inputs/select/mod.rs +++ b/pgml-dashboard/src/components/inputs/select/mod.rs @@ -31,11 +31,7 @@ impl Select { name: "input_name".to_owned(), ..Default::default() } - .options(vec![ - "option1".to_owned(), - "option2".to_owned(), - "option3".to_owned(), - ]) + .options(vec!["option1".to_owned(), "option2".to_owned(), "option3".to_owned()]) } pub fn options(mut self, values: Vec) -> Self { diff --git a/pgml-dashboard/src/components/layouts/head/mod.rs b/pgml-dashboard/src/components/layouts/head/mod.rs index debe33496..e42d12e79 100644 --- a/pgml-dashboard/src/components/layouts/head/mod.rs +++ b/pgml-dashboard/src/components/layouts/head/mod.rs @@ -9,6 +9,7 @@ pub struct Head { pub image: Option, pub preloads: Vec, pub context: Option, + pub canonical: Option, } impl Head { @@ -31,6 +32,11 @@ impl Head { self } + pub fn canonical(mut self, canonical: &str) -> Head { + self.canonical = Some(canonical.to_owned()); + self + } + pub fn image(mut self, image: &str) -> Head { self.image = Some(image.to_owned()); self diff --git a/pgml-dashboard/src/components/layouts/head/template.html b/pgml-dashboard/src/components/layouts/head/template.html index e0b36d896..0f9b09dae 100644 --- a/pgml-dashboard/src/components/layouts/head/template.html +++ b/pgml-dashboard/src/components/layouts/head/template.html @@ -49,7 +49,11 @@ } } - + + <% if canonical.is_some() { %> + + <% } %> + "> diff --git a/pgml-dashboard/src/components/postgres_logo/mod.rs b/pgml-dashboard/src/components/postgres_logo/mod.rs index 8f5c63aa9..fdeef1100 100644 --- a/pgml-dashboard/src/components/postgres_logo/mod.rs +++ b/pgml-dashboard/src/components/postgres_logo/mod.rs @@ -9,9 +9,7 @@ pub struct PostgresLogo { impl PostgresLogo { pub fn new(link: &str) -> PostgresLogo { - PostgresLogo { - link: link.to_owned(), - } + PostgresLogo { link: link.to_owned() } } } diff --git a/pgml-dashboard/src/components/star/mod.rs b/pgml-dashboard/src/components/star/mod.rs index 3689d028f..d84a2db45 100644 --- a/pgml-dashboard/src/components/star/mod.rs +++ b/pgml-dashboard/src/components/star/mod.rs @@ -14,14 +14,8 @@ pub struct Star { static SVGS: Lazy> = Lazy::new(|| { let mut map = HashMap::new(); - map.insert( - "green", - include_str!("../../../static/images/icons/stars/green.svg"), - ); - map.insert( - "party", - include_str!("../../../static/images/icons/stars/party.svg"), - ); + map.insert("green", include_str!("../../../static/images/icons/stars/green.svg")); + map.insert("party", include_str!("../../../static/images/icons/stars/party.svg")); map.insert( "give_it_a_spin", include_str!("../../../static/images/icons/stars/give_it_a_spin.svg"), diff --git a/pgml-dashboard/src/components/stimulus/stimulus_target/mod.rs b/pgml-dashboard/src/components/stimulus/stimulus_target/mod.rs index 7b751aee3..dcc00698b 100644 --- a/pgml-dashboard/src/components/stimulus/stimulus_target/mod.rs +++ b/pgml-dashboard/src/components/stimulus/stimulus_target/mod.rs @@ -8,9 +8,7 @@ pub struct StimulusTarget { impl StimulusTarget { pub fn new() -> Self { - Self { - ..Default::default() - } + Self { ..Default::default() } } pub fn controller(mut self, controller: &str) -> Self { @@ -27,9 +25,7 @@ impl StimulusTarget { impl Render for StimulusTarget { fn render(&self, b: &mut Buffer) -> Result<(), sailfish::RenderError> { match (self.controller.as_ref(), self.name.as_ref()) { - (Some(controller), Some(name)) => { - format!("data-{}-target=\"{}\"", controller, name).render(b) - } + (Some(controller), Some(name)) => format!("data-{}-target=\"{}\"", controller, name).render(b), _ => String::new().render(b), } } diff --git a/pgml-dashboard/src/fairings.rs b/pgml-dashboard/src/fairings.rs index 6107809db..ca818df75 100644 --- a/pgml-dashboard/src/fairings.rs +++ b/pgml-dashboard/src/fairings.rs @@ -34,9 +34,7 @@ impl Fairing for RequestMonitor { } async fn on_response<'r>(&self, request: &'r Request<'_>, response: &mut Response<'r>) { - let start = request - .local_cache(|| RequestMonitorStart(std::time::Instant::now())) - .0; + let start = request.local_cache(|| RequestMonitorStart(std::time::Instant::now())).0; let elapsed = start.elapsed().as_micros() as f32 / 1000.0; let status = response.status().code; let method = request.method().as_str(); diff --git a/pgml-dashboard/src/guards.rs b/pgml-dashboard/src/guards.rs index b16da5cdc..5b60479fa 100644 --- a/pgml-dashboard/src/guards.rs +++ b/pgml-dashboard/src/guards.rs @@ -33,8 +33,7 @@ impl Cluster { .min_connections(min_connections) .after_connect(|conn, _meta| { Box::pin(async move { - conn.execute("SET application_name = 'pgml_dashboard';") - .await?; + conn.execute("SET application_name = 'pgml_dashboard';").await?; Ok(()) }) }) @@ -47,87 +46,39 @@ impl Cluster { user: models::User::default(), cluster: models::Cluster::default(), dropdown_nav: StaticNav { - links: vec![ - StaticNavLink::new("Local".to_string(), "/dashboard".to_string()) - .active(true), - ], + links: vec![StaticNavLink::new("Local".to_string(), "/dashboard".to_string()).active(true)], }, account_management_nav: StaticNav { links: vec![ StaticNavLink::new("Notebooks".to_string(), "/dashboard".to_string()), - StaticNavLink::new( - "Projects".to_string(), - "/dashboard?tab=Projects".to_string(), - ), - StaticNavLink::new( - "Models".to_string(), - "/dashboard?tab=Models".to_string(), - ), - StaticNavLink::new( - "Snapshots".to_string(), - "/dashboard?tab=Snapshots".to_string(), - ), - StaticNavLink::new( - "Upload data".to_string(), - "/dashboard?tab=Upload_Data".to_string(), - ), - StaticNavLink::new( - "PostgresML.org".to_string(), - "https://postgresml.org".to_string(), - ), + StaticNavLink::new("Projects".to_string(), "/dashboard?tab=Projects".to_string()), + StaticNavLink::new("Models".to_string(), "/dashboard?tab=Models".to_string()), + StaticNavLink::new("Snapshots".to_string(), "/dashboard?tab=Snapshots".to_string()), + StaticNavLink::new("Upload data".to_string(), "/dashboard?tab=Upload_Data".to_string()), + StaticNavLink::new("PostgresML.org".to_string(), "https://postgresml.org".to_string()), ], }, upper_left_nav: StaticNav { links: vec![ - StaticNavLink::new( - "Notebooks".to_string(), - "/dashboard?tab=Notebooks".to_string(), - ) - .icon("add_notes") - .active( - uri.is_some() - && (uri.clone().unwrap().starts_with("/dashboard?tab=Notebook") - || uri.clone().unwrap() == "/dashboard"), - ), - StaticNavLink::new( - "Projects".to_string(), - "/dashboard?tab=Projects".to_string(), - ) - .icon("library_add") - .active( - uri.is_some() - && uri.clone().unwrap().starts_with("/dashboard?tab=Project"), - ), - StaticNavLink::new( - "Models".to_string(), - "/dashboard?tab=Models".to_string(), - ) - .icon("space_dashboard") - .active( - uri.is_some() - && uri.clone().unwrap().starts_with("/dashboard?tab=Model"), - ), - StaticNavLink::new( - "Snapshots".to_string(), - "/dashboard?tab=Snapshots".to_string(), - ) - .icon("filter_center_focus") - .active( - uri.is_some() - && uri.clone().unwrap().starts_with("/dashboard?tab=Snapshot"), - ), - StaticNavLink::new( - "Upload data".to_string(), - "/dashboard?tab=Upload_Data".to_string(), - ) - .icon("upload") - .active( - uri.is_some() - && uri - .clone() - .unwrap() - .starts_with("/dashboard?tab=Upload_Data"), - ), + StaticNavLink::new("Notebooks".to_string(), "/dashboard?tab=Notebooks".to_string()) + .icon("add_notes") + .active( + uri.is_some() + && (uri.clone().unwrap().starts_with("/dashboard?tab=Notebook") + || uri.clone().unwrap() == "/dashboard"), + ), + StaticNavLink::new("Projects".to_string(), "/dashboard?tab=Projects".to_string()) + .icon("library_add") + .active(uri.is_some() && uri.clone().unwrap().starts_with("/dashboard?tab=Project")), + StaticNavLink::new("Models".to_string(), "/dashboard?tab=Models".to_string()) + .icon("space_dashboard") + .active(uri.is_some() && uri.clone().unwrap().starts_with("/dashboard?tab=Model")), + StaticNavLink::new("Snapshots".to_string(), "/dashboard?tab=Snapshots".to_string()) + .icon("filter_center_focus") + .active(uri.is_some() && uri.clone().unwrap().starts_with("/dashboard?tab=Snapshot")), + StaticNavLink::new("Upload data".to_string(), "/dashboard?tab=Upload_Data".to_string()) + .icon("upload") + .active(uri.is_some() && uri.clone().unwrap().starts_with("/dashboard?tab=Upload_Data")), ], }, lower_left_nav: StaticNav::default(), diff --git a/pgml-dashboard/src/lib.rs b/pgml-dashboard/src/lib.rs index efbff38a1..3149be444 100644 --- a/pgml-dashboard/src/lib.rs +++ b/pgml-dashboard/src/lib.rs @@ -192,17 +192,12 @@ pub async fn project_get(cluster: ConnectedCluster<'_>, id: i64) -> Result")] -pub async fn notebook_index( - cluster: ConnectedCluster<'_>, - new: Option<&str>, -) -> Result { +pub async fn notebook_index(cluster: ConnectedCluster<'_>, new: Option<&str>) -> Result { Ok(ResponseOk( templates::Notebooks { notebooks: models::Notebook::all(cluster.pool()).await?, @@ -214,47 +209,30 @@ pub async fn notebook_index( } #[post("/notebooks", data = "")] -pub async fn notebook_create( - cluster: &Cluster, - data: Form>, -) -> Result { +pub async fn notebook_create(cluster: &Cluster, data: Form>) -> Result { let notebook = crate::models::Notebook::create(cluster.pool(), data.name).await?; models::Cell::create(cluster.pool(), ¬ebook, models::CellType::Sql as i32, "").await?; - Ok(Redirect::to(format!( - "/dashboard?tab=Notebook&id={}", - notebook.id - ))) + Ok(Redirect::to(format!("/dashboard?tab=Notebook&id={}", notebook.id))) } #[get("/notebooks/")] -pub async fn notebook_get( - cluster: ConnectedCluster<'_>, - notebook_id: i64, -) -> Result { +pub async fn notebook_get(cluster: ConnectedCluster<'_>, notebook_id: i64) -> Result { let notebook = models::Notebook::get_by_id(cluster.pool(), notebook_id).await?; let cells = notebook.cells(cluster.pool()).await?; Ok(ResponseOk( - templates::Notebook { cells, notebook } - .render_once() - .unwrap(), + templates::Notebook { cells, notebook }.render_once().unwrap(), )) } #[post("/notebooks//reset")] -pub async fn notebook_reset( - cluster: ConnectedCluster<'_>, - notebook_id: i64, -) -> Result { +pub async fn notebook_reset(cluster: ConnectedCluster<'_>, notebook_id: i64) -> Result { let notebook = models::Notebook::get_by_id(cluster.pool(), notebook_id).await?; notebook.reset(cluster.pool()).await?; - Ok(Redirect::to(format!( - "/dashboard/notebooks/{}", - notebook_id - ))) + Ok(Redirect::to(format!("/dashboard/notebooks/{}", notebook_id))) } #[post("/notebooks//cell", data = "")] @@ -264,22 +242,14 @@ pub async fn cell_create( cell: Form>, ) -> Result { let notebook = models::Notebook::get_by_id(cluster.pool(), notebook_id).await?; - let mut cell = models::Cell::create( - cluster.pool(), - ¬ebook, - cell.cell_type.parse::()?, - cell.contents, - ) - .await?; + let mut cell = + models::Cell::create(cluster.pool(), ¬ebook, cell.cell_type.parse::()?, cell.contents).await?; if !cell.contents.is_empty() { cell.render(cluster.pool()).await?; } - Ok(Redirect::to(format!( - "/dashboard/notebooks/{}", - notebook_id - ))) + Ok(Redirect::to(format!("/dashboard/notebooks/{}", notebook_id))) } #[post("/notebooks//reorder", data = "")] @@ -301,18 +271,11 @@ pub async fn notebook_reorder( transaction.commit().await?; - Ok(Redirect::to(format!( - "/dashboard/notebooks/{}", - notebook_id - ))) + Ok(Redirect::to(format!("/dashboard/notebooks/{}", notebook_id))) } #[get("/notebooks//cell/")] -pub async fn cell_get( - cluster: ConnectedCluster<'_>, - notebook_id: i64, - cell_id: i64, -) -> Result { +pub async fn cell_get(cluster: ConnectedCluster<'_>, notebook_id: i64, cell_id: i64) -> Result { let notebook = models::Notebook::get_by_id(cluster.pool(), notebook_id).await?; let cell = models::Cell::get_by_id(cluster.pool(), cell_id).await?; @@ -329,11 +292,7 @@ pub async fn cell_get( } #[post("/notebooks//cell//cancel")] -pub async fn cell_cancel( - cluster: ConnectedCluster<'_>, - notebook_id: i64, - cell_id: i64, -) -> Result { +pub async fn cell_cancel(cluster: ConnectedCluster<'_>, notebook_id: i64, cell_id: i64) -> Result { let cell = models::Cell::get_by_id(cluster.pool(), cell_id).await?; cell.cancel(cluster.pool()).await?; Ok(Redirect::to(format!( @@ -352,12 +311,8 @@ pub async fn cell_edit( let notebook = models::Notebook::get_by_id(cluster.pool(), notebook_id).await?; let mut cell = models::Cell::get_by_id(cluster.pool(), cell_id).await?; - cell.update( - cluster.pool(), - data.cell_type.parse::()?, - data.contents, - ) - .await?; + cell.update(cluster.pool(), data.cell_type.parse::()?, data.contents) + .await?; debug!("Rendering cell id={}", cell.id); cell.render(cluster.pool()).await?; @@ -397,11 +352,7 @@ pub async fn cell_trigger_edit( } #[post("/notebooks//cell//play")] -pub async fn cell_play( - cluster: ConnectedCluster<'_>, - notebook_id: i64, - cell_id: i64, -) -> Result { +pub async fn cell_play(cluster: ConnectedCluster<'_>, notebook_id: i64, cell_id: i64) -> Result { let notebook = models::Notebook::get_by_id(cluster.pool(), notebook_id).await?; let mut cell = models::Cell::get_by_id(cluster.pool(), cell_id).await?; cell.render(cluster.pool()).await?; @@ -419,11 +370,7 @@ pub async fn cell_play( } #[post("/notebooks//cell//remove")] -pub async fn cell_remove( - cluster: ConnectedCluster<'_>, - notebook_id: i64, - cell_id: i64, -) -> Result { +pub async fn cell_remove(cluster: ConnectedCluster<'_>, notebook_id: i64, cell_id: i64) -> Result { let notebook = models::Notebook::get_by_id(cluster.pool(), notebook_id).await?; let cell = models::Cell::get_by_id(cluster.pool(), cell_id).await?; let bust_cache = std::time::SystemTime::now() @@ -442,11 +389,7 @@ pub async fn cell_remove( } #[post("/notebooks//cell//delete")] -pub async fn cell_delete( - cluster: ConnectedCluster<'_>, - notebook_id: i64, - cell_id: i64, -) -> Result { +pub async fn cell_delete(cluster: ConnectedCluster<'_>, notebook_id: i64, cell_id: i64) -> Result { let _notebook = models::Notebook::get_by_id(cluster.pool(), notebook_id).await?; let cell = models::Cell::get_by_id(cluster.pool(), cell_id).await?; @@ -518,9 +461,7 @@ pub async fn models_get(cluster: ConnectedCluster<'_>, id: i64) -> Result) -> Result { let snapshots = models::Snapshot::all(cluster.pool()).await?; - Ok(ResponseOk( - templates::Snapshots { snapshots }.render_once().unwrap(), - )) + Ok(ResponseOk(templates::Snapshots { snapshots }.render_once().unwrap())) } #[get("/snapshots/")] @@ -560,12 +501,7 @@ pub async fn deployments_index(cluster: ConnectedCluster<'_>) -> Result")] pub async fn uploaded_index(cluster: ConnectedCluster<'_>, table_name: &str) -> ResponseOk { - let sql = templates::Sql::new( - cluster.pool(), - &format!("SELECT * FROM {} LIMIT 10", table_name), - ) - .await - .unwrap(); + let sql = templates::Sql::new(cluster.pool(), &format!("SELECT * FROM {} LIMIT 10", table_name)) + .await + .unwrap(); ResponseOk( templates::Uploaded { table_name: table_name.to_string(), @@ -636,11 +569,7 @@ pub async fn uploaded_index(cluster: ConnectedCluster<'_>, table_name: &str) -> } #[get("/?&")] -pub async fn dashboard( - cluster: ConnectedCluster<'_>, - tab: Option<&str>, - id: Option, -) -> Result { +pub async fn dashboard(cluster: ConnectedCluster<'_>, tab: Option<&str>, id: Option) -> Result { let mut layout = crate::templates::WebAppBase::new("Dashboard", &cluster.inner.context); let mut breadcrumbs = vec![NavLink::new("Dashboard", "/dashboard")]; @@ -672,13 +601,8 @@ pub async fn dashboard( "Project" => { let project = models::Project::get_by_id(cluster.pool(), id.unwrap()).await?; breadcrumbs.push(NavLink::new("Projects", "/dashboard?tab=Projects")); - breadcrumbs.push( - NavLink::new( - &project.name, - &format!("/dashboard?tab=Project&id={}", project.id), - ) - .active(), - ); + breadcrumbs + .push(NavLink::new(&project.name, &format!("/dashboard?tab=Project&id={}", project.id)).active()); } "Models" => { @@ -694,13 +618,7 @@ pub async fn dashboard( &project.name, &format!("/dashboard?tab=Project&id={}", project.id), )); - breadcrumbs.push( - NavLink::new( - &model.algorithm, - &format!("/dashboard?tab=Model&id={}", model.id), - ) - .active(), - ); + breadcrumbs.push(NavLink::new(&model.algorithm, &format!("/dashboard?tab=Model&id={}", model.id)).active()); } "Snapshots" => { @@ -756,11 +674,7 @@ pub async fn dashboard( "Model" => vec![tabs::Tab { name: "Model", - content: ModelTab { - model_id: id.unwrap(), - } - .render_once() - .unwrap(), + content: ModelTab { model_id: id.unwrap() }.render_once().unwrap(), }], "Snapshots" => vec![tabs::Tab { @@ -786,9 +700,7 @@ pub async fn dashboard( let nav_tabs = tabs::Tabs::new(tabs, Some("Notebooks"), Some(tab))?; - Ok(ResponseOk( - layout.render(templates::Dashboard { tabs: nav_tabs }), - )) + Ok(ResponseOk(layout.render(templates::Dashboard { tabs: nav_tabs }))) } #[get("/playground")] @@ -798,12 +710,7 @@ pub async fn playground(cluster: &Cluster) -> Result { } #[get("/notifications/remove_banner?&")] -pub fn remove_banner( - id: String, - alert: bool, - cookies: &CookieJar<'_>, - context: &Cluster, -) -> ResponseOk { +pub fn remove_banner(id: String, alert: bool, cookies: &CookieJar<'_>, context: &Cluster) -> ResponseOk { let mut viewed = Notifications::get_viewed(cookies); viewed.push(id); @@ -814,9 +721,7 @@ pub fn remove_banner( if alert { notifications .into_iter() - .filter(|n: &&Notification| -> bool { - Notification::is_alert(&n.level) && !viewed.contains(&n.id) - }) + .filter(|n: &&Notification| -> bool { Notification::is_alert(&n.level) && !viewed.contains(&n.id) }) .next() } else { notifications @@ -831,17 +736,9 @@ pub fn remove_banner( }; if alert { - return ResponseOk( - AlertBanner::from_notification(notification) - .render_once() - .unwrap(), - ); + return ResponseOk(AlertBanner::from_notification(notification).render_once().unwrap()); } else { - return ResponseOk( - FeatureBanner::from_notification(notification) - .render_once() - .unwrap(), - ); + return ResponseOk(FeatureBanner::from_notification(notification).render_once().unwrap()); } } diff --git a/pgml-dashboard/src/main.rs b/pgml-dashboard/src/main.rs index e8161a452..f09b21d8b 100644 --- a/pgml-dashboard/src/main.rs +++ b/pgml-dashboard/src/main.rs @@ -1,8 +1,6 @@ use log::{error, info, warn}; -use rocket::{ - catch, catchers, fs::FileServer, get, http::Status, request::Request, response::Redirect, -}; +use rocket::{catch, catchers, fs::FileServer, get, http::Status, request::Request, response::Redirect}; use pgml_dashboard::{ guards, @@ -33,10 +31,7 @@ async fn not_found_handler(_status: Status, _request: &Request<'_>) -> Response } #[catch(default)] -async fn error_catcher( - status: Status, - request: &Request<'_>, -) -> Result { +async fn error_catcher(status: Status, request: &Request<'_>) -> Result { Err(responses::Error(anyhow::anyhow!( "{} {}\n{:?}", status.code, @@ -59,8 +54,7 @@ async fn configure_reporting() -> Option { log::set_boxed_logger(Box::new(logger)).unwrap(); log::set_max_level(level); - let name = - sentry::release_name!().unwrap_or_else(|| std::borrow::Cow::Borrowed("cloud2")); + let name = sentry::release_name!().unwrap_or_else(|| std::borrow::Cow::Borrowed("cloud2")); let sha = env!("GIT_SHA"); let release = format!("{name}+{sha}"); let result = sentry::init(( @@ -111,10 +105,7 @@ async fn main() { .mount("/dashboard", pgml_dashboard::routes()) .mount("/", pgml_dashboard::api::routes()) .mount("/", rocket::routes![pgml_dashboard::playground]) - .register( - "/", - catchers![error_catcher, not_authorized_catcher, not_found_handler], - ) + .register("/", catchers![error_catcher, not_authorized_catcher, not_found_handler]) .attach(pgml_dashboard::fairings::RequestMonitor::new()) .ignite() .await @@ -138,9 +129,7 @@ mod test { async fn rocket() -> Rocket { dotenv::dotenv().ok(); - pgml_dashboard::migrate(Cluster::default(None).pool()) - .await - .unwrap(); + pgml_dashboard::migrate(Cluster::default(None).pool()).await.unwrap(); rocket::build() .manage(markdown::SearchIndex::open().unwrap()) @@ -290,7 +279,10 @@ mod test { #[rocket::async_test] async fn test_blogs() { let client = Client::tracked(rocket().await).await.unwrap(); - let response = client.get("/blog/postgresml-raises-usd4.7m-to-launch-serverless-ai-application-databases-based-on-postgres").dispatch().await; + let response = client + .get("/blog/postgresml-raises-usd4.7m-to-launch-serverless-ai-application-databases-based-on-postgres") + .dispatch() + .await; assert_eq!(response.status().code, 200); } } diff --git a/pgml-dashboard/src/models.rs b/pgml-dashboard/src/models.rs index 8896b9fae..c26ca363f 100644 --- a/pgml-dashboard/src/models.rs +++ b/pgml-dashboard/src/models.rs @@ -187,12 +187,7 @@ pub struct Cell { } impl Cell { - pub async fn create( - pool: &PgPool, - notebook: &Notebook, - cell_type: i32, - contents: &str, - ) -> anyhow::Result { + pub async fn create(pool: &PgPool, notebook: &Notebook, cell_type: i32, contents: &str) -> anyhow::Result { Ok(sqlx::query_as!( Cell, " @@ -249,12 +244,7 @@ impl Cell { .await?) } - pub async fn update( - &mut self, - pool: &PgPool, - cell_type: i32, - contents: &str, - ) -> anyhow::Result<()> { + pub async fn update(&mut self, pool: &PgPool, cell_type: i32, contents: &str) -> anyhow::Result<()> { self.cell_type = cell_type; self.contents = contents.to_string(); @@ -296,11 +286,7 @@ impl Cell { .await?) } - pub async fn reorder( - self, - pool: impl sqlx::PgExecutor<'_>, - cell_number: i32, - ) -> anyhow::Result { + pub async fn reorder(self, pool: impl sqlx::PgExecutor<'_>, cell_number: i32) -> anyhow::Result { Ok(sqlx::query_as!( Cell, " @@ -348,11 +334,7 @@ impl Cell { let (rendering, execution_time) = match cell_type { CellType::Sql => { - let queries: Vec<&str> = self - .contents - .split(';') - .filter(|q| !q.trim().is_empty()) - .collect(); + let queries: Vec<&str> = self.contents.split(';').filter(|q| !q.trim().is_empty()).collect(); let mut rendering = String::new(); let mut total_execution_duration = std::time::Duration::default(); @@ -678,18 +660,12 @@ impl Snapshot { pub fn rows(&self) -> Option { match self.analysis.as_ref() { - Some(analysis) => analysis - .get("samples") - .map(|samples| samples.as_f64().unwrap() as i64), + Some(analysis) => analysis.get("samples").map(|samples| samples.as_f64().unwrap() as i64), None => None, } } - pub async fn samples( - &self, - pool: &PgPool, - rows: i64, - ) -> anyhow::Result>> { + pub async fn samples(&self, pool: &PgPool, rows: i64) -> anyhow::Result>> { let mut samples = HashMap::new(); if self.exists { @@ -722,12 +698,9 @@ impl Snapshot { pub fn columns(&self) -> Option>> { match self.columns.as_ref() { - Some(columns) => columns.as_array().map(|columns| { - columns - .iter() - .map(|column| column.as_object().unwrap()) - .collect() - }), + Some(columns) => columns + .as_array() + .map(|columns| columns.iter().map(|column| column.as_object().unwrap()).collect()), None => None, } @@ -793,9 +766,7 @@ impl Snapshot { // 2.2+ None => { let columns = self.columns().unwrap(); - let column = columns - .iter() - .find(|column| column["name"].as_str().unwrap() == name); + let column = columns.iter().find(|column| column["name"].as_str().unwrap() == name); match column { Some(column) => column .get("statistics") @@ -825,10 +796,7 @@ pub struct Deployment { } impl Deployment { - pub async fn get_by_project_id( - pool: &PgPool, - project_id: i64, - ) -> anyhow::Result> { + pub async fn get_by_project_id(pool: &PgPool, project_id: i64) -> anyhow::Result> { Ok(sqlx::query_as!( Deployment, "SELECT @@ -904,12 +872,7 @@ impl UploadedFile { .await?) } - pub async fn upload( - &mut self, - pool: &PgPool, - file: &std::path::Path, - headers: bool, - ) -> anyhow::Result<()> { + pub async fn upload(&mut self, pool: &PgPool, file: &std::path::Path, headers: bool) -> anyhow::Result<()> { // Open the temp file. let mut reader = tokio::io::BufReader::new(tokio::fs::File::open(file).await?); diff --git a/pgml-dashboard/src/responses.rs b/pgml-dashboard/src/responses.rs index fe7574124..cec755200 100644 --- a/pgml-dashboard/src/responses.rs +++ b/pgml-dashboard/src/responses.rs @@ -81,8 +81,7 @@ impl<'r> response::Responder<'r, 'r> for Response { let body = match self.body { Some(body) => body, None => match self.status.code { - 404 => templates::Layout::new("Internal Server Error", None) - .render(templates::NotFound {}), + 404 => templates::Layout::new("Internal Server Error", None).render(templates::NotFound {}), _ => "".into(), }, }; @@ -133,8 +132,7 @@ impl<'r> response::Responder<'r, 'r> for Error { "".into() }; - let body = templates::Layout::new("Internal Server Error", None) - .render(templates::Error { error }); + let body = templates::Layout::new("Internal Server Error", None).render(templates::Error { error }); response::Response::build_from(body.respond_to(request)?) .header(ContentType::new("text", "html")) diff --git a/pgml-dashboard/src/templates/mod.rs b/pgml-dashboard/src/templates/mod.rs index 6d9a6c4fd..07b5e7488 100644 --- a/pgml-dashboard/src/templates/mod.rs +++ b/pgml-dashboard/src/templates/mod.rs @@ -44,9 +44,7 @@ pub struct Layout { impl Layout { pub fn new(title: &str, context: Option<&crate::guards::Cluster>) -> Self { let head = match context.as_ref() { - Some(context) => Head::new() - .title(title) - .context(&context.context.head_items), + Some(context) => Head::new().title(title).context(&context.context.head_items), None => Head::new().title(title), }; @@ -68,6 +66,11 @@ impl Layout { self } + pub fn canonical(&mut self, canonical: &str) -> &mut Self { + self.head.canonical = Some(canonical.to_string()); + self + } + pub fn content(&mut self, content: &str) -> &mut Self { self.content = Some(content.to_owned()); self @@ -346,10 +349,7 @@ impl Sql { let (hour, minute, second, milli) = value.as_hms_milli(); let (year, month, day) = value.to_calendar_date(); - format!( - "{}-{}-{} {}:{}:{}.{}", - year, month, day, hour, minute, second, milli - ) + format!("{}-{}-{} {}:{}:{}.{}", year, month, day, hour, minute, second, milli) } "MONEY" => { diff --git a/pgml-dashboard/src/utils/config.rs b/pgml-dashboard/src/utils/config.rs index 9f76eaabd..fdb31ad57 100644 --- a/pgml-dashboard/src/utils/config.rs +++ b/pgml-dashboard/src/utils/config.rs @@ -74,8 +74,7 @@ impl Config { render_errors: env_is_set("RENDER_ERRORS") || dev_mode, deployment: env_string_default("DEPLOYMENT", "localhost"), signup_url, - standalone_dashboard: !cargo_manifest_dir.contains("deps") - && !cargo_manifest_dir.contains("cloud2"), + standalone_dashboard: !cargo_manifest_dir.contains("deps") && !cargo_manifest_dir.contains("cloud2"), github_stars, css_extension, js_extension, diff --git a/pgml-dashboard/src/utils/cookies.rs b/pgml-dashboard/src/utils/cookies.rs index af791b0da..02f102205 100644 --- a/pgml-dashboard/src/utils/cookies.rs +++ b/pgml-dashboard/src/utils/cookies.rs @@ -12,10 +12,7 @@ impl Notifications { pub fn get_viewed(cookies: &CookieJar<'_>) -> Vec { let viewed = match cookies.get_private("session") { Some(session) => { - match serde_json::from_str::(session.value()).unwrap() - ["notifications"] - .as_array() - { + match serde_json::from_str::(session.value()).unwrap()["notifications"].as_array() { Some(items) => items .into_iter() .map(|x| x.as_str().unwrap().to_string()) diff --git a/pgml-dashboard/src/utils/datadog.rs b/pgml-dashboard/src/utils/datadog.rs index f85d64c26..e7a832388 100644 --- a/pgml-dashboard/src/utils/datadog.rs +++ b/pgml-dashboard/src/utils/datadog.rs @@ -74,11 +74,7 @@ pub async fn timing(metric: &str, millis: f32, tags: Option<&HashMap( - metric: &str, - tags: Option<&HashMap>, - f: impl FnOnce() -> T, -) -> T { +pub async fn time(metric: &str, tags: Option<&HashMap>, f: impl FnOnce() -> T) -> T { let start = Instant::now(); let result = f(); send( diff --git a/pgml-dashboard/src/utils/markdown.rs b/pgml-dashboard/src/utils/markdown.rs index fb0557aa2..0cf172289 100644 --- a/pgml-dashboard/src/utils/markdown.rs +++ b/pgml-dashboard/src/utils/markdown.rs @@ -601,9 +601,7 @@ impl Admonition { impl From<&str> for Admonition { fn from(utf8: &str) -> Admonition { - let (class, icon, title) = if utf8.starts_with("!!! info") - || utf8.starts_with(r#"{% hint style="info" %}"#) - { + let (class, icon, title) = if utf8.starts_with("!!! info") || utf8.starts_with(r#"{% hint style="info" %}"#) { ("admonition-info", "help", "Info") } else if utf8.starts_with("!!! note") { ("admonition-note", "priority_high", "Note") @@ -615,22 +613,17 @@ impl From<&str> for Admonition { ("admonition-question", "help", "Question") } else if utf8.starts_with("!!! example") { ("admonition-example", "code", "Example") - } else if utf8.starts_with("!!! success") - || utf8.starts_with(r#"{% hint style="success" %}"#) - { + } else if utf8.starts_with("!!! success") || utf8.starts_with(r#"{% hint style="success" %}"#) { ("admonition-success", "check_circle", "Success") } else if utf8.starts_with("!!! quote") { ("admonition-quote", "format_quote", "Quote") } else if utf8.starts_with("!!! bug") { ("admonition-bug", "bug_report", "Bug") - } else if utf8.starts_with("!!! warning") - || utf8.starts_with(r#"{% hint style="warning" %}"#) - { + } else if utf8.starts_with("!!! warning") || utf8.starts_with(r#"{% hint style="warning" %}"#) { ("admonition-warning", "warning", "Warning") } else if utf8.starts_with("!!! fail") { ("admonition-fail", "dangerous", "Fail") - } else if utf8.starts_with("!!! danger") || utf8.starts_with(r#"{% hint style="danger" %}"#) - { + } else if utf8.starts_with("!!! danger") || utf8.starts_with(r#"{% hint style="danger" %}"#) { ("admonition-danger", "gpp_maybe", "Danger") } else { ("admonition-generic", "", "") @@ -796,13 +789,12 @@ pub fn mkdocs<'a>(root: &'a AstNode<'a>, arena: &'a Arena>) -> anyho let tab = Tab::new(text.replace("=== ", "").replace('\"', "")); if tabs.is_empty() { - let n = - arena.alloc(Node::new(RefCell::new(Ast::new(NodeValue::HtmlInline( - r#" + let n = arena.alloc(Node::new(RefCell::new(Ast::new(NodeValue::HtmlInline( + r#" ".to_string()), - )))); + let n = arena.alloc(Node::new(RefCell::new(Ast::new(NodeValue::HtmlInline( + "".to_string(), + ))))); parent.insert_after(n); parent.detach(); parent = n; - let n = arena.alloc(Node::new(RefCell::new(Ast::new( - NodeValue::HtmlInline(r#"
"#.to_string()), - )))); + let n = arena.alloc(Node::new(RefCell::new(Ast::new(NodeValue::HtmlInline( + r#"
"#.to_string(), + ))))); parent.insert_after(n); parent = n; for tab in tabs.iter() { - let r = arena.alloc(Node::new(RefCell::new(Ast::new( - NodeValue::HtmlInline(format!( - r#" + let r = arena.alloc(Node::new(RefCell::new(Ast::new(NodeValue::HtmlInline(format!( + r#"
"#, - active = if tab.active { "show active" } else { "" }, - id = tab.id - )), - )))); + active = if tab.active { "show active" } else { "" }, + id = tab.id + )))))); for child in tab.children.iter() { r.append(child); @@ -869,17 +859,17 @@ pub fn mkdocs<'a>(root: &'a AstNode<'a>, arena: &'a Arena>) -> anyho parent.append(r); parent = r; - let n = arena.alloc(Node::new(RefCell::new(Ast::new( - NodeValue::HtmlInline(r#"
"#.to_string()), - )))); + let n = arena.alloc(Node::new(RefCell::new(Ast::new(NodeValue::HtmlInline( + r#"
"#.to_string(), + ))))); parent.insert_after(n); parent = n; } - parent.insert_after(arena.alloc(Node::new(RefCell::new(Ast::new( - NodeValue::HtmlInline(r#"
"#.to_string()), - ))))); + parent.insert_after(arena.alloc(Node::new(RefCell::new(Ast::new(NodeValue::HtmlInline( + r#""#.to_string(), + )))))); tabs.clear(); node.detach(); @@ -901,13 +891,12 @@ pub fn mkdocs<'a>(root: &'a AstNode<'a>, arena: &'a Arena>) -> anyho let tab = Tab::new(text.replace("{% tab title=\"", "").replace("\" %}", "")); if tabs.is_empty() { - let n = - arena.alloc(Node::new(RefCell::new(Ast::new(NodeValue::HtmlInline( - r#" + let n = arena.alloc(Node::new(RefCell::new(Ast::new(NodeValue::HtmlInline( + r#" ".to_string()), - )))); + let n = arena.alloc(Node::new(RefCell::new(Ast::new(NodeValue::HtmlInline( + "".to_string(), + ))))); parent.insert_after(n); parent.detach(); parent = n; - let n = arena.alloc(Node::new(RefCell::new(Ast::new( - NodeValue::HtmlInline(r#"
"#.to_string()), - )))); + let n = arena.alloc(Node::new(RefCell::new(Ast::new(NodeValue::HtmlInline( + r#"
"#.to_string(), + ))))); parent.insert_after(n); parent = n; for tab in tabs.iter() { - let r = arena.alloc(Node::new(RefCell::new(Ast::new( - NodeValue::HtmlInline(format!( - r#" + let r = arena.alloc(Node::new(RefCell::new(Ast::new(NodeValue::HtmlInline(format!( + r#"
"#, - active = if tab.active { "show active" } else { "" }, - id = tab.id - )), - )))); + active = if tab.active { "show active" } else { "" }, + id = tab.id + )))))); for child in tab.children.iter() { r.append(child); @@ -974,17 +961,17 @@ pub fn mkdocs<'a>(root: &'a AstNode<'a>, arena: &'a Arena>) -> anyho parent.append(r); parent = r; - let n = arena.alloc(Node::new(RefCell::new(Ast::new( - NodeValue::HtmlInline(r#"
"#.to_string()), - )))); + let n = arena.alloc(Node::new(RefCell::new(Ast::new(NodeValue::HtmlInline( + r#"
"#.to_string(), + ))))); parent.insert_after(n); parent = n; } - parent.insert_after(arena.alloc(Node::new(RefCell::new(Ast::new( - NodeValue::HtmlInline(r#"
"#.to_string()), - ))))); + parent.insert_after(arena.alloc(Node::new(RefCell::new(Ast::new(NodeValue::HtmlInline( + r#""#.to_string(), + )))))); tabs.clear(); node.detach(); @@ -1031,9 +1018,7 @@ pub fn mkdocs<'a>(root: &'a AstNode<'a>, arena: &'a Arena>) -> anyho }; if let Some(html) = code_block.html("code") { - let n = arena.alloc(Node::new(RefCell::new(Ast::new( - NodeValue::HtmlInline(html), - )))); + let n = arena.alloc(Node::new(RefCell::new(Ast::new(NodeValue::HtmlInline(html))))); parent.insert_after(n); } @@ -1051,9 +1036,7 @@ pub fn mkdocs<'a>(root: &'a AstNode<'a>, arena: &'a Arena>) -> anyho }; if let Some(html) = code_block.html("results") { - let n = arena.alloc(Node::new(RefCell::new(Ast::new( - NodeValue::HtmlInline(html), - )))); + let n = arena.alloc(Node::new(RefCell::new(Ast::new(NodeValue::HtmlInline(html))))); parent.insert_after(n); } @@ -1062,14 +1045,12 @@ pub fn mkdocs<'a>(root: &'a AstNode<'a>, arena: &'a Arena>) -> anyho } else if text.contains("{% content-ref url=") { let url = parser(text.as_ref(), r#"url=""#); - let n = arena.alloc(Node::new(RefCell::new(Ast::new(NodeValue::HtmlInline( - format!( - r#" "# - .to_string(), - ), - )))); + .to_string(), + ))))); parent.insert_after(n); } }, None => { - let n = arena.alloc(Node::new(RefCell::new(Ast::new( - NodeValue::HtmlInline( - r#" + let n = arena.alloc(Node::new(RefCell::new(Ast::new(NodeValue::HtmlInline( + r#" "# - .to_string(), - ), - )))); + .to_string(), + ))))); parent.insert_after(n); } @@ -1203,10 +1180,8 @@ impl SearchIndex { pub fn documents() -> Vec { // TODO imrpove this .display().to_string() - let guides = glob::glob(&config::cms_dir().join("docs/**/*.md").display().to_string()) - .expect("glob failed"); - let blogs = glob::glob(&config::cms_dir().join("blog/**/*.md").display().to_string()) - .expect("glob failed"); + let guides = glob::glob(&config::cms_dir().join("docs/**/*.md").display().to_string()).expect("glob failed"); + let blogs = glob::glob(&config::cms_dir().join("blog/**/*.md").display().to_string()).expect("glob failed"); guides .chain(blogs) .map(|path| path.expect("glob path failed")) @@ -1294,8 +1269,7 @@ impl SearchIndex { let path = Self::path(); if !path.exists() { - std::fs::create_dir(&path) - .expect("failed to create search_index directory, is the filesystem writable?"); + std::fs::create_dir(&path).expect("failed to create search_index directory, is the filesystem writable?"); } let index = match tantivy::Index::open_in_dir(&path) { @@ -1348,14 +1322,13 @@ impl SearchIndex { // If that's not enough, search using prefix search on the title. if top_docs.len() < 10 { - let query = - match RegexQuery::from_pattern(&format!("{}.*", query_string), title_regex_field) { - Ok(query) => query, - Err(err) => { - warn!("Query regex error: {}", err); - return Ok(Vec::new()); - } - }; + let query = match RegexQuery::from_pattern(&format!("{}.*", query_string), title_regex_field) { + Ok(query) => query, + Err(err) => { + warn!("Query regex error: {}", err); + return Ok(Vec::new()); + } + }; let more_results = searcher.search(&query, &TopDocs::with_limit(10)).unwrap(); top_docs.extend(more_results); diff --git a/pgml-dashboard/src/utils/tabs.rs b/pgml-dashboard/src/utils/tabs.rs index 408eb462a..e7d81099d 100644 --- a/pgml-dashboard/src/utils/tabs.rs +++ b/pgml-dashboard/src/utils/tabs.rs @@ -12,18 +12,10 @@ pub struct Tabs<'a> { } impl<'a> Tabs<'a> { - pub fn new( - tabs: Vec>, - default: Option<&'a str>, - active: Option<&'a str>, - ) -> anyhow::Result { + pub fn new(tabs: Vec>, default: Option<&'a str>, active: Option<&'a str>) -> anyhow::Result { let default = match default { Some(default) => default, - _ => { - tabs.get(0) - .ok_or(anyhow!("There must be at least one tab."))? - .name - } + _ => tabs.get(0).ok_or(anyhow!("There must be at least one tab."))?.name, }; let active = active @@ -34,10 +26,6 @@ impl<'a> Tabs<'a> { }) .unwrap_or(default); - Ok(Tabs { - tabs, - default, - active, - }) + Ok(Tabs { tabs, default, active }) } }