this is add_post cli implementation in rust

[dependencies]
chrono = "0.4.26"
dirs = "5.0.1"
doe = "0.1.64"
tsu = "1.0.1"
#[allow(warnings)]
pub mod cli {
use doe::traits::{DebugPrint, Print, Str};
use doe::*;
use std::collections::BTreeMap;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::{mpsc, Arc, Mutex};
use std::{fs, thread};
//path of the config file
pub fn config_file_path() -> PathBuf {
let mut development = false;
if development {
let path = PathBuf::from_str("./config.toml").unwrap();
path
} else {
let config_dir = dirs::config_dir().expect("get_config_dir fail");
let config_file_path = config_dir.join("mdbook-post/config.toml");
config_file_path
}
}
// the config file exists or not
pub fn is_config_available() -> bool {
return Path::new(&config_file_path()).exists();
}
//if the config file not exists then create it
pub fn create_config() {
if !config_file_path().exists() {
// Create the directory structure if it doesn't already exist
if let Some(parent_dir) = config_file_path().parent() {
fs::create_dir_all(parent_dir).expect("Failed to create config directory");
}
// Write the default configuration content to the file
let home_dir = dirs::home_dir().expect("get home_dir fail");
let blog_dir_path = home_dir.join("code/gitee/blog");
let default_config = include_str!("../config.toml")
.format(targets! {"{{blog_dir}}"=>format!("{}",blog_dir_path.to_string_lossy())});
fs::write(&config_file_path(), default_config.trim()).is_ok_and(|_| {
println!("{} created", config_file_path().display());
true
});
}
}
pub fn edit_config(blog_dir: &str) {
if !config_file_path().exists() {
// Create the directory structure if it doesn't already exist
if let Some(parent_dir) = config_file_path().parent() {
fs::create_dir_all(parent_dir).expect("Failed to create config directory");
}
// Write the default configuration content to the file
let default_config = format!("[mdbook-post]\nblog_dir = {:?}", blog_dir);
fs::write(&config_file_path(), default_config.trim())
.expect("Failed to write config file");
}
}
//if config file exists read the config file
pub fn read_config() -> Option<String> {
if config_file_path().exists() {
// Read the content of the configuration file
let config_content =
fs::read_to_string(&config_file_path()).expect("Failed to read config file");
return Some(config_content);
}
None
}
//remove the config file
pub fn remove_config() {
let config_dir = dirs::config_dir().expect("get_config_dir failed");
let config_file_path = config_dir.join("mdbook-post/config.toml");
fs::remove_file(config_file_path.clone()).is_ok_and(|_| {
println!("{} removed", config_file_path.display());
true
});
}
//parse get blog_dir
pub fn parse_toml_config_get_blog_dir(config: &str) -> String {
let config = tsu::toml_from_str(config);
let mdbook_post = config.get("mdbook-post").unwrap();
let name = mdbook_post.get("blog_dir").unwrap();
name.as_str()
.expect("get blog_dir from config error")
.to_string()
}
//parse get blog_dir
pub fn parse_toml_config_get_post_to_path(config: &str) -> BTreeMap<String, String> {
let mut map: BTreeMap<String, String> = btreemap!();
let post_config = tsu::toml_from_str(config);
if let Some(post_to_path) = post_config.get("post-to-path").to_owned().clone() {
post_to_path
.as_table()
.unwrap()
.into_iter()
.for_each(|(k, v)| {
map.insert(k.to_string(), v.as_str().unwrap().to_string());
});
}
map
}
pub fn run() {}
pub fn show_catogorylist() {
if let Some(config) = read_config() {
let blog_dir = PathBuf::from_str(&parse_toml_config_get_blog_dir(&config))
.expect("get blog_dir from config error");
if blog_dir.is_dir() {
let summary = fs::read_to_string(&blog_dir.join("./src/SUMMARY.md")).unwrap();
from_summary_file_get_categorylist(summary)
.into_iter()
.map(|(i, s)| s)
.collect::<Vec<_>>()
.join("   ")
.println();
} else {
println!("{} do't exists", blog_dir.display());
}
}
}
pub fn category_to_path(category: &str) -> Option<String> {
if let Some(config) = read_config() {
let map = parse_toml_config_get_post_to_path(&config);
return Some(map.get(category).unwrap().to_string());
}
return None;
}
fn title_to_markdown(category: &str) -> String {
category
.to_lowercase()
.replace("-", "_")
.replace(" ", "_")
.replace("。", ".")
.replace("(", "(")
.replace(")", ")")
.to_string()
}
pub fn get_category_list(category: &str) {
if let Some(config) = read_config() {
let blog_dir = PathBuf::from_str(&parse_toml_config_get_blog_dir(&config))
.expect("get blog_dir from config error");
if blog_dir.is_dir() {
let summary = fs::read_to_string(&blog_dir.join("./src/SUMMARY.md")).unwrap();
let mut summary_vec = summary
.split("\n")
.filter(|s| !s.is_empty())
.map(|s| s.to_string())
.collect::<Vec<_>>();
let category_vec = from_summary_file_get_categorylist(summary);
let index = category_vec
.clone()
.into_iter()
.enumerate()
.filter(|(i, s)| s.1 == category.to_string())
.nth(0)
.expect("get category index error");
if category_vec.len() > index.0 + 1 {
let index_1 = index.clone().1;
let index_2 = category_vec.clone().get(index.0 + 1).unwrap().to_owned();
for i in index_1.0..index_2.0 {
summary_vec[i]
.replace(
"./posts/",
&blog_dir.join("./src/posts/").display().to_string(),
)
.println();
}
} else {
let index_1 = index.clone().1;
for i in index_1.0..summary_vec.len() {
summary_vec[i]
.replace(
"./posts/",
&blog_dir.join("./src/posts/").display().to_string(),
)
.println();
}
}
} else {
println!("{} do't exists", blog_dir.display());
}
}
}
pub fn create_post(category: &str, title: &str) {
if let Some(config) = read_config() {
let blog_dir = PathBuf::from_str(&parse_toml_config_get_blog_dir(&config))
.expect("get blog_dir from config error");
if blog_dir.is_dir() {
let summary = fs::read_to_string(&blog_dir.join("./src/SUMMARY.md")).unwrap();
let mut summary_vec = summary
.split("\n")
.filter(|s| !s.is_empty())
.map(|s| s.to_string())
.collect::<Vec<_>>();
let category_vec = from_summary_file_get_categorylist(summary);
let index = category_vec
.clone()
.into_iter()
.enumerate()
.filter(|(i, s)| s.1 == category.to_string())
.nth(0)
.expect("get category index error");
if category_vec.len() > index.0 + 1 {
let index_1 = index.clone().1;
let index_2 = category_vec.clone().get(index.0 + 1).unwrap().to_owned();
summary_vec.insert(
index_2.0,
format!(
"    - [{}](./posts/{}/{}.md)",
title,
category_to_path(category).unwrap_or_else(|| format!("post")),
title_to_markdown(title)
),
);
} else {
summary_vec.push(format!(
"    - [{}](./posts/{}/{}.md)",
title,
category_to_path(category).unwrap_or_else(|| format!("post")),
title_to_markdown(title)
));
}
use chrono::format::strftime::StrftimeItems;
use chrono::Utc;
let current_date = Utc::today();
let formatted_date = current_date.format("%Y-%m-%d").to_string();
let markdown_content = include_str!("../defalut_markdown_content.md")
.format(targets! {"{{title}}"=>title.to_string(),"{{date}}"=>formatted_date});
fs::write(
&blog_dir.join(format!(
"./src/posts/{}/{}.md",
category_to_path(category).unwrap_or_else(|| format!("post")),
title_to_markdown(title)
)),
markdown_content,
)
.unwrap();
fs::write(&blog_dir.join("./src/SUMMARY.md"), summary_vec.join("\n")).unwrap();
blog_dir
.join(format!(
"./src/posts/{}/{}.md",
category_to_path(category).unwrap_or_else(|| format!("post")),
title_to_markdown(title)
))
.display()
.println();
} else {
println!("{} do't exists", blog_dir.display());
}
}
}
pub fn from_summary_file_get_categorylist(summary: String) -> Vec<(usize, String)> {
let summary_vec = summary
.split("\n")
.filter(|s| !s.is_empty())
.map(|s| s.to_string())
.collect::<Vec<_>>();
let v = summary_vec
.clone()
.into_iter()
.filter(|s| s.contains("- "))
.collect::<Vec<_>>();
let v_title = summary_vec
.clone()
.into_iter()
.filter(|s| s.contains("-"))
.into_iter()
.enumerate()
.map(|(i, s)| {
if s.starts_with("- [") {
return Some((
i + 2,
split_to_vec!(split_to_vec!(s, "[").get(1).unwrap(), "]")
.get(0)
.unwrap()
.to_string(),
));
}
None
})
.filter(|s| s.is_some())
.map(|s| s.unwrap())
.collect::<Vec<_>>();
v_title
}
pub fn display_help() {
println!("mdbook-post");
println!("Example:");
println!("mdbook-post <-c || --config> show  => show cli config");
println!("mdbook-post <-c || --config> remove => remove cli config");
println!("mdbook-post <-c || --config> edit /home/andrew/code/gitee/blog =>set blog path");
println!("mdbook-post <-l || --catogorylist>  => show category list");
println!("mdbook-post <-l Rust || --catogorylist Rust>  => show all list of Rust");
println!("mdbook-post <-n || --new> Rust 'implement todo CLI'  => add new post");
}
pub fn cli() {
let t1 = thread::spawn({
move || {
if is_config_available() {
let args = args!();
let first_arg = args
.clone()
.into_iter()
.nth(0)
.map_or_else(|| String::from(""), |arg| arg.to_string());
if args.len() > 0 {
if &first_arg == "--config" || &first_arg == "-c" {
let second_arg = args
.clone()
.into_iter()
.nth(1)
.map_or_else(|| String::from(""), |arg| arg.to_string());
if second_arg == "show" {
println!(
"config path: {}",
config_file_path().display().to_string()
);
println!("");
fs::read_to_string(config_file_path()).unwrap().println();
}
if second_arg == "remove" {
remove_config();
}
if second_arg == "edit" {
let path = args
.clone()
.into_iter()
.nth(2)
.expect("edit path must be specified");
edit_config(&path);
}
} else if &first_arg == "--catogorylist" || &first_arg == "-l" {
if args.len() == 1 {
show_catogorylist();
} else {
let title = args
.clone()
.into_iter()
.nth(1)
.map_or_else(|| String::from(""), |arg| arg.to_string());
if !title.is_empty() {
get_category_list(&title);
}
}
} else if &first_arg == "--new" || &first_arg == "-n" {
let title = args
.clone()
.into_iter()
.nth(1)
.map_or_else(|| String::from(""), |arg| arg.to_string());
let content_name = args
.clone()
.into_iter()
.nth(2)
.map_or_else(|| String::from(""), |arg| arg.to_string());
if title.is_empty() && content_name.is_empty() {
display_help();
} else {
create_post(&title, &content_name);
}
} else if &first_arg == "--help" || &first_arg == "-h" {
display_help();
}
} else {
display_help();
}
} else {
create_config();
}
}
});
t1.join().unwrap();
}
}
fn main() {
cli::cli();
}