diff options
Diffstat (limited to '')
| -rw-r--r-- | examples/changelog/src/changelog.rs | 69 | ||||
| -rw-r--r-- | examples/changelog/src/main.rs | 96 | 
2 files changed, 99 insertions, 66 deletions
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]  | 
