Expand description
A fast linter for changelogs in the Keep a Changelog format.
use notabene::{Linter, parse};
let s = "# Changelog ...";
let changelog = parse(&s);
let diagnostics = Linter::default().lint(&changelog);
This crate provides a three-step workflow for working with changelogs:
- Parse a changelog document into a structured type.
- Lint the structured changelog to produce diagnostics.
- Optional. Locate the diagnostics to map them to line and column numbers.
The examples below use this minimal, invalid changelog:
# Changelog
## [Unreleased]
### Added
* Add foo ([#12345])
[Unreleased]: https://example.org/
This changelog is invalid because the [#12345]
link label has no corresponding reference
definition.
§Parse
Parse a changelog with parse
.
use notabene::parse;
let s = r#"# Changelog
## [Unreleased]
### Added
* Add foo ([#12345])
[Unreleased]: https://example.org/
"#;
let changelog = parse(&s);
There are two ways to represent a changelog:
ParsedChangelog
, a borrowed version of the dataOwnedChangelog
, an owned version of the data
Parsing is nearly a zero-copy operation and returns a ParsedChangelog
. This version also
includes location information about elements in the changelog for use in the linter. Use
ParsedChangelog::to_owned
if you need owned data.
This crate uses a trait-based API to access the changelog data of either type. The most convenient way to import the traits is to import the prelude:
use notabene::prelude::*;
assert_eq!(changelog.title(), Some("Changelog"));
let unreleased = changelog.unreleased().unwrap();
assert_eq!(unreleased.changes()[0].kind(), "Added");
assert_eq!(unreleased.changes()[0].items().collect::<Vec<_>>()[0], "Add foo ([#12345])");
§Lint
Use Linter
to lint a ParsedChangelog
. Linter::default
returns a linter configured
with the default set of rules.
let diagnostics = Linter::default().lint(&changelog);
The linter reports the invalid link as a Diagnostic
.
use notabene::Rule;
assert_eq!(diagnostics[0].rule, Rule::UndefinedLinkReference);
assert_eq!(diagnostics[0].rule.code(), "E300");
Use Linter::new
to create a linter with a custom set of rules.
use notabene::RuleSet;
// This ruleset excludes the rule above.
let ruleset = RuleSet::new([Rule::MissingTitle, Rule::MissingUnreleased]);
let linter = Linter::new(&ruleset);
let diagnostics = linter.lint(&changelog);
assert_eq!(diagnostics, vec![]);
§Locate
By default, diagnostics report the spans in the source document that matched a Rule
.
A Span
represents a range of byte offsets in the source document.
The Position
type includes the line and column corresponding to the
offset.
Use ParsedChangelog::locate_all
to convert a
collection of Diagnostic<Span>
to a Vec<Diagnostic<Position>>
.
assert_eq!(diagnostics[0].range(), Some(52..60));
let diagnostics = changelog.locate_all(&diagnostics);
assert_eq!((diagnostics[0].range()), (Some(52..60)));
assert_eq!((diagnostics[0].line(), diagnostics[0].column()), (Some(7), Some(12)));
Modules§
- changelog
- Represent changelogs in Keep a Changelog format.
- prelude
- Export key traits.
- span
- Work with spans within the source document.
Structs§
- Diagnostic
- A rule violation.
- Linter
- A changelog linter.
- RuleSet
- A set of linter rules.
Enums§
- Rule
- A linter rule.
Functions§
- parse
- Parse a changelog.