Skip to content

Commit

Permalink
basic new
Browse files Browse the repository at this point in the history
  • Loading branch information
joshrotenberg committed Feb 25, 2024
1 parent 9338e22 commit 6836618
Show file tree
Hide file tree
Showing 11 changed files with 244 additions and 119 deletions.
1 change: 0 additions & 1 deletion .adr-dir

This file was deleted.

1 change: 0 additions & 1 deletion doc/adr/.adr-dir

This file was deleted.

19 changes: 0 additions & 19 deletions doc/adr/0001-record-architecture-decisions.md

This file was deleted.

19 changes: 0 additions & 19 deletions doc/adr/0002-record-architecture-decisions.md

This file was deleted.

19 changes: 0 additions & 19 deletions doc/adr/0003-record-architecture-decisions.md

This file was deleted.

16 changes: 8 additions & 8 deletions src/adr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ use tinytemplate::TinyTemplate;
static TEMPLATE: &str = include_str!("../templates/nygard.md");

#[derive(Debug, Serialize)]
struct AdrContext {
title: String,
number: i32,
date: String,
status: String,
context: String,
decision: String,
consequences: String,
pub struct AdrContext {
pub title: String,
pub number: i32,
pub date: String,
pub status: String,
pub context: String,
pub decision: String,
pub consequences: String,
}

pub struct AdrBuilder {
Expand Down
56 changes: 56 additions & 0 deletions src/init.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use std::{fs::create_dir_all, path::PathBuf};

use anyhow::Result;
use clap::Args;
use serde::Serialize;
use tinytemplate::TinyTemplate;

use crate::{adr_filename, next_adr_sequence, now};

static INIT_TEMPLATE: &str = include_str!("../templates/nygard/init.md");

#[derive(Debug, Args)]
#[command(version, about, long_about = None)]
pub(crate) struct InitArgs {
/// Directory to initialize
#[arg(default_value = "doc/adr")]
pub(crate) directory: PathBuf,
}

#[derive(Debug, Serialize)]
struct InitAdrContext {
number: i32,
date: String,
}

pub(crate) fn run(args: &InitArgs) -> Result<()> {
create_dir_all(&args.directory)?;

let filename = format!(
"{}/{:0>4}-{}.md",
args.directory.to_str().unwrap(),
next_adr_sequence(&args.directory)?,
adr_filename("Record architecture decisions")
);

let init_context = InitAdrContext {
number: next_adr_sequence(&args.directory)?,
date: now()?,
};

let mut tt = TinyTemplate::new();
tt.add_template("init_adr", INIT_TEMPLATE)?;
let rendered = tt.render("init_adr", &init_context)?;
std::fs::write(&filename, rendered)?;

tracing::debug!("Created {}", filename);

std::fs::write(
std::env::current_dir()?.join(".adr-dir"),
args.directory.to_str().unwrap(),
)?;

tracing::debug!("Wrote .adr-dir");

Ok(())
}
133 changes: 81 additions & 52 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
use anyhow::Result;
use clap::{Parser, Subcommand};
use serde::Serialize;
use std::{fs::create_dir_all, path::PathBuf};
use std::{
fs::create_dir_all,

Check warning on line 5 in src/main.rs

View workflow job for this annotation

GitHub Actions / Check

unused import: `fs::create_dir_all`

Check warning on line 5 in src/main.rs

View workflow job for this annotation

GitHub Actions / Check

unused import: `fs::create_dir_all`

Check failure on line 5 in src/main.rs

View workflow job for this annotation

GitHub Actions / Clippy

unused import: `fs::create_dir_all`

Check failure on line 5 in src/main.rs

View workflow job for this annotation

GitHub Actions / Clippy

unused import: `fs::create_dir_all`

Check warning on line 5 in src/main.rs

View workflow job for this annotation

GitHub Actions / Test Suite

unused import: `fs::create_dir_all`

Check warning on line 5 in src/main.rs

View workflow job for this annotation

GitHub Actions / Test Suite

unused import: `fs::create_dir_all`
path::{Path, PathBuf},
};
use time::macros::format_description;

use crate::adr::AdrBuilder;
// use crate::adr::{AdrBuilder, AdrContext};

mod adr;
// mod adr;
mod init;
mod new;

#[derive(Debug, Serialize)]
struct TemplateContext {
Expand All @@ -28,44 +34,30 @@ struct Cli {

#[derive(Subcommand)]
enum Commands {
/// Initializes the directory of architecture decision records
Init {
/// Directory to initialize
#[arg(default_value = "doc/adr")]
directory: PathBuf,
},
/// Create a new, numbered ADR
New {
/// A reference to a previous decision to supercede with this new one
#[arg(short, long)]
superceded: Option<Vec<String>>,
/// Link the new ADR to a previous ADR
#[arg(short, long)]
link: Option<Vec<String>>,
/// Title of the new ADR
#[arg(trailing_var_arg = true, required = true)]
title: Vec<String>,
},
/// Link ADRs
/// Initializes the directory of Architecture Decision Records
Init(init::InitArgs),
/// Create a new, numbered Architectural Decision Record
New(new::NewArgs),
/// Link Architectural Decision Records
Link {
/// The source ADR number or file name match
/// The source Architectural Decision Record number or file name match
source: i32,
/// Description of the link to create in the source ADR
/// Description of the link to create in the source Architectural Decision Record
link: String,
/// The target ADR number or file name match
/// The target Architectural Decision Record number or file name match
target: i32,
/// Description of the link to create in the target ADR
/// Description of the link to create in the target Architectural Decision Record
reverse_link: String,
},
/// List ADRs
/// List Architectural Decision Records
List {
/// Directory to list
#[arg(default_value = "doc/adr")]
directory: PathBuf,
},
/// Show the current configuration
Config {},
/// Generates summary documentation about the ADRs
/// Generates summary documentation about the Architectural Decision Records
#[command(subcommand)]
Generate(GenerateCommands),
}
Expand All @@ -84,31 +76,11 @@ fn main() -> Result<()> {
let cli = Cli::parse();

match &cli.command {
Commands::Init { directory } => {
create_dir_all(directory)?;
let adr = AdrBuilder::new()
.title("Record architecture decisions")
.status("Accepted")
.context("We need to record the architectural decisions made on this project.")
.decision("We will use ADRs to record the decisions made on this project.")
.consequences("We will have a record of the decisions made on this project.")
.write(directory)?;

tracing::debug!("Created {}", adr);
std::fs::write(
std::env::current_dir()?.join(".adr-dir"),
directory.to_str().unwrap(),
)?;
tracing::debug!("Wrote .adr-dir");
Commands::Init(args) => {
init::run(args)?;
}
Commands::New {
superceded,
link,
title,
} => {
tracing::debug!(?title);
tracing::debug!(?superceded);
tracing::debug!(?link);
Commands::New(args) => {
new::run(args)?;
}
Commands::Link {
source,
Expand Down Expand Up @@ -144,3 +116,60 @@ fn main() -> Result<()> {
}
Ok(())
}

pub(crate) fn now() -> Result<String> {
let now = time::OffsetDateTime::now_local()?;
let x = now.format(format_description!("[year]-[month]-[day]"))?;
Ok(x)
}

pub(crate) fn adr_filename(title: &str) -> String {
title
.split_whitespace()
.collect::<Vec<&str>>()
.join("-")
.to_lowercase()
}

pub(crate) fn next_adr_sequence(path: impl AsRef<Path>) -> Result<i32> {
let entries = std::fs::read_dir(path)?;
let mut max = 0;
for entry in entries {
let entry = entry?;
let path = entry.path();
if path.is_file() {
let file_name = path.file_name().unwrap().to_str().unwrap();
if file_name.starts_with(char::is_numeric) {
if let Some((num, _rest)) = file_name.split_once('-') {
if let Ok(number) = num.parse::<i32>() {
if number > max {
max = number;
}
}
}
}
}
}
Ok(max + 1)
}

#[cfg(test)]
mod tests {
use assert_fs::TempDir;

use super::*;

#[test]
fn test_generate_filename() {
let title = "Record Architecture Decisions";
let result = adr_filename(title);
assert_eq!(result, "record-architecture-decisions");
}

#[test]
fn test_next_adr_number() {
let tmp_dir = TempDir::new().unwrap();
let result = next_adr_sequence(tmp_dir.path());
assert_eq!(result.unwrap(), 1);
}
}
59 changes: 59 additions & 0 deletions src/new.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use std::fs::read_to_string;

use anyhow::Result;
use clap::Args;
use edit::edit;
use serde::Serialize;
use tinytemplate::TinyTemplate;

use crate::{adr_filename, next_adr_sequence, now};

static NEW_TEMPLATE: &str = include_str!("../templates/nygard/new.md");

#[derive(Debug, Args)]
#[command(version, about, long_about = None)]
pub(crate) struct NewArgs {
/// A reference to a previous decision to supercede with this new one
#[arg(short, long)]
superceded: Option<Vec<String>>,
/// Link the new Architectural Decision to a previous Architectural Decision Record
#[arg(short, long)]
link: Option<Vec<String>>,
/// Title of the new Architectural Decision Record
#[arg(trailing_var_arg = true, required = true)]
title: Vec<String>,
}

#[derive(Debug, Serialize)]
struct NewAdrContext {
number: i32,
title: String,
date: String,
}

pub(crate) fn run(args: &NewArgs) -> Result<()> {
let adr_dir = read_to_string(".adr-dir")?;

let new_context = NewAdrContext {
number: next_adr_sequence(&adr_dir)?,
date: now()?,
title: args.title.join(" "),
};

let mut tt = TinyTemplate::new();
tt.add_template("new_adr", NEW_TEMPLATE)?;
let rendered = tt.render("new_adr", &new_context)?;
let edited = edit(rendered)?;

let filename = format!(
"{}/{:0>4}-{}.md",
adr_dir,
new_context.number,
adr_filename(&new_context.title),
);
std::fs::write(&filename, edited)?;

tracing::debug!("Created {}", filename);

Ok(())
}
Loading

0 comments on commit 6836618

Please sign in to comment.