notabene/changelog/
parsed.rs1use serde::Serialize;
5
6use crate::span::{Locator, Span, Spanned};
7
8use super::traits::Release as _;
9use super::{owned, traits};
10
11type SpannedStr<'a> = Spanned<&'a str>;
12
13#[derive(Debug, Default, PartialEq, Serialize)]
14pub struct ParsedChangelog<'a> {
15 pub(crate) source: &'a str,
16 pub(crate) title: Option<SpannedStr<'a>>,
17 pub(crate) unreleased: Option<ParsedUnreleased<'a>>,
18 pub(crate) releases: Vec<ParsedRelease<'a>>,
19 pub(crate) invalid_spans: Vec<InvalidSpan>,
20}
21
22#[derive(Debug, Default, PartialEq, Serialize)]
23pub struct ParsedUnreleased<'a> {
24 pub(crate) heading_span: Span,
25 pub(crate) url: Option<String>,
26 pub(crate) changes: Vec<ParsedChanges<'a>>,
27}
28
29#[derive(Debug, Default, PartialEq, Serialize)]
30pub struct ParsedRelease<'a> {
31 pub(crate) heading_span: Span,
32 pub(crate) version: SpannedStr<'a>,
33 pub(crate) url: Option<String>,
34 pub(crate) date: Option<SpannedStr<'a>>,
35 pub(crate) yanked: Option<SpannedStr<'a>>,
36 pub(crate) changes: Vec<ParsedChanges<'a>>,
37}
38
39#[derive(Debug, Default, PartialEq, Serialize)]
40pub struct ParsedChanges<'a> {
41 pub(crate) heading_span: Span,
42 pub(crate) kind: SpannedStr<'a>,
43 pub(crate) items: Vec<SpannedStr<'a>>,
44}
45
46#[derive(Debug, PartialEq, Serialize)]
47pub enum InvalidSpan {
48 InvalidTitle(Span),
49 InvalidSectionHeading(Span),
50 UndefinedLinkReference(Span),
51 DuplicateUnreleased(Span),
52 DuplicateTitle(Span),
53 InvalidUnreleasedPosition(Span),
54}
55
56impl<'a> traits::Changelog for ParsedChangelog<'a> {
57 type Unreleased = ParsedUnreleased<'a>;
58 type Release = ParsedRelease<'a>;
59
60 fn title(&self) -> Option<&str> {
61 self.title.as_deref()
62 }
63
64 fn unreleased(&self) -> Option<&Self::Unreleased> {
65 self.unreleased.as_ref()
66 }
67
68 fn releases(&self) -> &[Self::Release] {
69 &self.releases
70 }
71}
72
73impl<'a> traits::Unreleased for ParsedUnreleased<'a> {
74 type Changes = ParsedChanges<'a>;
75
76 fn url(&self) -> Option<&str> {
77 self.url.as_deref()
78 }
79
80 fn changes(&self) -> &[Self::Changes] {
81 &self.changes
82 }
83}
84
85impl<'a> traits::Release for ParsedRelease<'a> {
86 type Changes = ParsedChanges<'a>;
87
88 fn version(&self) -> &str {
89 &self.version
90 }
91
92 fn url(&self) -> Option<&str> {
93 self.url.as_deref()
94 }
95
96 fn date(&self) -> Option<&str> {
97 self.date.as_deref()
98 }
99
100 fn yanked(&self) -> bool {
101 self.yanked.is_some_and(|s| s.value == "[YANKED]")
102 }
103
104 fn changes(&self) -> &[Self::Changes] {
105 &self.changes
106 }
107}
108
109impl<'a> traits::Changes for ParsedChanges<'a> {
110 fn kind(&self) -> &str {
111 self.kind.value
112 }
113
114 fn items(&self) -> impl Iterator<Item = &str> {
115 self.items.iter().map(|i| &**i)
116 }
117}
118
119impl<'a> ParsedChangelog<'a> {
120 pub fn parse(s: &'a str) -> Self {
121 crate::parser::parse(s)
122 }
123
124 pub(crate) fn locator(&self) -> Locator<'a> {
125 Locator::new(self.source)
126 }
127
128 pub(crate) fn locate(
129 &self,
130 diagnostic: &crate::Diagnostic<Span>,
131 ) -> crate::Diagnostic<crate::span::Position> {
132 self.locator().locate(diagnostic)
133 }
134
135 pub fn locate_all(
136 &self,
137 diagnostics: &[crate::Diagnostic],
138 ) -> Vec<crate::Diagnostic<crate::span::Position>> {
139 diagnostics.iter().map(|d| self.locate(d)).collect()
140 }
141
142 pub fn to_owned(&self) -> owned::OwnedChangelog {
143 owned::OwnedChangelog {
144 title: self.title.map(|s| s.value.to_owned()),
145 unreleased: self.unreleased.as_ref().map(|u| u.to_owned()),
146 releases: self.releases.iter().map(|r| r.to_owned()).collect(),
147 }
148 }
149}
150
151impl<'a> ParsedUnreleased<'a> {
152 pub fn to_owned(&self) -> owned::OwnedUnreleased {
153 owned::OwnedUnreleased {
154 url: self.url.clone(),
155 changes: self.changes.iter().map(|c| c.to_owned()).collect(),
156 }
157 }
158}
159
160impl<'a> ParsedRelease<'a> {
161 pub fn to_owned(&self) -> owned::OwnedRelease {
162 owned::OwnedRelease {
163 version: self.version.value.to_owned(),
164 url: self.url.clone(),
165 date: self.date.map(|s| s.value.to_owned()),
166 yanked: self.yanked(),
167 changes: self.changes.iter().map(|c| c.to_owned()).collect(),
168 }
169 }
170}
171
172impl<'a> ParsedChanges<'a> {
173 pub fn to_owned(&self) -> owned::OwnedChanges {
174 owned::OwnedChanges {
175 kind: self.kind.value.to_owned(),
176 items: self.items.iter().map(|i| i.value.to_owned()).collect(),
177 }
178 }
179}