Skip to content

Commit

Permalink
feat(app): resource layout into app (#1)
Browse files Browse the repository at this point in the history
* feat(app): resource layout into app

* ci(main): add build and clippy checks

* chore(gh): add PR template

* chore(blog): rename templates to blog

* feat(manifest): add manifest to README

* docs(README): format toml

* ci(typos): add typos check
  • Loading branch information
clearloop authored Dec 30, 2023
1 parent dd2e9cd commit bac5f71
Show file tree
Hide file tree
Showing 19 changed files with 361 additions and 25 deletions.
6 changes: 6 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Resolves #

### Changes

-
-
38 changes: 38 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]

concurrency:
group: ${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@nextest
- uses: foundry-rs/foundry-toolchain@v1

- name: Run Tests
run: cargo nextest run --no-fail-fast --release

check:
name: Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Format
run: cargo fmt --check
- name: Clippy
run: cargo clippy --all -- -D warnings
18 changes: 18 additions & 0 deletions .github/workflows/typos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Typos

on:
push:
branches: [main]
pull_request:
branches: [main]

env:
TERM: xterm-256color

jobs:
check:
name: Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: crate-ci/typos@v1.16.25
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@

The static site generator in rust.

```toml
# my-blog/cydonia.toml
name = "Cydonia" # The name of the site.

# Default values of the optional fields.
# --------------------------------------

favicon = "favicon.ico" # The path to the favicon.ico.
posts = "posts" # The path to the posts.
templates = "templates" # The path to the templates.

# Theme could also be a folder:
#
# - [theme]
# - index.css (required)
# - post.css (required)
# - theme.css (optional)
theme = "theme.css"
```

## LICENSE

GPL-3.0-only
18 changes: 17 additions & 1 deletion blog/cydonia.toml
Original file line number Diff line number Diff line change
@@ -1 +1,17 @@
name = "Cydonia"
# my-blog/cydonia.toml
name = "Cydonia" # The name of the site.

# Default values of the optional fields.
# --------------------------------------

favicon = "favicon.ico" # The path to the favicon.ico.
posts = "posts" # The path to the posts.
templates = "templates" # The path to the templates.

# Theme could also be a folder:
#
# - [theme]
# - index.css (required)
# - post.css (required)
# - theme.css (optional)
theme = "theme.css"
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
32 changes: 32 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//! # App layout
//!
//! ```text
//! - blog
//! - posts
//! - 2023-12-29-foo-bar.md
//! - cydonia.toml
//! - theme [ theme.css ]
//! - index.css
//! - post.css
//! - theme.css
//! ```
use crate::Manifest;
use anyhow::Result;
use std::path::PathBuf;

/// The root of the site.
pub struct App {
/// The root path of the resources.
pub root: PathBuf,
/// The manifest of the site.
pub manifest: Manifest,
}

impl App {
/// Create a new app.
pub fn new(root: PathBuf) -> Result<Self> {
let manifest = Manifest::load(&root)?;
Ok(Self { root, manifest })
}
}
17 changes: 0 additions & 17 deletions src/layout.rs

This file was deleted.

9 changes: 6 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
//! The static site genrator.
//! The static site generator.
mod layout;
mod app;
mod manifest;
mod post;
mod theme;
mod utils;

pub use self::{
layout::Layout,
app::App,
manifest::Manifest,
post::{Meta, Post},
theme::Theme,
};
78 changes: 76 additions & 2 deletions src/manifest.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,84 @@
//! Manifest of the site.
use serde::Deserialize;
use crate::{utils::Read, Post};
use anyhow::Result;
use handlebars::Handlebars;
use serde::{Deserialize, Serialize};
use std::{
fs,
path::{Path, PathBuf},
};

/// Manifest of the site.
#[derive(Debug, Clone, Deserialize)]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Manifest {
/// The name of the site.
pub name: String,

/// The path to the favicon.
favicon: Option<PathBuf>,

/// The path of the posts.
#[serde(default = "Manifest::default_posts")]
pub posts: PathBuf,

/// The path of the templates.
#[serde(default = "Manifest::default_templates")]
pub templates: PathBuf,

/// The path of the theme.
///
/// Could be a file or a directory.
#[serde(default = "Manifest::default_theme")]
pub theme: PathBuf,
}

