From 877d7e15a304254ab285b8f093397113d8aaf682 Mon Sep 17 00:00:00 2001 From: Myrddin Dundragon Date: Tue, 2 Sep 2025 14:00:19 -0400 Subject: [PATCH] Initial Bard library commit. This was added to test the database into server context pipeline. --- bard/Cargo.toml | 3 +- bard/src/components.rs | 238 +++++++++++++++++++++++++++++++++++++++++ bard/src/lib.rs | 12 +++ bard/src/server.rs | 27 +++++ 4 files changed, 279 insertions(+), 1 deletion(-) create mode 100644 bard/src/components.rs create mode 100644 bard/src/server.rs diff --git a/bard/Cargo.toml b/bard/Cargo.toml index c203d67..734a949 100644 --- a/bard/Cargo.toml +++ b/bard/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bard" -version = "0.1.0" +version = "0.0.1" edition = "2024" description = "Dioxus components that will display a Tavern blogging system Blog." repository = "/CyberMages/tavern" @@ -10,3 +10,4 @@ readme = "README.md" license = "Apache-2.0" [dependencies] +tavern = { version = "0.2.4", path = "../tavern", registry = "cybermages" } diff --git a/bard/src/components.rs b/bard/src/components.rs new file mode 100644 index 0000000..2c1895b --- /dev/null +++ b/bard/src/components.rs @@ -0,0 +1,238 @@ + +use dioxus::prelude::*; + +use tavern::Lore; + +use crate::components::social::*; +use crate::page::Page; + + + +const AUTHOR_CSS: Asset = asset!("/assets/css/blog_author.css"); +const LIST_CSS: Asset = asset!("/assets/css/blog_list.css"); +const NAV_CSS: Asset = asset!("/assets/css/blog_nav.css"); +const POST_CSS: Asset = asset!("/assets/css/blog_post.css"); +const TAG_CSS: Asset = asset!("/assets/css/blog_tag.css"); + + + +#[derive(Clone, Copy, PartialEq)] +pub enum TagStyle +{ + Regular, + Easy, + Medium, + Hard +} + +#[derive(Clone, Copy, PartialEq)] +pub enum Tag +{ + LeetCode, + Embedded, + Simulation, + Web +} + + + +#[component] +pub fn TagItem(tag: Tag, style: TagStyle) -> Element +{ + let text = match tag + { + Tag::LeetCode => { "LeetCode" } + Tag::Embedded => { "Embedded" } + Tag::Simulation => { "Simulation" } + Tag::Web => { "Web" } + }; + + let tag_style = match style + { + TagStyle::Regular => { "none" } + TagStyle::Easy => { "easy" } + TagStyle::Medium => { "medium" } + TagStyle::Hard => { "hard" } + }; + + rsx! + { + li + { + class: "tag_item", + a { class: "{tag_style}", "{text}" } + } + } +} + +#[component] +pub fn TagList(children: Element) -> Element +{ + rsx! + { + document::Link { rel: "stylesheet", href: TAG_CSS } + + h5 + { + class: "blog_tag_style", + ul + { + class: "tag_list", + {children} + } + } + } +} + + +#[component] +pub fn BlogAuthor() -> Element +{ + rsx! + { + document::Link { rel: "stylesheet", href: AUTHOR_CSS } + + section + { + class: "blog_author_style", + + } + } +} + + +#[component] +pub fn BlogItem(title: String, slug: String, author: String, + summary: String, tags: Vec<(Tag, TagStyle)>) -> Element +{ + rsx! + { + li + { + class: "blog_item", + Link + { + to: Page::Post { slug: slug }, + h1 { "{title}" } + } + TagList + { + for (tag, style) in tags + { + TagItem + { + tag: tag, + style: style + } + } + } + h4 { "Author: {author}" } + p { "{summary}" } + } + } +} + + +#[component] +pub fn BlogList(children: Element) -> Element +{ + rsx! + { + document::Link { rel: "stylesheet", href: LIST_CSS } + + section + { + class: "blog_list_style", + ul + { + class: "blog_list", + {children} + } + } + } +} + + +#[component] +pub fn BlogNav() -> Element +{ + rsx! + { + document::Link { rel: "stylesheet", href: NAV_CSS } + + section + { + class: "blog_nav_style", + div + { + class: "tag_style", + h3 { "Categories" } + ul + { + class: "tag_list", + li + { + class: "tag_item", + a { href: "/blog.html", "LeetCode" } + } + li + { + class: "tag_item", + a { href: "/blog.html", "Embedded" } + } + li + { + class: "tag_item", + a { href: "/blog.html", "Simulation" } + } + li + { + class: "tag_item", + a { href: "/blog.html", "Web" } + } + } + } + SocialButtonList + { + SocialButton + { + link: String::from("https://www.linkedin.com/company/cybermages-llc"), + company: Company::LinkedIn, + size: Size::Small + } + SocialButton + { + link: String::from("https://bsky.app/profile/cybermages.tech"), + company: Company::Bluesky, + size: Size::Small + } + } + } + } +} + + +#[component] +pub fn BlogPost(children: Element) -> Element +{ + rsx! + { + document::Link { rel: "stylesheet", href: POST_CSS } + + article + { + class: "blog_post_style", + h1 { "Leet Code: Let's Get Started" } + TagList + { + TagItem + { + tag: Tag::LeetCode, + style: TagStyle::Regular + } + } + h4 { "Author: ", a { href: "https://cybermages.tech", "Jason Smith" } } + {children} + } + } +} diff --git a/bard/src/lib.rs b/bard/src/lib.rs index 65ef63c..2a171fe 100644 --- a/bard/src/lib.rs +++ b/bard/src/lib.rs @@ -4,4 +4,16 @@ mod info; +use tavern::Database; + pub use crate::info::{get_name, get_version}; + + + +pub async fn init_database

(path: P) + -> Result, Box> + where P: AsRef +{ + let db = Database::open(path).await?; + Ok(std::sync::Arc::new(db)) +} diff --git a/bard/src/server.rs b/bard/src/server.rs new file mode 100644 index 0000000..81e5bfa --- /dev/null +++ b/bard/src/server.rs @@ -0,0 +1,27 @@ +use dioxus::prelude::*; +use std::sync::Arc; +use crate::{Database, Lore, Tale}; + +#[server] +pub async fn get_blog_list() -> Result, ServerFnError> { + let db = server_context() + .get::>() + .ok_or_else(|| ServerFnError::ServerError("Database context not available".to_string()))?; + + let summaries = db.get_tales_summary(&[]).await + .map_err(|e| ServerFnError::ServerError(e.to_string()))?; + + Ok(summaries) +} + +#[server] +pub async fn get_blog_post(slug: String) -> Result { + let db = server_context() + .get::>() + .ok_or_else(|| ServerFnError::ServerError("Database context not available".to_string()))?; + + let tale = db.get_tale_by_slug(&slug).await + .map_err(|e| ServerFnError::ServerError(e.to_string()))?; + + tale.ok_or(ServerFnError::ServerError("Post not found".into())) +}