[#1] Blog metadata can be stored using serde.

Created the initial blog data, posts and authors, and allows it to
be stored using serde.
This commit is contained in:
2025-08-22 10:46:38 -04:00
parent 35772ba1c6
commit 9dd87d0e42
10 changed files with 339 additions and 22 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
# Added by cargo
*.swp
/target

142
Cargo.lock generated Normal file
View File

@ -0,0 +1,142 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
[[package]]
name = "indexmap"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "proc-macro2"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_spanned"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83"
dependencies = [
"serde",
]
[[package]]
name = "syn"
version = "2.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tavern"
version = "0.0.0"
dependencies = [
"serde",
"toml",
]
[[package]]
name = "toml"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"toml_parser",
"toml_writer",
"winnow",
]
[[package]]
name = "toml_datetime"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3"
dependencies = [
"serde",
]
[[package]]
name = "toml_parser"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10"
dependencies = [
"winnow",
]
[[package]]
name = "toml_writer"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64"
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "winnow"
version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95"

View File

@ -11,3 +11,5 @@ license = "Apache-2.0"
[dependencies]
serde = { version = "1.0.219", features = ["derive"] }
toml = "0.9.5"

View File

@ -1,8 +1,7 @@
# tavern
A blogging system that will allow you to write your blog in Markdown and then display it in HTML using Dioxus.
# Tavern
A blogging system that will allow you to write your blog in Markdown and then
display it in HTML using Dioxus.
---

32
src/adventurer.rs Normal file
View File

@ -0,0 +1,32 @@
use serde::{Deserialize, Serialize};
/// Represents an author or contributor of a tale.
///
/// An `Adventurer` contains identifying and descriptive information
/// such as their name, handle, profile URL, image, and a short blurb.
#[derive(Deserialize, Serialize)]
pub struct Adventurer
{
/// The full name of the adventurer.
pub name: String,
/// A unique handle or username for the adventurer (e.g., used in URLs or mentions).
pub handle: String,
/// A link to the adventurer's profile (e.g., personal website, GitHub, etc.).
pub profile: String,
/// A URL or path to an image representing the adventurer (e.g., avatar or portrait).
pub image: String,
/// A short descriptive text or tagline about the adventurer.
pub blurb: String
}
impl Adventurer
{
}

15
src/lib.rs Normal file
View File

@ -0,0 +1,15 @@
//! A blogging system that will allow you to write your blog in Markdown and then display it in HTML using Dioxus.
mod info;
mod adventurer;
mod tale;
mod tavern;
pub use crate::info::{get_name, get_version};
pub use crate::adventurer::Adventurer;
pub use crate::tale::Tale;
pub use crate::tavern::Tavern;

View File

@ -1,18 +0,0 @@
//! A blogging system that will allow you to write your blog in Markdown and then display it in HTML using Dioxus.
mod info;
/// Print the version.
fn print_version()
{
println!("{} v{}", info::get_name(), info::get_version());
}
fn main()
{
print_version();
}

40
src/tale.rs Normal file
View File

@ -0,0 +1,40 @@
use serde::{Deserialize, Serialize};
/// A type alias representing the path to a Markdown file.
/// This type is used to point to the location of the content of a `Tale`.
pub type Markdown = std::path::PathBuf;
/// Represents a post or story in the application.
///
/// A `Tale` contains metadata about the post such as its title, author,
/// and tags, along with a path to its Markdown content.
#[derive(Deserialize, Serialize)]
pub struct Tale
{
/// The title of the tale.
pub title: String,
/// A URL-friendly version of the title, used for routing and linking.
pub slug: String,
/// The name of the author who wrote the tale.
pub author: String,
/// A short summary or description of the tale.
pub summary: String,
/// A list of tags associated with the tale for categorization and searching.
pub tags: Vec<String>,
/// The file path to the Markdown content of the tale.
pub content: Markdown
}
impl Tale
{
}

20
src/tavern.rs Normal file
View File

@ -0,0 +1,20 @@
use serde::{Deserialize, Serialize};
use crate::adventurer::Adventurer;
use crate::tale::Tale;
/// Represents the entire blog or collection of content.
///
/// A `Tavern` contains a list of all tales (posts) and their respective authors.
/// It serves as the central structure for organizing and serializing the site's content.
#[derive(Deserialize, Serialize)]
pub struct Tavern
{
/// A list of all published tales (posts) in the tavern.
pub tales: Vec<Tale>,
/// A list of all adventurers (authors) who have contributed tales.
pub authors: Vec<Adventurer>
}

84
tests/serde.rs Normal file
View File

@ -0,0 +1,84 @@
use tavern::{Adventurer, Tale, Tavern};
fn generate_tavern() -> Tavern
{
let author: Adventurer = Adventurer
{
name: String::from("Jason Smith"),
handle: String::from("myrddin"),
profile: String::from("https://cybermages.tech/about/myrddin"),
image: String::from("https://cybermages.tech/about/myrddin/pic"),
blurb: String::from("I love code!")
};
let tale: Tale = Tale
{
title: String::from("Test post"),
slug: String::from("test_post"),
author: author.handle.clone(),
summary: String::from("The Moon is made of cheese!"),
tags: vec![String::from("Space"), String::from("Cheese")],
content: std::path::PathBuf::from("posts/test_post.md")
};
Tavern { tales: vec![tale], authors: vec![author]}
}
fn cleanup_temp_file(path: &std::path::PathBuf)
{
if path.exists()
{
let _ = std::fs::remove_file(path);
}
}
#[test]
fn to_file()
{
let tavern = generate_tavern();
let toml_string = toml::to_string_pretty(&tavern)
.expect("Serialization to TOML should succeed");
// Save the TOML to a temporary file.
let mut path = std::env::temp_dir();
path.push("tavern_test_out.toml");
std::fs::write(&path, &toml_string)
.expect("Failed to write TOML to file");
cleanup_temp_file(&path);
}
#[test]
fn from_file()
{
let tavern = generate_tavern();
let toml_string = toml::to_string_pretty(&tavern)
.expect("Serialization to TOML should succeed");
// Save the TOML to a temporary file.
let mut path = std::env::temp_dir();
path.push("tavern_test_in.toml");
std::fs::write(&path, &toml_string)
.expect("Failed to write TOML to file");
// Read the previously written TOML file
let toml_data = std::fs::read_to_string(&path)
.expect("Failed to read TOML file");
// Deserialize it
let tavern: Tavern = toml::from_str(&toml_data)
.expect("Failed to parse TOML");
// Assert some known values to make this a real test
let tale = &tavern.tales[0];
assert_eq!(tale.title, "Test post");
assert_eq!(tale.slug, "test_post");
assert_eq!(tale.author, "myrddin");
cleanup_temp_file(&path);
}