HXCFU6ZFK2G33HC3VX2PAQTL3UJK4BG472O4G6T5YF3ZOQKIE4HQC
27L7IZBULKDP76DFPUBKKJHLP7XIXN3KFKSMYS35TU6Y574LUUYQC
YUEIF6HWQIGNY7TAR62MAZZA6RDS55VXA6E5TUFLBL6O3U6TXRZAC
2TXT5LGDNIJNMM4EORWKP35BGREM3RPDQ7IUEXZFKQ4N6LGNPLWQC
SLT33ZQRY5MDXBIPTHJOC52AWRBDIAM5H6FCOUCXKNW6HUQKWYHAC
H4XOMSRRIWYCNQUA2YSVY6F2LLXVT7HQJDFDC7ENZGTUMC55I73AC
CUZW3MMNXRGZEPWDI6M6QIAZPEQHA35KMXO7VWTPYBLXVLT7CMOAC
VUSECHH6JNFVRXXS5ZP7NBGO3BQWBJZCWRJCYF2SKOGZLKRYLK4QC
CHJDF2CR5BZF5FQN6ZQQKU6DS4UXTDYMPPCCCBOWR6DGWNW3QR4QC
4FCEKFETVRRY5ZNAO44GFUCEIXYCM3J2MRTUQ7CUYZ2ZHANWI2MAC
PKI5SXRHXTLHPUJQANVDKGA2HLTZNMIVPYU2JVOBC45QUGNBJI7QC
M3X5J5GHHZXBPTFXDCHWQKQPWUVRUH6RJNGVWIA44B57J4T3O75AC
6CDBBU3Z2KQRMC4KUE4HU7ST2RYXD5Y7GC24JFLYPDDIKNXHTJ4AC
VE5MD6H3NT7PNDZH5EWQT72QV7R3753HICOLYHK3OYW57ZT25YNQC
LGQWR3KUNCFZCI6E3OT7FY4TBL3LGOBWVTIQMVHJQSNHO6QY4AXAC
G4X7YXNZRQNHFE5N3HFXKD7R66RLP6RCIUZYSGU4Y4AYZTRAPMQAC
4LFON6GEZVZPITBA7AZCNARXHUSRHWGFYQMR2X6JEVBGHKHECVTAC
M6A6H2RURXUQHHW5WY2ZD753U7A5WBOEC2JJWVMPYU3MEQ7HNENQC
EJ72J6N2Y2FLX4V7UB2P6MORVVQZILQRZAC4ZYNTMPPA6W6WRKGAC
template: "src/conventional/changelog"
scopeRegex: "changelog|check|commit|version"
}
}
fn make_cl_config(git: &GitHelper) -> Config {
let mut config: Config = std::fs::read(".versionrc")
.ok()
.and_then(|versionrc| serde_yaml::from_reader(versionrc.as_slice()).ok())
.unwrap_or_default();
if let Config {
host: None,
owner: None,
repository: None,
..
} = config
{
if let Ok((host, owner, repository)) = host_info(git) {
config.host = host;
config.owner = owner;
config.repository = repository;
}
type HostOwnerRepo = (Option<String>, Option<String>, Option<String>);
/// Get host, owner and repository based on the git remote origin url.
pub(crate) fn host_info(git: &GitHelper) -> Result<HostOwnerRepo, Error> {
if let Some(mut url) = git.url()? {
if !url.contains("://") {
// check if it contains a port
if let Some(colon) = url.find(':') {
match url.as_bytes()[colon + 1] {
b'0'..=b'9' => url = format!("scheme://{}", url),
_ => url = format!("scheme://{}/{}", &url[..colon], &url[colon + 1..]),
}
}
}
let url = Url::parse(url.as_str())?;
let host = url.host().map(|h| format!("https://{}", h));
let mut owner = None;
let mut repository = None;
if let Some(mut segments) = url.path_segments() {
owner = segments.next().map(|s| s.to_string());
repository = segments
.next()
.map(|s: &str| s.trim_end_matches(".git").to_string());
}
Ok((host, owner, repository))
} else {
Ok((None, None, None))
}
}
fn from_str(s: &str) -> Result<Self, Self::Err> {
lazy_static! {
static ref RE_FIRST_LINE: Regex = Regex::new(
r#"(?xms)
^
(?P<type>[a-zA-Z]+)
(?:\((?P<scope>[[:alnum:]]+(?:[-_/][[:alnum:]]+)*)\))?
(?P<breaking>!)?
:\x20(?P<desc>[^\r\n]+)
$"#,
)
.unwrap();
}
impl CommitParser {
pub fn builder() -> CommitParserBuilder {
CommitParserBuilder::new()
}
pub fn parse(&self, s: &str) -> Result<Commit, &str> {
pub struct CommitParserBuilder {
scope_regex: String,
}
impl CommitParserBuilder {
pub fn new() -> Self {
Self {
scope_regex: "[[:alnum:]]+(?:[-_/][[:alnum:]]+)*".into(),
}
}
pub fn scope_regex(self, scope_regex: String) -> Self {
Self { scope_regex }
}
pub fn build(&self) -> CommitParser {
let regex_first_line = Regex::new(&format!(
r#"(?xms)
^
(?P<type>[a-zA-Z]+)
(?:\((?P<scope>{})\))?
(?P<breaking>!)?
:\x20(?P<desc>[^\r\n]+)
$"#,
self.scope_regex
))
.expect("valid scope regex");
let regex_footer = Regex::new(
r#"(?xm)
^
(?:(?P<key>(?:BREAKING\x20CHANGE|[a-zA-Z]+(?:-[a-zA-Z]+)*)):\x20|
(?P<ref>[a-zA-Z]+(?:-[a-zA-Z]+)*)\x20\#)
(?P<value>.+)
$"#,
)
.unwrap();
CommitParser {
regex_first_line,
regex_footer,
}
}
}
/// Additional config: `host`, `owner`, `repository` and `template`
/// Additional config: `host`, `owner`, `repository`, `scope_regex` and `template`
/// An optional template directory. The template should be called `template.hbs`. Partials can be used.
/// `template`. An optional template directory. The template should be called `template.hbs`. Partials can be used.
/// `scopeRegex`. A regex to define possible scopes.
/// For this project this could be `"changelog|check|commit|version"`.
/// Defaults to `"[[:alnum:]]+(?:[-_/][[:alnum:]]+)*"`.
#[serde(default = "default_scope_regex")]
pub(crate) scope_regex: String,
fn default_scope_regex() -> String {
"[[:alnum:]]+(?:[-_/][[:alnum:]]+)*".to_string()
}
type HostOwnerRepo = (Option<String>, Option<String>, Option<String>);
/// Get host, owner and repository based on the git remote origin url.
pub(crate) fn host_info(git: &GitHelper) -> Result<HostOwnerRepo, Error> {
if let Some(mut url) = git.url()? {
if !url.contains("://") {
// check if it contains a port
if let Some(colon) = url.find(':') {
match url.as_bytes()[colon + 1] {
b'0'..=b'9' => url = format!("scheme://{}", url),
_ => url = format!("scheme://{}/{}", &url[..colon], &url[colon + 1..]),
}
}
}
let url = Url::parse(url.as_str())?;
let host = url.host().map(|h| format!("https://{}", h));
let mut owner = None;
let mut repository = None;
if let Some(mut segments) = url.path_segments() {
owner = segments.next().map(|s| s.to_string());
repository = segments
.next()
.map(|s: &str| s.trim_end_matches(".git").to_string());
}
Ok((host, owner, repository))
} else {
Ok((None, None, None))
}
}
pub(crate) fn make_cl_config(git: &GitHelper, path: impl AsRef<Path>) -> Config {
let mut config: Config = (std::fs::read(path))
.ok()
.and_then(|versionrc| (serde_yaml::from_reader(versionrc.as_slice())).ok())
.unwrap_or_default();
if let Config {
host: None,
owner: None,
repository: None,
..
} = config
{
if let Ok((host, owner, repository)) = host_info(git) {
config.host = host;
config.owner = owner;
config.repository = repository;
}
}
config
}
cli::Command::Check(cmd) => cmd.exec(),
cli::Command::Changelog(cmd) => cmd.exec(),
cli::Command::Version(cmd) => cmd.exec(),
cli::Command::Commit(cmd) => cmd.exec(),
cli::Command::Check(cmd) => cmd.exec(config),
cli::Command::Changelog(cmd) => cmd.exec(config),
cli::Command::Version(cmd) => cmd.exec(config),
cli::Command::Commit(cmd) => cmd.exec(config),