Skip to content

Commit 1be278e

Browse files
committed
Save CHANGELOG.md after each review in changelog tool
1 parent 547e509 commit 1be278e

File tree

4 files changed

+99
-67
lines changed

4 files changed

+99
-67
lines changed

CHANGELOG.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- Fix `block_on` in `iced_wgpu` hanging Wasm builds. [#2313](https://github.com/iced-rs/iced/pull/2313)
1313

1414
Many thanks to...
15-
- @hecrj
1615
- @n1ght-hunter
1716

1817
## [0.12.1] - 2024-02-22
-5.63 KB
Binary file not shown.

examples/changelog/src/changelog.rs

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ impl Changelog {
2929
}
3030
}
3131

32-
pub async fn list() -> Result<(Self, Vec<Candidate>), Error> {
32+
pub async fn list() -> Result<(Self, Vec<Contribution>), Error> {
3333
let mut changelog = Self::new();
3434

3535
{
@@ -97,7 +97,7 @@ impl Changelog {
9797
}
9898
}
9999

100-
let mut candidates = Candidate::list().await?;
100+
let mut candidates = Contribution::list().await?;
101101

102102
for reviewed_entry in changelog.entries() {
103103
candidates.retain(|candidate| candidate.id != reviewed_entry);
@@ -106,6 +106,30 @@ impl Changelog {
106106
Ok((changelog, candidates))
107107
}
108108

109+
pub async fn save(self) -> Result<(), Error> {
110+
let markdown = fs::read_to_string("CHANGELOG.md").await?;
111+
112+
let Some((header, rest)) = markdown.split_once("\n## ") else {
113+
return Err(Error::InvalidFormat);
114+
};
115+
116+
let Some((_unreleased, rest)) = rest.split_once("\n## ") else {
117+
return Err(Error::InvalidFormat);
118+
};
119+
120+
let unreleased = format!(
121+
"\n## [Unreleased]\n{changelog}",
122+
changelog = self.to_string()
123+
);
124+
125+
let rest = format!("\n## {rest}");
126+
127+
let changelog = [header, &unreleased, &rest].concat();
128+
fs::write("CHANGELOG.md", changelog).await?;
129+
130+
Ok(())
131+
}
132+
109133
pub fn len(&self) -> usize {
110134
self.ids.len()
111135
}
@@ -132,7 +156,7 @@ impl Changelog {
132156

133157
target.push(item);
134158

135-
if !self.authors.contains(&entry.author) {
159+
if entry.author != "hecrj" && !self.authors.contains(&entry.author) {
136160
self.authors.push(entry.author);
137161
self.authors.sort_by_key(|author| author.to_lowercase());
138162
}
@@ -238,21 +262,12 @@ impl fmt::Display for Category {
238262
}
239263

240264
#[derive(Debug, Clone)]
241-
pub struct Candidate {
265+
pub struct Contribution {
242266
pub id: u64,
243267
}
244268

245-
#[derive(Debug, Clone)]
246-
pub struct PullRequest {
247-
pub id: u64,
248-
pub title: String,
249-
pub description: String,
250-
pub labels: Vec<String>,
251-
pub author: String,
252-
}
253-
254-
impl Candidate {
255-
pub async fn list() -> Result<Vec<Candidate>, Error> {
269+
impl Contribution {
270+
pub async fn list() -> Result<Vec<Contribution>, Error> {
256271
let output = process::Command::new("git")
257272
.args([
258273
"log",
@@ -273,20 +288,31 @@ impl Candidate {
273288
let (_, pull_request) = title.split_once("#")?;
274289
let (pull_request, _) = pull_request.split_once([')', ' '])?;
275290

276-
Some(Candidate {
291+
Some(Contribution {
277292
id: pull_request.parse().ok()?,
278293
})
279294
})
280295
.collect())
281296
}
297+
}
282298

283-
pub async fn fetch(self) -> Result<PullRequest, Error> {
299+
#[derive(Debug, Clone)]
300+
pub struct PullRequest {
301+
pub id: u64,
302+
pub title: String,
303+
pub description: String,
304+
pub labels: Vec<String>,
305+
pub author: String,
306+
}
307+
308+
impl PullRequest {
309+
pub async fn fetch(contribution: Contribution) -> Result<Self, Error> {
284310
let request = reqwest::Client::new()
285311
.request(
286312
reqwest::Method::GET,
287313
format!(
288314
"https://api.github.com/repos/iced-rs/iced/pulls/{}",
289-
self.id
315+
contribution.id
290316
),
291317
)
292318
.header("User-Agent", "iced changelog generator")
@@ -319,8 +345,8 @@ impl Candidate {
319345

320346
let schema: Schema = request.send().await?.json().await?;
321347

322-
Ok(PullRequest {
323-
id: self.id,
348+
Ok(Self {
349+
id: contribution.id,
324350
title: schema.title,
325351
description: schema.body,
326352
labels: schema.labels.into_iter().map(|label| label.name).collect(),
@@ -339,6 +365,9 @@ pub enum Error {
339365

340366
#[error("no GITHUB_TOKEN variable was set")]
341367
GitHubTokenNotFound,
368+
369+
#[error("the changelog format is not valid")]
370+
InvalidFormat,
342371
}
343372

344373
impl From<io::Error> for Error {

examples/changelog/src/main.rs

Lines changed: 50 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,33 @@
11
mod changelog;
2-
mod icon;
32

43
use crate::changelog::Changelog;
54

6-
use iced::clipboard;
75
use iced::font;
86
use iced::widget::{
97
button, center, column, container, markdown, pick_list, progress_bar,
108
rich_text, row, scrollable, span, stack, text, text_input,
119
};
12-
use iced::{Element, Fill, FillPortion, Font, Task, Theme};
10+
use iced::{Center, Element, Fill, FillPortion, Font, Task, Theme};
1311

1412
pub fn main() -> iced::Result {
1513
iced::application("Changelog Generator", Generator::update, Generator::view)
16-
.font(icon::FONT_BYTES)
1714
.theme(Generator::theme)
1815
.run_with(Generator::new)
1916
}
2017

2118
enum Generator {
2219
Loading,
23-
Empty,
2420
Reviewing {
2521
changelog: Changelog,
26-
pending: Vec<changelog::Candidate>,
22+
pending: Vec<changelog::Contribution>,
2723
state: State,
2824
preview: Vec<markdown::Item>,
2925
},
26+
Done,
3027
}
3128

3229
enum State {
33-
Loading(changelog::Candidate),
30+
Loading(changelog::Contribution),
3431
Loaded {
3532
pull_request: changelog::PullRequest,
3633
description: Vec<markdown::Item>,
@@ -42,15 +39,16 @@ enum State {
4239
#[derive(Debug, Clone)]
4340
enum Message {
4441
ChangelogListed(
45-
Result<(Changelog, Vec<changelog::Candidate>), changelog::Error>,
42+
Result<(Changelog, Vec<changelog::Contribution>), changelog::Error>,
4643
),
4744
PullRequestFetched(Result<changelog::PullRequest, changelog::Error>),
4845
UrlClicked(markdown::Url),
4946
TitleChanged(String),
5047
CategorySelected(changelog::Category),
5148
Next,
5249
OpenPullRequest(u64),
53-
CopyPreview,
50+
ChangelogSaved(Result<(), changelog::Error>),
51+
Quit,
5452
}
5553

5654
impl Generator {
@@ -64,23 +62,23 @@ impl Generator {
6462
fn update(&mut self, message: Message) -> Task<Message> {
6563
match message {
6664
Message::ChangelogListed(Ok((changelog, mut pending))) => {
67-
if let Some(candidate) = pending.pop() {
65+
if let Some(contribution) = pending.pop() {
6866
let preview =
6967
markdown::parse(&changelog.to_string()).collect();
7068

7169
*self = Self::Reviewing {
7270
changelog,
7371
pending,
74-
state: State::Loading(candidate.clone()),
72+
state: State::Loading(contribution.clone()),
7573
preview,
7674
};
7775

7876
Task::perform(
79-
candidate.fetch(),
77+
changelog::PullRequest::fetch(contribution),
8078
Message::PullRequestFetched,
8179
)
8280
} else {
83-
*self = Self::Empty;
81+
*self = Self::Done;
8482

8583
Task::none()
8684
}
@@ -108,12 +106,6 @@ impl Generator {
108106

109107
Task::none()
110108
}
111-
Message::ChangelogListed(Err(error))
112-
| Message::PullRequestFetched(Err(error)) => {
113-
log::error!("{error}");
114-
115-
Task::none()
116-
}
117109
Message::UrlClicked(url) => {
118110
let _ = webbrowser::open(url.as_str());
119111

@@ -172,19 +164,27 @@ impl Generator {
172164
{
173165
changelog.push(entry);
174166

167+
let save = Task::perform(
168+
changelog.clone().save(),
169+
Message::ChangelogSaved,
170+
);
171+
175172
*preview =
176173
markdown::parse(&changelog.to_string()).collect();
177174

178-
if let Some(candidate) = pending.pop() {
179-
*state = State::Loading(candidate.clone());
175+
if let Some(contribution) = pending.pop() {
176+
*state = State::Loading(contribution.clone());
180177

181-
Task::perform(
182-
candidate.fetch(),
183-
Message::PullRequestFetched,
184-
)
178+
Task::batch([
179+
save,
180+
Task::perform(
181+
changelog::PullRequest::fetch(contribution),
182+
Message::PullRequestFetched,
183+
),
184+
])
185185
} else {
186-
// TODO: We are done!
187-
Task::none()
186+
*self = Self::Done;
187+
save
188188
}
189189
} else {
190190
Task::none()
@@ -197,20 +197,32 @@ impl Generator {
197197

198198
Task::none()
199199
}
200-
Message::CopyPreview => {
201-
let Self::Reviewing { changelog, .. } = self else {
202-
return Task::none();
203-
};
200+
Message::ChangelogSaved(Ok(())) => Task::none(),
201+
202+
Message::ChangelogListed(Err(error))
203+
| Message::PullRequestFetched(Err(error))
204+
| Message::ChangelogSaved(Err(error)) => {
205+
log::error!("{error}");
204206

205-
clipboard::write(changelog.to_string())
207+
Task::none()
206208
}
209+
Message::Quit => iced::exit(),
207210
}
208211
}
209212

210213
fn view(&self) -> Element<Message> {
211214
match self {
212215
Self::Loading => center("Loading...").into(),
213-
Self::Empty => center("No changes found!").into(),
216+
Self::Done => center(
217+
column![
218+
text("Changelog is up-to-date! 🎉")
219+
.shaping(text::Shaping::Advanced),
220+
button("Quit").on_press(Message::Quit),
221+
]
222+
.spacing(10)
223+
.align_x(Center),
224+
)
225+
.into(),
214226
Self::Reviewing {
215227
changelog,
216228
pending,
@@ -237,8 +249,8 @@ impl Generator {
237249
};
238250

239251
let form: Element<_> = match state {
240-
State::Loading(candidate) => {
241-
text!("Loading #{}...", candidate.id).into()
252+
State::Loading(contribution) => {
253+
text!("Loading #{}...", contribution.id).into()
242254
}
243255
State::Loaded {
244256
pull_request,
@@ -318,17 +330,16 @@ impl Generator {
318330
}
319331
};
320332

321-
let preview: Element<_> = if preview.is_empty() {
333+
let preview = if preview.is_empty() {
322334
center(
323335
container(
324336
text("The changelog is empty... so far!").size(12),
325337
)
326338
.padding(10)
327339
.style(container::rounded_box),
328340
)
329-
.into()
330341
} else {
331-
let content = container(
342+
container(
332343
scrollable(
333344
markdown::view(
334345
preview,
@@ -343,14 +354,7 @@ impl Generator {
343354
)
344355
.width(Fill)
345356
.padding(10)
346-
.style(container::rounded_box);
347-
348-
let copy = button(icon::copy().size(12))
349-
.on_press(Message::CopyPreview)
350-
.style(button::text);
351-
352-
center(stack![content, container(copy).align_right(Fill)])
353-
.into()
357+
.style(container::rounded_box)
354358
};
355359

356360
let review = column![container(form).height(Fill), progress]

0 commit comments

Comments
 (0)