Skip to content

Commit

Permalink
Add interactive --series flag to cli
Browse files Browse the repository at this point in the history
  • Loading branch information
sneakycrow committed Oct 18, 2024
1 parent 0c55ccf commit 79ce6bd
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 17 deletions.
1 change: 1 addition & 0 deletions labs/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ edition = "2021"
[dependencies]
clap = { version = "4.4", features = ["derive"] }
chrono = "0.4"
dialoguer = "0.10.0"
67 changes: 50 additions & 17 deletions labs/cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
mod series;

use chrono::Local;
use clap::{Parser, Subcommand};
use dialoguer::{Input, Select};
use series::get_existing_series;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
Expand All @@ -19,13 +23,11 @@ enum Commands {
#[arg(short, long)]
category: Option<String>,
#[arg(short, long)]
series_key: Option<String>,
#[arg(short, long)]
series_pos: Option<i32>,
#[arg(short, long)]
summary: Option<String>,
#[arg(short, long, action)]
draft: bool,
#[arg(short, long, action)]
series: bool,
},
}

Expand All @@ -36,47 +38,78 @@ fn main() {
Commands::New {
title,
category,
series_key,
series_pos,
summary,
draft,
series,
} => {
create_new_post(title, category, series_key, series_pos, summary, draft);
create_new_post(title, category, summary, draft, series);
}
}
}

fn create_new_post(
title: &str,
category: &Option<String>,
series_key: &Option<String>,
series_pos: &Option<i32>,
summary: &Option<String>,
draft: &bool,
series: &bool,
) {
// Construct the date out of todays date
let date = Local::now().format("%Y-%m-%d").to_string();
// Create a dash separated slug out of the title
let slug = title.to_lowercase().replace(" ", "-");
// Check if we're posting a draft
let folder = if *draft { "_drafts" } else { "_posts" };
// Make sure the destination folder is available
std::fs::create_dir_all(folder).expect("Unable to create folder");
// Construct the markdown file with the folder, date, and slug
let filename = format!("{}/{}-{}.md", folder, date, slug);

// Initialize the frontmatter YAML
let mut content = String::new();
// It must start and end with `---` to be valid
content.push_str("---\n");
// Push all provided properties to the YAML
content.push_str(&format!("title: \"{}\"\n", title));
if let Some(cat) = category {
content.push_str(&format!("category: \"{}\"\n", cat));
}
if let Some(key) = series_key {
content.push_str(&format!("series_key: \"{}\"\n", key));
}
if let Some(pos) = series_pos {
content.push_str(&format!("series_pos: {}\n", pos));
}
if let Some(sum) = summary {
content.push_str(&format!("summary: \"{}\"\n", sum));
}
// If the series flag is set, parse the available series from the existing posts
if *series {
// Get all of our existing series in _posts/
let existing_series = get_existing_series();
// Convert them to options in addition to a "new" option
let mut series_options: Vec<String> = existing_series.into_iter().collect();
series_options.push("New series".to_string());
// Ask the user which series they want
let selection = Select::new()
.with_prompt("Select a series or create a new one")
.items(&series_options)
.default(0)
.interact()
.unwrap();
// If they selected a "new" option, ask for a new key, otherwise use the existing key
let series_key = if selection == series_options.len() - 1 {
Input::new()
.with_prompt("Enter new series key")
.interact_text()
.unwrap()
} else {
series_options[selection].clone()
};
// Push the key and the position of the article up
content.push_str(&format!("series_key: \"{}\"\n", series_key));
// TODO: Have the default be the next position in the series
let series_pos: i32 = Input::new()
.with_prompt("Enter series position")
.interact_text()
.unwrap();
content.push_str(&format!("series_pos: {}\n", series_pos));
}
// Close out the frontmatter YAML
content.push_str("---\n\n");

let path = PathBuf::from(&filename);
let mut file = File::create(path).expect("Unable to create file");
file.write_all(content.as_bytes())
Expand Down
26 changes: 26 additions & 0 deletions labs/cli/src/series.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use std::collections::HashSet;
use std::fs;
use std::path::Path;

/// Gets all existing series from the _posts directory based on their frontmatter
// TODO: Make this work for drafts as well
// NOTE: Drafts may want to be part of a published series in _posts, so this requires some thought
pub(crate) fn get_existing_series() -> HashSet<String> {
let mut series_keys = HashSet::new();
let posts_dir = Path::new("_posts");

if let Ok(entries) = fs::read_dir(posts_dir) {
for entry in entries.flatten() {
if let Ok(content) = fs::read_to_string(entry.path()) {
if let Some(start) = content.find("series_key:") {
if let Some(end) = content[start..].find('\n') {
let series_key = content[start + 11..start + end].trim().trim_matches('"');
series_keys.insert(series_key.to_string());
}
}
}
}
}

series_keys
}

0 comments on commit 79ce6bd

Please sign in to comment.