mod detection;
mod error;
mod model;
mod parsers;
mod validation;
pub use detection::{DetectionError, detect, detect_named};
pub use error::{ErrorKind, ParseError};
pub use model::{
Category, Changelog, Entry, EntryKind, LinkDefinition, Metadata, Release, Section, Span,
};
pub use validation::{Diagnostic, DiagnosticCode, Severity, validate, validate_for_format};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum Format {
#[default]
KeepAChangelog,
ConventionalChangelog,
GenericMarkdown,
#[serde(rename = "github-release-notes")]
GitHubReleaseNotes,
Changesets,
Towncrier,
DebianChangelog,
GnuNews,
#[serde(rename = "gnu-changelog")]
GnuChangeLog,
}
impl Format {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::KeepAChangelog => "keep-a-changelog",
Self::ConventionalChangelog => "conventional-changelog",
Self::GenericMarkdown => "generic-markdown",
Self::GitHubReleaseNotes => "github-release-notes",
Self::Changesets => "changesets",
Self::Towncrier => "towncrier",
Self::DebianChangelog => "debian-changelog",
Self::GnuNews => "gnu-news",
Self::GnuChangeLog => "gnu-changelog",
}
}
}
pub const MAX_INPUT_BYTES: usize = 16_777_216;
pub fn parse(input: &str, format: Format) -> Result<Changelog, ParseError> {
parse_internal(input, format, None)
}
pub fn parse_named(input: &str, format: Format, name: &str) -> Result<Changelog, ParseError> {
parse_internal(input, format, Some(name))
}
fn parse_internal(
input: &str,
format: Format,
name: Option<&str>,
) -> Result<Changelog, ParseError> {
if input.len() > MAX_INPUT_BYTES {
return Err(ParseError::new(
ErrorKind::ResourceLimit,
None,
"input exceeds the 16 MiB limit",
));
}
if input.trim().is_empty() && !(format == Format::Towncrier && name.is_some()) {
return Err(ParseError::new(
ErrorKind::EmptyInput,
None,
"expected nonempty changelog or release-note content",
));
}
let mut changelog = match format {
Format::KeepAChangelog => parsers::keep_a_changelog::parse(input),
Format::ConventionalChangelog | Format::GenericMarkdown => {
parsers::histories::parse(input, format)
}
Format::GitHubReleaseNotes => parsers::github::parse(input),
Format::Changesets => parsers::changesets::parse(input),
Format::Towncrier => parsers::towncrier::parse(input, name),
Format::DebianChangelog => parsers::debian::parse(input),
Format::GnuNews | Format::GnuChangeLog => parsers::gnu::parse(input, format),
}?;
changelog.format = format;
Ok(changelog)
}