From 1fee0775295b644c68f5464696ecc5ffaf3e2342 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 20 Mar 2018 16:57:51 -0600 Subject: [PATCH] feat: Rename subcommand Cobalt now has supprot for: `cobalt rename [--file ]` This will - Update the `title` in the frontmatter - Move the file to the `dest_path` (or cwd) We have all the information we need to help the user set up a redirection, so ideally we would. Unfortunately, that will require: 1. Deciding on an approach (alias vs file) 2. Refactoring the code necesary to make either work. This has now been split out into #403. Fixes #393 --- src/bin/cobalt/main.rs | 2 + src/bin/cobalt/new.rs | 118 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) diff --git a/src/bin/cobalt/main.rs b/src/bin/cobalt/main.rs index 8924eb72..e519a06e 100644 --- a/src/bin/cobalt/main.rs +++ b/src/bin/cobalt/main.rs @@ -43,6 +43,7 @@ fn run() -> Result<()> { .args(&args::get_logging_args()) .subcommand(new::init_command_args()) .subcommand(new::new_command_args()) + .subcommand(new::rename_command_args()) .subcommand(new::publish_command_args()) .subcommand(build::build_command_args()) .subcommand(build::clean_command_args()) @@ -64,6 +65,7 @@ fn run() -> Result<()> { match command { "init" => new::init_command(matches), "new" => new::new_command(matches), + "rename" => new::rename_command(matches), "publish" => new::publish_command(matches), "build" => build::build_command(matches), "clean" => build::clean_command(matches), diff --git a/src/bin/cobalt/new.rs b/src/bin/cobalt/new.rs index 27eda79e..056c5720 100644 --- a/src/bin/cobalt/new.rs +++ b/src/bin/cobalt/new.rs @@ -78,6 +78,52 @@ pub fn new_command(matches: &clap::ArgMatches) -> Result<()> { Ok(()) } +pub fn rename_command_args() -> clap::App<'static, 'static> { + clap::SubCommand::with_name("rename") + .about("Rename a document") + .args(&args::get_config_args()) + .arg( + clap::Arg::with_name("SRC") + .required(true) + .help("File to rename") + .takes_value(true), + ) + .arg( + clap::Arg::with_name("TITLE") + .required(true) + .help("Title of the post") + .takes_value(true), + ) + .arg( + clap::Arg::with_name("file") + .short("f") + .long("file") + .value_name("DIR_OR_FILE") + .help("New document's parent directory or file (default: `/title.ext`)") + .takes_value(true), + ) +} + +pub fn rename_command(matches: &clap::ArgMatches) -> Result<()> { + let config = args::get_config(matches)?; + let config = config.build()?; + + let source = path::PathBuf::from(matches.value_of("SRC").unwrap()); + + let title = matches.value_of("TITLE").unwrap(); + + let mut file = env::current_dir().expect("How does this fail?"); + if let Some(rel_file) = matches.value_of("file") { + file.push(path::Path::new(rel_file)) + } + let file = file; + + rename_document(&config, source, title, file) + .chain_err(|| format!("Could not rename `{}`", title))?; + + Ok(()) +} + pub fn publish_command_args() -> clap::App<'static, 'static> { clap::SubCommand::with_name("publish") .about("Publish a document") @@ -282,6 +328,78 @@ fn create_file_for_path(path: &path::Path, content: &str) -> Result<()> { Ok(()) } +pub fn rename_document( + config: &cobalt_model::Config, + source: path::PathBuf, + title: &str, + file: path::PathBuf, +) -> Result<()> { + let target = if file.extension().is_none() || file.is_dir() { + let extension = source.extension().and_then(|s| s.to_str()).unwrap_or("md"); + let file_name = format!("{}.{}", cobalt_model::slug::slugify(title), extension); + let mut file = file; + file.push(path::Path::new(&file_name)); + file + } else { + file + }; + + let doc = cobalt_model::files::read_file(&source)?; + let doc = cobalt_model::DocumentBuilder::::parse(&doc)?; + let (front, content) = doc.parts(); + + let pages = config.pages.clone().build()?; + let posts = config.posts.clone().build()?; + let full_front = if posts.pages.includes_file(&target) + || posts + .drafts + .map(|d| d.includes_file(&target)) + .unwrap_or_default() + { + // Can't rely on this for drafts atm + let rel_src = target + .strip_prefix(&config.source) + .expect("file was found under the root"); + front + .clone() + .merge_path(rel_src) + .merge(posts.default.clone()) + } else if pages.pages.includes_file(&target) + || pages + .drafts + .map(|d| d.includes_file(&target)) + .unwrap_or_default() + { + // Can't rely on this for drafts atm + let rel_src = target + .strip_prefix(&config.source) + .expect("file was found under the root"); + front + .clone() + .merge_path(rel_src) + .merge(pages.default.clone()) + } else { + bail!( + "Target file wouldn't be a member of any collection: {:?}", + target + ); + }; + let full_front = full_front.build()?; + + let new_front = front.set_title(Some(title.to_string())); + let doc = + cobalt_model::DocumentBuilder::::new(new_front, content); + let doc = doc.to_string(); + cobalt_model::files::write_document_file(doc, target)?; + + if !full_front.is_draft { + warn!("Renaming a published page might invalidate links"); + } + fs::remove_file(source)?; + + Ok(()) +} + pub fn publish_document(file: &path::Path) -> Result<()> { let doc = cobalt_model::files::read_file(file)?; let doc = cobalt_model::DocumentBuilder::::parse(&doc)?;