1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
use std::collections::BTreeMap; use std::io::{Write, Result}; use time; use git::Commit; use clog::Clog; /// Writes commits to a specified `Write` object pub struct LogWriter<'a, 'cc> { /// The `Write` object writer: &'a mut (Write + 'a), /// The options used when writing sections and commits options: &'cc Clog } impl<'a, 'cc> LogWriter<'a, 'cc> { /// Creates a new instance of the LogWriter using a `Write` object and a `Clog` object as the /// configuration options to use while writing. /// /// # Example /// /// ```no_run /// # use std::fs::File; /// # use std::io::Read; /// # use std::path::Path; /// # use std::collections::BTreeMap; /// # use clog::{Clog, LogWriter}; /// let clog = Clog::new().unwrap_or_else(|e| { /// println!("Error initializing: {}", e); /// std::process::exit(1); /// }); /// /// // Open and prepend, or create the changelog file... /// let mut contents = String::new(); /// File::open(&Path::new(&clog.changelog[..])).map(|mut f| f.read_to_string(&mut contents).ok()).ok(); /// let mut file = File::create(&Path::new(&clog.changelog[..])).ok().unwrap(); /// /// // Create the LogWriter... /// let mut writer = LogWriter::new(&mut file, &clog); /// ``` pub fn new<T>(writer: &'a mut T, options: &'cc Clog) -> LogWriter<'a, 'cc> where T: Write + Send { LogWriter { writer: writer, options: options } } /// Writes the initial header inforamtion for a release /// /// # Example /// /// ```no_run /// # use std::fs::File; /// # use std::io::Read; /// # use std::path::Path; /// # use std::collections::BTreeMap; /// # use clog::{Clog, LogWriter, SectionMap}; /// let clog = Clog::new().unwrap_or_else(|e| { /// println!("Error initializing: {}", e); /// std::process::exit(1); /// }); /// /// // Get the commits we're interested in... /// let sm = SectionMap::from_commits(clog.get_commits()); /// /// // Open and prepend, or create the changelog file... /// let mut contents = String::new(); /// File::open(&Path::new(&clog.changelog[..])).map(|mut f| f.read_to_string(&mut contents).ok()).ok(); /// let mut file = File::create(&Path::new(&clog.changelog[..])).ok().unwrap(); /// /// // Write the header... /// let mut writer = LogWriter::new(&mut file, &clog); /// writer.write_header().ok().expect("failed to write header"); /// ``` pub fn write_header(&mut self) -> Result<()> { let subtitle = match self.options.subtitle.len() { 0 => self.options.subtitle.to_owned(), _ => format!(" {}", self.options.subtitle) }; let version_text = if self.options.patch_ver { format!("### {}{}", self.options.version, subtitle) } else { format!("## {}{}", self.options.version, subtitle) }; let date = time::now_utc(); match date.strftime("%Y-%m-%d") { Ok(date) => write!(self.writer, "<a name=\"{}\"></a>\n{} ({})\n\n", self.options.version, version_text, date), Err(_) => write!(self.writer, "<a name=\"{}\"></a>\n{} ({})\n\n", self.options.version, version_text, "XXXX-XX-XX") } } /// Writes a particular section of a changelog /// /// # Example /// /// ```no_run /// # use std::fs::File; /// # use std::io::Read; /// # use std::path::Path; /// # use std::collections::BTreeMap; /// # use clog::{Clog, LogWriter, SectionMap}; /// let clog = Clog::new().unwrap_or_else(|e| { /// println!("Error initializing: {}", e); /// std::process::exit(1); /// }); /// /// // Get the commits we're interested in... /// let sm = SectionMap::from_commits(clog.get_commits()); /// /// // Open and prepend, or create the changelog file... /// let mut contents = String::new(); /// File::open(&Path::new(&clog.changelog[..])).map(|mut f| f.read_to_string(&mut contents).ok()).ok(); /// let mut file = File::create(&Path::new(&clog.changelog[..])).ok().unwrap(); /// /// // Write the header... /// let mut writer = LogWriter::new(&mut file, &clog); /// writer.write_header().ok().expect("failed to write header"); /// /// // Write the sections /// for (sec, secmap) in sm.sections { /// writer.write_section(&sec[..], &secmap.iter().collect::<BTreeMap<_,_>>()).ok().expect(&format!("failed to write {}", sec)[..]); /// } /// writer.write(&contents[..]).ok().expect("failed to write contents"); /// ``` pub fn write_section(&mut self, title: &str, section: &BTreeMap<&String, &Vec<Commit>>) -> Result<()> { if section.len() == 0 { return Ok(()) } try!(self.writer.write(&format!("\n#### {}\n\n", title)[..].as_bytes())); for (component, entries) in section.iter() { let nested = (entries.len() > 1) && !component.is_empty(); let prefix = if nested { try!(write!(self.writer, "* **{}**\n", component)); " *".to_owned() } else if !component.is_empty() { format!("* **{}**", component) } else { format!("* ") }; for entry in entries.iter() { try!(write!(self.writer, "{} {} ({}", prefix, entry.subject, self.options.link_style .commit_link(&entry.hash[..], &self.options.repo[..]))); if entry.closes.len() > 0 { let closes_string = entry.closes.iter() .map(|s| self.options.link_style.issue_link(&s[..], &self.options.repo[..])) // FIXME: Connect should be // used on the Iterator .collect::<Vec<String>>() .connect(", "); try!(write!(self.writer, ", closes {}", closes_string)); } try!(write!(self.writer, ")\n")); } } Ok(()) } /// Writes some contents to the `Write` writer object pub fn write(&mut self, content: &str) -> Result<()> { try!(write!(self.writer, "\n\n\n")); write!(self.writer, "{}", content) } }