From 606537a72fac152b113b5422ff723646f4dbb028 Mon Sep 17 00:00:00 2001 From: Folyd Date: Sun, 23 Apr 2023 21:02:06 +0800 Subject: [PATCH] Support `zine new --article` (#216) --- src/entity/zine.rs | 4 +++ src/main.rs | 19 ++++++++++-- src/new.rs | 74 ++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 85 insertions(+), 12 deletions(-) diff --git a/src/entity/zine.rs b/src/entity/zine.rs index 0a4665e..3087546 100644 --- a/src/entity/zine.rs +++ b/src/entity/zine.rs @@ -131,6 +131,10 @@ impl Zine { Ok(()) } + pub fn get_issue_by_number(&self, number: u32) -> Option<&Issue> { + self.issues.iter().find(|issue| issue.number == number) + } + // Get the article metadata list by author id, sorted by descending order of publishing date. fn get_articles_by_author(&self, author_id: &str) -> Vec { let mut items = self diff --git a/src/main.rs b/src/main.rs index 32d53df..313b736 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ -use anyhow::Result; +use anyhow::{bail, Result}; use clap::{Parser, Subcommand}; use zine::build::watch_build; -use zine::new::{new_zine_issue, new_zine_project}; +use zine::new::{new_article, new_zine_issue, new_zine_project}; use zine::serve::run_serve; use zine::{lint, Mode}; @@ -43,6 +43,9 @@ enum Commands { /// New issue. #[arg(short, long)] issue: bool, + /// New article. + #[arg(short, long)] + article: bool, }, /// Lint Zine project. Lint { @@ -73,9 +76,19 @@ async fn main() -> Result<()> { zine::set_current_mode(Mode::Serve); run_serve(source.as_deref().unwrap_or("."), port, open).await?; } - Commands::New { name, issue } => { + Commands::New { + name, + issue, + article, + } => { + if issue && article { + bail!("Can't create both issue and article at the same time.") + } + if issue { new_zine_issue()?; + } else if article { + new_article()?; } else { new_zine_project(name)? } diff --git a/src/new.rs b/src/new.rs index 0f00b88..1faee6c 100644 --- a/src/new.rs +++ b/src/new.rs @@ -1,11 +1,15 @@ -use std::{borrow::Cow, env, fs, path::PathBuf}; +use std::{borrow::Cow, env, fs, io::Write, path::PathBuf}; -use anyhow::{Context as _, Result}; +use anyhow::{Context as _, Ok, Result}; use minijinja::render; use promptly::prompt_default; -use time::{format_description, OffsetDateTime}; +use time::OffsetDateTime; -use crate::{helpers::run_command, ZINE_FILE}; +use crate::{ + entity::Zine, + helpers::{self, run_command}, + ZINE_FILE, +}; static TEMPLATE_PROJECT_FILE: &str = r#" [site] @@ -34,6 +38,18 @@ publish = true featured = true "#; +static TEMPLATE_ARTICLE: &str = r#" + +[[article]] +file = "{{ file }}" +title = "{{ title }}" +author = "{{ author | lower }}" +cover = "" +pub_date = "{{ pub_date }}" +publish = true +featured = true +"#; + struct ZineScaffold { source: PathBuf, author: String, @@ -62,8 +78,6 @@ impl ZineScaffold { .join(crate::ZINE_CONTENT_DIR) .join(self.issue_dir.as_ref()); fs::create_dir_all(&issue_dir)?; - let format = format_description::parse("[year]-[month]-[day]")?; - let today = OffsetDateTime::now_utc().format(&format)?; fs::write( issue_dir.join(ZINE_FILE), @@ -72,7 +86,7 @@ impl ZineScaffold { slug => self.issue_dir, number => self.issue_number, title => self.issue_title, - pub_date => today, + pub_date => helpers::format_date(&OffsetDateTime::now_utc().date()), author => self.author ), )?; @@ -117,13 +131,16 @@ pub fn new_zine_project(name: Option) -> Result<()> { Ok(()) } -pub fn new_zine_issue() -> Result<()> { +fn load_zine_project() -> Result<(PathBuf, Zine)> { // Use zine.toml to find root path let (source, mut zine) = crate::locate_root_zine_folder(env::current_dir()?)? .with_context(|| "Failed to find the root zine.toml file".to_string())?; zine.parse_issue_from_dir(&source)?; + Ok((source, zine)) +} - let author = git_user_name(); +pub fn new_zine_issue() -> Result<()> { + let (source, zine) = load_zine_project()?; let next_issue_number = zine.issues.len() + 1; let issue_dir = prompt_default( "What is your issue directory name?", @@ -135,6 +152,7 @@ pub fn new_zine_issue() -> Result<()> { format!("Issue {next_issue_number}"), )?; + let author = git_user_name(); let scaffold = ZineScaffold { source, author, @@ -146,6 +164,44 @@ pub fn new_zine_issue() -> Result<()> { Ok(()) } +pub fn new_article() -> Result<()> { + let (source, zine) = load_zine_project()?; + let latest_issue_number = zine.issues.len(); + let issue_number = prompt_default( + "Which Issue do you want create a new article?", + latest_issue_number, + )?; + if let Some(issue) = zine.get_issue_by_number(issue_number as u32) { + let article_file = prompt_default( + "What is your article file name?", + "new-article.md".to_owned(), + )?; + let title = prompt_default("What is your article title?", "New Article".to_owned())?; + let author = git_user_name(); + + let issue_dir = source.join(crate::ZINE_CONTENT_DIR).join(&issue.dir); + // Write article file + fs::write(issue_dir.join(&article_file), "Hello Zine")?; + + // Append article to issue zine.toml + let article_content = render!( + TEMPLATE_ARTICLE, + title, + author, + file => article_file, + pub_date => helpers::format_date(&OffsetDateTime::now_utc().date()), + ); + let mut issue_file = fs::OpenOptions::new() + .append(true) + .open(issue_dir.join(ZINE_FILE))?; + issue_file.write_all(article_content.as_bytes())?; + } else { + println!("Issue {} not found", issue_number); + } + + Ok(()) +} + fn git_user_name() -> String { run_command("git", &["config", "user.name"]) .ok()