impl Manifest {
/// Load manifest from the provided path.
pub fn load(root: impl AsRef<Path>) -> Result<Self> {
let path = root.as_ref().join("cydonia.toml");
let mut manifest: Self = toml::from_str(&root.as_ref().join("cydonia.toml").read()?)
.map_err(|e| anyhow::anyhow!("Failed to parse {}: {}", path.display(), e))?;

if manifest.posts.is_relative() {
manifest.posts = root.as_ref().join(&manifest.posts);
}

if manifest.templates.is_relative() {
manifest.templates = root.as_ref().join(&manifest.templates);
}

Ok(manifest)
}

/// Get the posts.
pub fn posts(&self) -> Result<Vec<Post>> {
fs::read_dir(&self.posts)?
.map(|e| Post::load(e?.path()))
.collect()
}

/// Resource the templates into handlebars.
pub fn handlebars<'r>(&self) -> Result<Handlebars<'r>> {
let mut handlebars = Handlebars::new();
handlebars.set_strict_mode(true);
handlebars.register_templates_directory(".hbs", &self.templates)?;

Ok(handlebars)
}

/// Default implementation of the posts.
pub fn default_posts() -> PathBuf {
fs::canonicalize(PathBuf::from("posts")).unwrap_or_default()
}

/// Default implementation of the templates.
pub fn default_templates() -> PathBuf {
fs::canonicalize(PathBuf::from("templates")).unwrap_or_default()
}

/// Default implementation of the templates.
pub fn default_theme() -> PathBuf {
fs::canonicalize(PathBuf::from("theme.css")).unwrap_or_default()
}
}
50 changes: 48 additions & 2 deletions src/post.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
//! post layout.
use anyhow::Result;
use serde::Deserialize;
use std::{fs, path::Path, str::FromStr};

/// The metadata of the post.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Deserialize)]
pub struct Meta {
/// The author of the post.
pub author: String,
Expand All @@ -15,11 +19,53 @@ pub struct Meta {
pub title: String,
}

/// Post layout.
impl FromStr for Meta {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self> {
serde_yaml::from_str(s).map_err(|e| anyhow::anyhow!(e))
}
}

/// Post layout with is markdown with yaml metadata.
#[derive(Clone, Debug)]
pub struct Post {
/// The metadata of the post.
pub meta: Meta,
/// The content of the post in markdown.
pub content: String,
}

impl Post {
/// Load post from path.
pub fn load(path: impl AsRef<Path>) -> Result<Self> {
fs::read_to_string(path.as_ref())
.map_err(|e| {
anyhow::anyhow!(
"failed to read post from {}: {}",
path.as_ref().display(),
e
)
})?
.parse()
}
}

impl FromStr for Post {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self> {
let markdown = s.splitn(3, "---").collect::<Vec<_>>();
if markdown.len() != 3 {
return Err(anyhow::anyhow!(
"yaml meta not found, see {} for the template.",
"https://github.com/clearloop/cydonia"
));
}

let meta = markdown[1].parse::<Meta>()?;
let content = markdown[2].to_string();

Ok(Self { meta, content })
}
}
34 changes: 34 additions & 0 deletions src/theme.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//! The Theme for the site.
use crate::utils::Read;
use anyhow::Result;
use std::path::PathBuf;

/// The theme for the site.
#[derive(Debug, Clone)]
pub struct Theme {
/// Styles for the index page.
pub index: String,
/// Styles for the post page.
pub post: String,
}

impl Theme {
/// Loads theme from the given path.
pub fn load(path: PathBuf) -> Result<Self> {
if path.is_file() {
let theme = path.read()?;

Ok(Self {
index: theme.clone(),
post: theme,
})
} else {
let theme = path.join("theme.css").read().unwrap_or_default();

let index = [theme.clone(), path.join("index.css").read()?].concat();
let post = [theme, path.join("post.css").read()?].concat();
Ok(Self { index, post })
}
}
}
26 changes: 26 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//! cydonia utils.
use anyhow::Result;
use colored::Colorize;
use std::path::Path;

/// A trait for reading file with full error info.
pub trait Read {
fn read(&self) -> Result<String>;
}

impl<P> Read for P
where
P: AsRef<Path>,
{
fn read(&self) -> Result<String> {
let path = self.as_ref();
std::fs::read_to_string(path).map_err(|e| {
anyhow::anyhow!(
"Failed to read file: {}, error: {}",
path.display().to_string().dimmed().underline(),
e.to_string()
)
})
}
}
Loading

0 comments on commit bac5f71

Please sign in to comment.