intermodal/bin/gen/src/subcommand.rs
Casey Rodarmor e7872f56f2
Move all output from bin/gen to target/gen
To make it clearer what is and isn't generated content, make gen place
all generated output in `target/gen`.

Also, try to make the readme clearer about the location of build
artifacts.

type: development
2020-05-01 00:29:40 -07:00

241 lines
6.1 KiB
Rust

use crate::common::*;
#[derive(StructOpt)]
pub(crate) enum Subcommand {
#[structopt(about("Update all generated docs"))]
All,
#[structopt(about("Generate book"))]
Book,
#[structopt(about("Generate the changelog"))]
Changelog,
#[structopt(about("Print a commit template to standard output"))]
CommitTemplate,
#[structopt(about("Print possible values for `type` field of commit metadata"))]
CommitTypes,
#[structopt(about("Generate completion scripts"))]
CompletionScripts,
#[structopt(about("Diff generated content between commits"))]
Diff,
#[structopt(about("Generate readme"))]
Readme,
#[structopt(about("Generate man pages"))]
Man,
}
#[throws]
fn blank(path: impl AsRef<Path>, title: &str) {
let path = path.as_ref();
info!("Writing blank page to `{}`…", path.display());
let text = format!(
"
# {}
This page intentionally left blank.
",
title
);
fs::write(&path, text).context(error::Filesystem { path })?;
}
#[throws]
fn clean_dir(path: impl AsRef<Path>) {
let path = path.as_ref();
info!("Cleaning `{}`…", path.display());
if path.is_dir() {
fs::remove_dir_all(path).context(error::Filesystem { path: &path })?;
}
fs::create_dir_all(path).context(error::Filesystem { path: &path })?;
}
impl Subcommand {
#[throws]
pub(crate) fn run(self, options: Options) {
let project = Project::load(&options)?;
match self {
Self::Changelog => Self::changelog(&project)?,
Self::CommitTemplate => {
println!("{}", Metadata::default().to_string());
}
Self::CommitTypes => {
for kind in Kind::VARIANTS {
println!("{}", kind)
}
}
Self::CompletionScripts => Self::completion_scripts(&project)?,
Self::Readme => Self::readme(&project)?,
Self::Book => Self::book(&project)?,
Self::Man => Self::man(&project)?,
Self::Diff => Self::diff(&project)?,
Self::All => Self::all(&project)?,
}
}
#[throws]
pub(crate) fn all(project: &Project) {
Self::changelog(&project)?;
Self::completion_scripts(&project)?;
Self::readme(&project)?;
Self::book(&project)?;
Self::man(&project)?;
}
#[throws]
pub(crate) fn changelog(project: &Project) {
info!("Generating changelog…");
let changelog = Changelog::new(&project)?;
let path = project.gen()?.join("CHANGELOG.md");
fs::write(&path, changelog.render(false)?).context(error::Filesystem { path })?;
}
#[throws]
pub(crate) fn completion_scripts(project: &Project) {
info!("Generating completion scripts…");
let completions = project.gen()?.join("completions");
clean_dir(&completions)?;
cmd!(&project.executable, "completions", "--dir", completions).status_into_result()?
}
#[throws]
pub(crate) fn diff(project: &Project) {
let tmp = tempfile::tempdir().context(error::Tempdir)?;
let gen = |name: &str| -> Result<(), Error> {
let src = Path::new("target/gen");
fs::remove_dir_all(src).context(error::Filesystem { path: src })?;
cmd!("cargo", "run", "--package", "gen", "all").status_into_result()?;
let dir = tmp.path().join(name);
fs::create_dir(&dir).context(error::Filesystem { path: &dir })?;
fs_extra::dir::copy(src, &dir, &fs_extra::dir::CopyOptions::new())
.context(error::FilesystemRecursiveCopy { src, dst: dir })?;
Ok(())
};
const HEAD: &str = "HEAD";
gen(HEAD)?;
let head = project.repo.head()?;
let head_commit = head.peel_to_commit()?;
let parent = head_commit.parent(0)?;
let parent_hash = parent.id().to_string();
cmd!("git", "checkout", &parent_hash).status_into_result()?;
gen(&parent_hash)?;
cmd!("colordiff", "-ur", parent_hash, HEAD)
.current_dir(tmp.path())
.status_into_result()
.ok();
cmd!(
"git",
"checkout",
head
.shorthand()
.map(str::to_owned)
.unwrap_or_else(|| head_commit.id().to_string())
)
.status_into_result()?;
}
#[throws]
pub(crate) fn readme(project: &Project) {
info!("Generating readme…");
let template = project.root.join("bin/gen/templates/README.md");
let readme = Readme::load(&project.config, &template)?;
let text = readme.render_newline()?;
let path = project.gen()?.join("README.md");
fs::write(&path, &text).context(error::Filesystem { path })?;
let path = project.root.join("README.md");
fs::write(&path, &text).context(error::Filesystem { path })?;
}
#[throws]
pub(crate) fn book(project: &Project) {
info!("Generating book…");
let gen = project.gen()?;
let out = gen.join("book");
fs::create_dir_all(&out).context(error::Filesystem { path: &out })?;
blank(out.join("commands.md"), "Commands")?;
blank(out.join("bittorrent.md"), "BitTorrent")?;
blank(out.join("references.md"), "References")?;
let commands = out.join("commands");
clean_dir(&commands)?;
for subcommand in &project.bin.subcommands {
let page = subcommand.page()?;
let dst = commands.join(format!("{}.md", subcommand.slug()));
fs::write(&dst, page).context(error::Filesystem { path: dst })?;
}
clean_dir(&out.join("references"))?;
for section in &project.config.references {
section.render_to(out.join(section.path()))?;
}
Faq::new(&project.config.faq).render_to(out.join("faq.md"))?;
Summary::new(project).render_to(out.join("SUMMARY.md"))?;
Introduction::new(&project.config).render_to(out.join("introduction.md"))?;
let changelog = Changelog::new(&project)?;
let dst = out.join("changelog.md");
fs::write(&dst, changelog.render(true)?).context(error::Filesystem { path: dst })?;
}
#[throws]
pub(crate) fn man(project: &Project) {
info!("Generating man pages…");
let mans = project.gen()?.join("man");
clean_dir(&mans)?;
for subcommand in &project.bin.subcommands {
let man = subcommand.man()?;
let dst = mans.join(format!("{}.1", subcommand.slug()));
info!("Writing man page to `{}`", dst.display());
fs::write(&dst, man).context(error::Filesystem { path: dst })?;
}
}
}