changelog/
lib.rs

1use serde_derive::Deserialize;
2
3mod changelog;
4mod cli;
5mod error;
6mod git;
7
8pub use crate::changelog::{format, stats};
9pub use crate::cli::Cli;
10pub use crate::error::{Error, ErrorKind, Result};
11pub use crate::git::{all_commits, full_diff, Commit, Tag};
12
13use failure::ResultExt;
14use mktemp::Temp;
15use std::env;
16use std::fs::{self, File, OpenOptions};
17use std::io::{self, Write};
18use std::path::PathBuf;
19
20#[derive(Deserialize)]
21struct Config {
22  package: Package,
23}
24
25#[derive(Deserialize)]
26struct Package {
27  repository: String,
28}
29
30/// Get the repository name from `Cargo.toml`.
31/// Fallbacks to directory name if `Cargo.toml` does not exists.
32pub fn read_repo(dir: &str) -> crate::Result<String> {
33  let mut dir = PathBuf::from(dir);
34  dir.push("Cargo.toml");
35
36  let config = if dir.exists() {
37    let cargo_toml =
38      fs::read_to_string(dir).context(crate::ErrorKind::Other)?;
39    toml::from_str(&cargo_toml).context(crate::ErrorKind::Other)?
40  } else {
41    dir.pop();
42    Config {
43      package: Package {
44        repository: read_path_name(&dir).context(crate::ErrorKind::Other)?,
45      },
46    }
47  };
48  Ok(config.package.repository)
49}
50
51/// Read the path name from a PathBuf
52pub fn read_path_name(dir: &PathBuf) -> crate::Result<String> {
53  // executable was called with the default path
54  let path_name = if dir.eq(&PathBuf::from(".")) {
55    let path = env::current_dir()?;
56
57    String::from(
58      path
59        .file_name()
60        .ok_or_else(|| crate::ErrorKind::Other)?
61        .to_str()
62        .ok_or_else(|| crate::ErrorKind::Other)?,
63    )
64  } else {
65    String::from(
66      dir
67        .file_name()
68        .ok_or_else(|| crate::ErrorKind::Other)?
69        .to_str()
70        .ok_or_else(|| crate::ErrorKind::Other)?,
71    )
72  };
73
74  Ok(path_name)
75}
76
77/// Prepend a changelog to a file.
78pub fn prepend_file(file_path: &str, data: &str) -> crate::Result<()> {
79  let file_path = PathBuf::from(file_path);
80
81  // Touch new file if it doesn't exist already
82  let file = OpenOptions::new()
83    .create(true)
84    .append(true)
85    .open(&file_path)
86    .context(crate::ErrorKind::Other)?;
87  file.sync_all().context(crate::ErrorKind::Other)?;
88
89  // Setup temp file & path
90  let tmp_path = Temp::new_file().context(crate::ErrorKind::Other)?;
91  let mut tmp = File::create(&tmp_path).context(crate::ErrorKind::Other)?;
92  let mut src = File::open(&file_path).context(crate::ErrorKind::Other)?;
93
94  // Prepend data
95  tmp
96    .write_all(data.as_bytes())
97    .context(crate::ErrorKind::Other)?;
98  tmp.write(b"\n\n").context(crate::ErrorKind::Other)?;
99  io::copy(&mut src, &mut tmp).context(crate::ErrorKind::Other)?;
100
101  // Cleanup
102  fs::remove_file(&file_path).context(crate::ErrorKind::Other)?;
103  fs::copy(&tmp_path, &file_path).context(crate::ErrorKind::Other)?;
104
105  Ok(())
106}