pub mod commands;
pub mod templates;
use std::{fs, path::PathBuf};
use clap::{Parser, Subcommand};
use commands::{compile::Compile, init::Init, new_post::NewPost};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum BlogError {
#[error("IO Error")]
Io {
error: std::io::Error,
message: String,
},
#[error("Invalid Front Matter: {0}")]
InvalidFrontMatter(String),
#[error("Invalid File Name: {0}")]
InvalidFileName(String),
#[error("Invalid File Extension: {0}")]
SimpleIoError(#[from] std::io::Error),
}
impl BlogError {
pub fn io(error: std::io::Error, message: String) -> Self {
BlogError::Io { error, message }
}
}
type BlogResult<T> = Result<T, BlogError>;
pub trait Run {
fn run(&self) -> BlogResult<()>;
}
#[derive(Debug, Parser)] #[command(name = "blog-rs", version, author, about = "A blog CLI")]
#[command(about = "Blogging Tool", long_about = None)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}
impl Cli {
pub fn run(&self) -> BlogResult<()> {
self.command.run()
}
}
#[derive(Debug, Subcommand)]
pub enum Commands {
#[command(about = "Compile the blog")]
Compile,
#[command(about = "Initialize a new blog")]
Init { path: String },
#[command(about = "Create a new post")]
Post { title: String },
}
impl Commands {
pub fn run(&self) -> BlogResult<()> {
match self {
Commands::Compile => Compile.run(),
Commands::Init { path } => Init.run(path),
Commands::Post { title } => NewPost.run(title),
}
}
}
#[derive(Debug, Default)]
pub struct Config {
posts: Vec<PathBuf>,
output: PathBuf,
}
impl Config {
pub fn load() -> BlogResult<Self> {
let dir = fs::read_dir("./posts")
.map_err(|e| BlogError::io(e, "Unable to read posts directory".to_string()))?;
let legible_posts = dir
.filter_map(|entry| {
let entry = entry.ok()?;
let path = entry.path();
if path.is_file() {
Some(path)
} else {
None
}
})
.collect();
Ok(Self {
posts: legible_posts,
output: PathBuf::from("./static"),
})
}
}