summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--examples/changelog/fonts/changelog-icons.ttfbin5764 -> 0 bytes
-rw-r--r--examples/changelog/src/changelog.rs69
-rw-r--r--examples/changelog/src/main.rs96
4 files changed, 99 insertions, 67 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e8ac8d68..2a381e21 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,7 +12,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fix `block_on` in `iced_wgpu` hanging Wasm builds. [#2313](https://github.com/iced-rs/iced/pull/2313)
Many thanks to...
-- @hecrj
- @n1ght-hunter
## [0.12.1] - 2024-02-22
diff --git a/examples/changelog/fonts/changelog-icons.ttf b/examples/changelog/fonts/changelog-icons.ttf
deleted file mode 100644
index a0f32553..00000000
--- a/examples/changelog/fonts/changelog-icons.ttf
+++ /dev/null
Binary files differ
diff --git a/examples/changelog/src/changelog.rs b/examples/changelog/src/changelog.rs
index 39cbb42c..9421f91c 100644
--- a/examples/changelog/src/changelog.rs
+++ b/examples/changelog/src/changelog.rs
@@ -29,7 +29,7 @@ impl Changelog {
}
}
- pub async fn list() -> Result<(Self, Vec<Candidate>), Error> {
+ pub async fn list() -> Result<(Self, Vec<Contribution>), Error> {
let mut changelog = Self::new();
{
@@ -97,7 +97,7 @@ impl Changelog {
}
}
- let mut candidates = Candidate::list().await?;
+ let mut candidates = Contribution::list().await?;
for reviewed_entry in changelog.entries() {
candidates.retain(|candidate| candidate.id != reviewed_entry);
@@ -106,6 +106,30 @@ impl Changelog {
Ok((changelog, candidates))
}
+ pub async fn save(self) -> Result<(), Error> {
+ let markdown = fs::read_to_string("CHANGELOG.md").await?;
+
+ let Some((header, rest)) = markdown.split_once("\n## ") else {
+ return Err(Error::InvalidFormat);
+ };
+
+ let Some((_unreleased, rest)) = rest.split_once("\n## ") else {
+ return Err(Error::InvalidFormat);
+ };
+
+ let unreleased = format!(
+ "\n## [Unreleased]\n{changelog}",
+ changelog = self.to_string()
+ );
+
+ let rest = format!("\n## {rest}");
+
+ let changelog = [header, &unreleased, &rest].concat();
+ fs::write("CHANGELOG.md", changelog).await?;
+
+ Ok(())
+ }
+
pub fn len(&self) -> usize {
self.ids.len()
}
@@ -132,7 +156,7 @@ impl Changelog {
target.push(item);
- if !self.authors.contains(&entry.author) {
+ if entry.author != "hecrj" && !self.authors.contains(&entry.author) {
self.authors.push(entry.author);
self.authors.sort_by_key(|author| author.to_lowercase());
}
@@ -238,21 +262,12 @@ impl fmt::Display for Category {
}
#[derive(Debug, Clone)]
-pub struct Candidate {
+pub struct Contribution {
pub id: u64,
}
-#[derive(Debug, Clone)]
-pub struct PullRequest {
- pub id: u64,
- pub title: String,
- pub description: String,
- pub labels: Vec<String>,
- pub author: String,
-}
-
-impl Candidate {
- pub async fn list() -> Result<Vec<Candidate>, Error> {
+impl Contribution {
+ pub async fn list() -> Result<Vec<Contribution>, Error> {
let output = process::Command::new("git")
.args([
"log",
@@ -273,20 +288,31 @@ impl Candidate {
let (_, pull_request) = title.split_once("#")?;
let (pull_request, _) = pull_request.split_once([')', ' '])?;
- Some(Candidate {
+ Some(Contribution {
id: pull_request.parse().ok()?,
})
})
.collect())
}
+}
- pub async fn fetch(self) -> Result<PullRequest, Error> {
+#[derive(Debug, Clone)]
+pub struct PullRequest {
+ pub id: u64,
+ pub title: String,
+ pub description: String,
+ pub labels: Vec<String>,
+ pub author: String,
+}
+
+impl PullRequest {
+ pub async fn fetch(contribution: Contribution) -> Result<Self, Error> {
let request = reqwest::Client::new()
.request(
reqwest::Method::GET,
format!(
"https://api.github.com/repos/iced-rs/iced/pulls/{}",
- self.id
+ contribution.id
),
)
.header("User-Agent", "iced changelog generator")
@@ -319,8 +345,8 @@ impl Candidate {
let schema: Schema = request.send().await?.json().await?;
- Ok(PullRequest {
- id: self.id,
+ Ok(Self {
+ id: contribution.id,
title: schema.title,
description: schema.body,
labels: schema.labels.into_iter().map(|label| label.name).collect(),
@@ -339,6 +365,9 @@ pub enum Error {
#[error("no GITHUB_TOKEN variable was set")]
GitHubTokenNotFound,
+
+ #[error("the changelog format is not valid")]
+ InvalidFormat,
}
impl From<io::Error> for Error {
diff --git a/examples/changelog/src/main.rs b/examples/changelog/src/main.rs
index 73da0f2c..79fa37b5 100644
--- a/examples/changelog/src/main.rs
+++ b/examples/changelog/src/main.rs
@@ -1,36 +1,33 @@
mod changelog;
-mod icon;
use crate::changelog::Changelog;
-use iced::clipboard;
use iced::font;
use iced::widget::{
button, center, column, container, markdown, pick_list, progress_bar,
rich_text, row, scrollable, span, stack, text, text_input,
};
-use iced::{Element, Fill, FillPortion, Font, Task, Theme};
+use iced::{Center, Element, Fill, FillPortion, Font, Task, Theme};
pub fn main() -> iced::Result {
iced::application("Changelog Generator", Generator::update, Generator::view)
- .font(icon::FONT_BYTES)
.theme(Generator::theme)
.run_with(Generator::new)
}
enum Generator {
Loading,
- Empty,
Reviewing {
changelog: Changelog,
- pending: Vec<changelog::Candidate>,
+ pending: Vec<changelog::Contribution>,
state: State,
preview: Vec<markdown::Item>,
},
+ Done,
}
enum State {
- Loading(changelog::Candidate),
+ Loading(changelog::Contribution),
Loaded {
pull_request: changelog::PullRequest,
description: Vec<markdown::Item>,
@@ -42,7 +39,7 @@ enum State {
#[derive(Debug, Clone)]
enum Message {
ChangelogListed(
- Result<(Changelog, Vec<changelog::Candidate>), changelog::Error>,
+ Result<(Changelog, Vec<changelog::Contribution>), changelog::Error>,
),
PullRequestFetched(Result<changelog::PullRequest, changelog::Error>),
UrlClicked(markdown::Url),
@@ -50,7 +47,8 @@ enum Message {
CategorySelected(changelog::Category),
Next,
OpenPullRequest(u64),
- CopyPreview,
+ ChangelogSaved(Result<(), changelog::Error>),
+ Quit,
}
impl Generator {
@@ -64,23 +62,23 @@ impl Generator {
fn update(&mut self, message: Message) -> Task<Message> {
match message {
Message::ChangelogListed(Ok((changelog, mut pending))) => {
- if let Some(candidate) = pending.pop() {
+ if let Some(contribution) = pending.pop() {
let preview =
markdown::parse(&changelog.to_string()).collect();
*self = Self::Reviewing {
changelog,
pending,
- state: State::Loading(candidate.clone()),
+ state: State::Loading(contribution.clone()),
preview,
};
Task::perform(
- candidate.fetch(),
+ changelog::PullRequest::fetch(contribution),
Message::PullRequestFetched,
)
} else {
- *self = Self::Empty;
+ *self = Self::Done;
Task::none()
}
@@ -108,12 +106,6 @@ impl Generator {
Task::none()
}
- Message::ChangelogListed(Err(error))
- | Message::PullRequestFetched(Err(error)) => {
- log::error!("{error}");
-
- Task::none()
- }
Message::UrlClicked(url) => {
let _ = webbrowser::open(url.as_str());
@@ -172,19 +164,27 @@ impl Generator {
{
changelog.push(entry);
+ let save = Task::perform(
+ changelog.clone().save(),
+ Message::ChangelogSaved,
+ );
+
*preview =
markdown::parse(&changelog.to_string()).collect();
- if let Some(candidate) = pending.pop() {
- *state = State::Loading(candidate.clone());
+ if let Some(contribution) = pending.pop() {
+ *state = State::Loading(contribution.clone());
- Task::perform(
- candidate.fetch(),
- Message::PullRequestFetched,
- )
+ Task::batch([
+ save,
+ Task::perform(
+ changelog::PullRequest::fetch(contribution),
+ Message::PullRequestFetched,
+ ),
+ ])
} else {
- // TODO: We are done!
- Task::none()
+ *self = Self::Done;
+ save
}
} else {
Task::none()
@@ -197,20 +197,32 @@ impl Generator {
Task::none()
}
- Message::CopyPreview => {
- let Self::Reviewing { changelog, .. } = self else {
- return Task::none();
- };
+ Message::ChangelogSaved(Ok(())) => Task::none(),
+
+ Message::ChangelogListed(Err(error))
+ | Message::PullRequestFetched(Err(error))
+ | Message::ChangelogSaved(Err(error)) => {
+ log::error!("{error}");
- clipboard::write(changelog.to_string())
+ Task::none()
}
+ Message::Quit => iced::exit(),
}
}
fn view(&self) -> Element<Message> {
match self {
Self::Loading => center("Loading...").into(),
- Self::Empty => center("No changes found!").into(),
+ Self::Done => center(
+ column![
+ text("Changelog is up-to-date! 🎉")
+ .shaping(text::Shaping::Advanced),
+ button("Quit").on_press(Message::Quit),
+ ]
+ .spacing(10)
+ .align_x(Center),
+ )
+ .into(),
Self::Reviewing {
changelog,
pending,
@@ -237,8 +249,8 @@ impl Generator {
};
let form: Element<_> = match state {
- State::Loading(candidate) => {
- text!("Loading #{}...", candidate.id).into()
+ State::Loading(contribution) => {
+ text!("Loading #{}...", contribution.id).into()
}
State::Loaded {
pull_request,
@@ -318,7 +330,7 @@ impl Generator {
}
};
- let preview: Element<_> = if preview.is_empty() {
+ let preview = if preview.is_empty() {
center(
container(
text("The changelog is empty... so far!").size(12),
@@ -326,9 +338,8 @@ impl Generator {
.padding(10)
.style(container::rounded_box),
)
- .into()
} else {
- let content = container(
+ container(
scrollable(
markdown::view(
preview,
@@ -343,14 +354,7 @@ impl Generator {
)
.width(Fill)
.padding(10)
- .style(container::rounded_box);
-
- let copy = button(icon::copy().size(12))
- .on_press(Message::CopyPreview)
- .style(button::text);
-
- center(stack![content, container(copy).align_right(Fill)])
- .into()
+ .style(container::rounded_box)
};
let review = column![container(form).height(Fill), progress]