Skip to content

Commit

Permalink
feat(main): add basic functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
cburgdorf committed Sep 12, 2014
1 parent 6d8183f commit 05199ce
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Cargo.toml
@@ -0,0 +1,5 @@
[package]

name = "clog"
version = "0.0.1"
authors = ["Christoph Burgdorf <christoph.burgdorf@bvsn.org>"]
36 changes: 36 additions & 0 deletions src/common.rs
@@ -0,0 +1,36 @@
use std::fmt;
use std::collections::hashmap::HashMap;

#[deriving(Show, PartialEq, Clone)]
pub enum CommitType {
Feature,
Fix,
Unknown
}

#[deriving(Clone)]
pub struct LogEntry {
pub hash: String,
pub subject: String,
pub component: String,
pub closes: Vec<String>,
pub breaks: Vec<String>,
pub commit_type: CommitType
}

impl fmt::Show for LogEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{{
hash:{},
subject: {},
commit_type: {},
component: {}
}}", self.hash, self.subject, self.commit_type, self.component)
}
}

pub struct SectionMap {
pub features: HashMap<String, Vec<LogEntry>>,
pub fixes: HashMap<String, Vec<LogEntry>>,
pub breaks: HashMap<String, Vec<LogEntry>>
}
64 changes: 64 additions & 0 deletions src/git.rs
@@ -0,0 +1,64 @@
use std::io::Command;
use regex::Regex;
use common:: { LogEntry, Feature, Fix, Unknown };

pub struct LogReaderConfig {
pub grep: String,
pub format: String,
pub from: String,
pub to: String
}

pub fn get_commits (config:LogReaderConfig) -> Vec<LogEntry>{
Command::new("git")
.arg("log")
.arg("-E")
.arg(format!("--grep={}",config.grep))
.arg(format!("--format={}", "%H%n%s%n%b%n==END=="))
//.arg("FROM..TO")
.spawn()
.ok().expect("failed to invoke `git log`")
.stdout.get_mut_ref().read_to_string()
.ok().expect("failed to read git log")
.as_slice()
.split_str("\n==END==\n")
.map(|commit_str| { parse_raw_commit(commit_str) })
.filter(| entry| entry.commit_type != Unknown)
.collect()
}

static COMMIT_PATTERN: Regex = regex!(r"^(.*)\((.*)\):(.*)");

fn parse_raw_commit(commit_str:&str) -> LogEntry {

let mut lines = commit_str.split('\n').collect::<Vec<&str>>();

//println!("parsed: {}", lines);

let hash = lines.remove(0).unwrap_or("").to_string();
let temp_subject = lines.remove(0).unwrap_or("").to_string();

let mut entry = LogEntry {
hash: hash,
subject: "".to_string(),
component: "".to_string(),
closes: vec!(),
breaks: vec!(),
commit_type: Unknown
};

match COMMIT_PATTERN.captures(temp_subject.as_slice()) {
Some(caps) => {
entry.commit_type = match caps.at(1) {
"feat" => Feature,
"fix" => Fix,
_ => Unknown
};
entry.component = caps.at(2).to_string();
entry.subject = caps.at(3).to_string();
},
_ => ()
};

entry
}
68 changes: 68 additions & 0 deletions src/log_writer.rs
@@ -0,0 +1,68 @@
use std::collections::hashmap::HashMap;
use std::io::Writer;
use common::{ LogEntry };

pub struct LogWriter<'a> {
writer: &'a mut Writer+'a,
options: LogWriterOptions<'a>
}

pub struct LogWriterOptions<'a> {
pub repository_link: String,
//pub writer: &'a mut Writer+'a
}

impl<'a> LogWriter<'a> {

// function getCommitLink(repository, hash) {
// var shortHash = hash.substring(0,8); // no need to show super long hash in log
// return repository ?
// util.format(LINK_COMMIT, shortHash, repository, hash) :
// util.format(COMMIT, shortHash);
// }

fn get_commit_link (repository: &String, hash: &String) -> String {
let short_hash = hash.as_slice().slice_chars(0,8);
format!("[{}]({}/commit/{})", short_hash, repository, hash)
}

pub fn write_section (&mut self, title: &str, section: &HashMap<String, Vec<LogEntry>>) {

if section.len() == 0 {
return;
}

self.writer.write_line(format!("\n#### {}\n\n", title).as_slice());

section.iter().all(|(component, entries)| {
let mut prefix:String;
let nested = entries.len() > 1;

//TODO: implement the empty component stuff
if nested {
self.writer.write(format!("* **{}**\n", component).as_bytes());
prefix = " *".to_string();
}
else {
prefix = format!("* **{}**", component)
}

entries.iter().all(|entry| {
self.writer.write(format!("{} {} ({}", prefix, entry.subject, LogWriter::get_commit_link(&self.options.repository_link, &entry.hash)).as_bytes());
//TODO: implement closes stuff

self.writer.write(")\n".as_bytes());

true
});
true
});
}

pub fn new<T:Writer + Send>(writer: &'a mut T, options: LogWriterOptions) -> LogWriter<'a> {
LogWriter {
writer: writer,
options: options
}
}
}
39 changes: 39 additions & 0 deletions src/main.rs
@@ -0,0 +1,39 @@
#![crate_name = "clog"]
#![comment = "A conventional changelog generator"]
#![license = "MIT"]
#![feature(macro_rules, phase)]

extern crate regex;
#[phase(plugin)]
extern crate regex_macros;

use git::{ LogReaderConfig, get_commits };
use log_writer::{ LogWriter, LogWriterOptions };
use section_builder::build_sections;
use std::io::{File, Open, Write};

mod common;
mod git;
mod log_writer;
mod section_builder;

fn main () {

let log_reader_config = LogReaderConfig {
grep: "^feat|^fix|BREAKING'".to_string(),
format: "%H%n%s%n%b%n==END==".to_string(),
from: "".to_string(),
to: "HEAD".to_string()
};
let commits = get_commits(log_reader_config);

let sections = build_sections(commits.clone());
let mut file = File::open_mode(&Path::new("changelog.md"), Open, Write).ok().unwrap();
let mut writer = LogWriter::new(&mut file, LogWriterOptions {
repository_link: "https://github.com/ajoslin/conventional-changelog".to_string()
});

writer.write_section("Bug Fixes", &sections.fixes);
writer.write_section("Features", &sections.features);
//println!("{}", commits);
}
32 changes: 32 additions & 0 deletions src/section_builder.rs
@@ -0,0 +1,32 @@
use std::collections::hashmap::HashMap;
use common::{ LogEntry, SectionMap, Feature, Fix };

pub fn build_sections(log_entries: Vec<LogEntry>) -> SectionMap {

let mut sections = SectionMap {
features: HashMap::new(),
fixes: HashMap::new(),
breaks: HashMap::new()
};

log_entries.iter().all(|entry| {

match entry.commit_type {
Feature => {
sections.features
.find_or_insert(entry.component.clone(), Vec::new())
.push(entry.clone());
},
Fix => {
sections.fixes
.find_or_insert(entry.component.clone(), Vec::new())
.push(entry.clone());
},
_ => {}
}

true
});

sections
}

0 comments on commit 05199ce

Please sign in to comment.