Skip to content

Commit 42d0ac7

Browse files
committed
refactor(tui): separate the changelog generate/write logic
1 parent f1af005 commit 42d0ac7

File tree

7 files changed

+183
-103
lines changed

7 files changed

+183
-103
lines changed

git-cliff-core/src/changelog.rs

+26-20
Original file line numberDiff line numberDiff line change
@@ -28,39 +28,38 @@ use std::time::{
2828
};
2929

3030
/// Changelog generator.
31-
#[derive(Debug)]
31+
#[derive(Clone, Debug)]
3232
pub struct Changelog<'a> {
3333
/// Releases that the changelog will contain.
3434
pub releases: Vec<Release<'a>>,
35+
/// Configuration.
36+
pub config: Config,
3537
header_template: Option<Template>,
3638
body_template: Template,
3739
footer_template: Option<Template>,
38-
config: &'a Config,
3940
additional_context: HashMap<String, serde_json::Value>,
4041
}
4142

4243
impl<'a> Changelog<'a> {
4344
/// Constructs a new instance.
44-
pub fn new(releases: Vec<Release<'a>>, config: &'a Config) -> Result<Self> {
45-
let mut changelog = Changelog::build(releases, config)?;
46-
changelog.add_remote_data()?;
47-
changelog.process_commits();
48-
changelog.process_releases();
49-
Ok(changelog)
50-
}
51-
52-
/// Builds a changelog from releases and config.
53-
fn build(releases: Vec<Release<'a>>, config: &'a Config) -> Result<Self> {
45+
///
46+
/// Processes the commits/releases and fetches the remote data
47+
/// if `process_data` is set to `true`.
48+
pub fn new(
49+
releases: Vec<Release<'a>>,
50+
config: Config,
51+
process_data: bool,
52+
) -> Result<Self> {
5453
let trim = config.changelog.trim.unwrap_or(true);
55-
Ok(Self {
54+
let mut changelog = Self {
5655
releases,
5756
header_template: match &config.changelog.header {
5857
Some(header) => {
5958
Some(Template::new("header", header.to_string(), trim)?)
6059
}
6160
None => None,
6261
},
63-
body_template: get_body_template(config, trim)?,
62+
body_template: get_body_template(&config, trim)?,
6463
footer_template: match &config.changelog.footer {
6564
Some(footer) => {
6665
Some(Template::new("footer", footer.to_string(), trim)?)
@@ -69,12 +68,18 @@ impl<'a> Changelog<'a> {
6968
},
7069
config,
7170
additional_context: HashMap::new(),
72-
})
71+
};
72+
if process_data {
73+
changelog.add_remote_data()?;
74+
changelog.process_commits();
75+
changelog.process_releases();
76+
}
77+
Ok(changelog)
7378
}
7479

7580
/// Constructs an instance from a serialized context object.
76-
pub fn from_context<R: Read>(input: &mut R, config: &'a Config) -> Result<Self> {
77-
Changelog::build(serde_json::from_reader(input)?, config)
81+
pub fn from_context<R: Read>(input: &mut R, config: Config) -> Result<Self> {
82+
Changelog::new(serde_json::from_reader(input)?, config, false)
7883
}
7984

8085
/// Adds a key value pair to the template context.
@@ -431,6 +436,7 @@ impl<'a> Changelog<'a> {
431436

432437
/// Adds information about the remote to the template context.
433438
pub fn add_remote_context(&mut self) -> Result<()> {
439+
debug!("Adding remote context...");
434440
self.additional_context.insert(
435441
"remote".to_string(),
436442
serde_json::to_value(self.config.remote.clone())?,
@@ -1024,7 +1030,7 @@ mod test {
10241030
#[test]
10251031
fn changelog_generator() -> Result<()> {
10261032
let (config, releases) = get_test_data();
1027-
let mut changelog = Changelog::new(releases, &config)?;
1033+
let mut changelog = Changelog::new(releases, config, true)?;
10281034
changelog.bump_version()?;
10291035
changelog.releases[0].timestamp = 0;
10301036
let mut out = Vec::new();
@@ -1143,7 +1149,7 @@ chore(deps): fix broken deps
11431149
",
11441150
),
11451151
));
1146-
let changelog = Changelog::new(releases, &config)?;
1152+
let changelog = Changelog::new(releases, config, true)?;
11471153
let mut out = Vec::new();
11481154
changelog.generate(&mut out)?;
11491155
assert_eq!(
@@ -1247,7 +1253,7 @@ chore(deps): fix broken deps
12471253
{% endfor %}{% endfor %}"#
12481254
.to_string(),
12491255
);
1250-
let mut changelog = Changelog::new(releases, &config)?;
1256+
let mut changelog = Changelog::new(releases, config, true)?;
12511257
changelog.add_context("custom_field", "Hello")?;
12521258
let mut out = Vec::new();
12531259
changelog.generate(&mut out)?;

git-cliff-core/src/template.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use tera::{
2020
};
2121

2222
/// Wrapper for [`Tera`].
23-
#[derive(Debug)]
23+
#[derive(Clone, Debug)]
2424
pub struct Template {
2525
/// Template name.
2626
name: String,

git-cliff-tui/src/event.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -122,17 +122,21 @@ pub fn handle_key_events(
122122
KeyCode::Char('c') | KeyCode::Char('C') => {
123123
if key_event.modifiers == KeyModifiers::CONTROL {
124124
state.quit();
125-
} else if let Some(clipboard) = &mut state.clipboard {
126-
if let Err(e) = clipboard.set_contents(state.changelog.clone()) {
127-
eprintln!("Failed to set clipboard contents: {e}");
125+
} else if let Some(contents) = state.get_changelog_contents()? {
126+
if let Some(clipboard) = &mut state.clipboard {
127+
if let Err(e) = clipboard.set_contents(contents) {
128+
eprintln!("Failed to set clipboard contents: {e}");
129+
}
128130
}
129131
}
130132
}
131133
KeyCode::Char('k') | KeyCode::Char('K') | KeyCode::Up => {
132134
state.list_state.select_previous();
135+
state.process_changelog()?;
133136
}
134137
KeyCode::Char('j') | KeyCode::Char('J') | KeyCode::Down => {
135138
state.list_state.select_next();
139+
state.process_changelog()?;
136140
}
137141
KeyCode::Char('h') | KeyCode::Char('H') | KeyCode::Left => {
138142
state.markdown.scroll_index =

git-cliff-tui/src/main.rs

+36-36
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
use std::{
2-
path::{
3-
Path,
4-
PathBuf,
5-
},
6-
thread,
1+
use std::path::{
2+
Path,
3+
PathBuf,
74
};
85

96
use crate::{
@@ -60,6 +57,9 @@ fn main() -> Result<()> {
6057
}
6158
}
6259

60+
// Generate the changelog.
61+
state.generate_changelog()?;
62+
6363
// Initialize the terminal user interface.
6464
let events = EventHandler::new(250);
6565
let mut terminal = ratatui::init();
@@ -104,37 +104,37 @@ fn main() -> Result<()> {
104104
Event::Mouse(_) => {}
105105
Event::Resize(_, _) => {}
106106
Event::Generate | Event::AutoGenerate => {
107-
if event == Event::AutoGenerate && !state.autoload {
108-
continue;
109-
}
110-
let sender = events.sender.clone();
111-
let args = state.args.clone();
112-
state.is_generating = true;
113-
state.args.config = PathBuf::from(
114-
state.configs[state.list_state.selected().unwrap_or_default()]
115-
.file
116-
.clone(),
117-
);
118-
thread::spawn(move || {
119-
let mut output = Vec::new();
120-
sender
121-
.send(match git_cliff::run(args, &mut output) {
122-
Ok(()) => Event::RenderMarkdown(
123-
String::from_utf8_lossy(&output).to_string(),
124-
),
125-
Err(e) => Event::Error(e.to_string()),
126-
})
127-
.expect("failed to send event");
128-
});
107+
// if event == Event::AutoGenerate && !state.autoload {
108+
// continue;
109+
// }
110+
// let sender = events.sender.clone();
111+
// let args = state.args.clone();
112+
// state.is_generating = true;
113+
// state.args.config = PathBuf::from(
114+
// state.configs[state.list_state.selected().
115+
// unwrap_or_default()] .file
116+
// .clone(),
117+
// );
118+
// thread::spawn(move || {
119+
// let mut output = Vec::new();
120+
// sender
121+
// .send(match git_cliff::run(args, &mut output) {
122+
// Ok(()) => Event::RenderMarkdown(
123+
// String::from_utf8_lossy(&output).to_string(),
124+
// ),
125+
// Err(e) => Event::Error(e.to_string()),
126+
// })
127+
// .expect("failed to send event");
128+
// });
129129
}
130-
Event::RenderMarkdown(changelog) => {
131-
state.is_generating = false;
132-
state.changelog = changelog;
133-
state.markdown.component = Some(md_tui::parser::parse_markdown(
134-
None,
135-
&state.changelog,
136-
state.markdown.area.width,
137-
));
130+
Event::RenderMarkdown(_) => {
131+
// state.is_generating = false;
132+
// state.changelog = changelog;
133+
// state.markdown.component =
134+
// Some(md_tui::parser::parse_markdown( None,
135+
// &state.changelog,
136+
// state.markdown.area.width,
137+
// ));
138138
}
139139
Event::Error(e) => {
140140
state.error = Some(e);

git-cliff-tui/src/state.rs

+49-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use copypasta::ClipboardContext;
22
use git_cliff::args::Args;
3+
use git_cliff::core::changelog::Changelog;
34
use git_cliff::core::embed::BuiltinConfig;
45
use md_tui::nodes::root::ComponentRoot;
56
use ratatui::layout::Rect;
@@ -32,7 +33,7 @@ pub struct Markdown {
3233

3334
/// Is the application running?
3435
/// Application state.
35-
pub struct State {
36+
pub struct State<'a> {
3637
/// git-cliff arguments.
3738
pub args: Args,
3839
/// Is the application running?
@@ -41,8 +42,8 @@ pub struct State {
4142
pub configs: Vec<Config>,
4243
/// The state of the list.
4344
pub list_state: ListState,
44-
/// Changelog contents.
45-
pub changelog: String,
45+
/// Changelog.
46+
pub changelog: Option<Changelog<'a>>,
4647
/// Error message.
4748
pub error: Option<String>,
4849
/// Rendered markdown.
@@ -59,7 +60,7 @@ pub struct State {
5960
pub is_generating: bool,
6061
}
6162

62-
impl State {
63+
impl<'a> State<'a> {
6364
/// Constructs a new instance.
6465
pub fn new(args: Args) -> Result<Self> {
6566
let configs = BuiltinConfig::iter()
@@ -78,7 +79,7 @@ impl State {
7879
list_state.select_first();
7980
list_state
8081
},
81-
changelog: String::new(),
82+
changelog: None,
8283
error: None,
8384
markdown: Markdown::default(),
8485
autoload: true,
@@ -93,6 +94,49 @@ impl State {
9394
})
9495
}
9596

97+
/// Generates the changelog.
98+
pub fn generate_changelog(&mut self) -> Result<()> {
99+
self.changelog = Some(git_cliff::generate_changelog(&mut self.args)?);
100+
Ok(())
101+
}
102+
103+
/// Returns the changelog contents.
104+
pub fn get_changelog_contents(&mut self) -> Result<Option<String>> {
105+
if let Some(changelog) = &self.changelog {
106+
let config = git_cliff::core::embed::BuiltinConfig::parse(
107+
self.configs[self.list_state.selected().unwrap_or_default()]
108+
.file
109+
.clone(),
110+
)?
111+
.0;
112+
let changelog =
113+
Changelog::new(changelog.releases.clone(), config.clone(), false)?;
114+
let mut output = Vec::new();
115+
git_cliff::write_changelog(
116+
self.args.clone(),
117+
changelog.clone(),
118+
&mut output,
119+
)?;
120+
let contents = String::from_utf8(output)?;
121+
self.changelog = Some(changelog);
122+
Ok(Some(contents))
123+
} else {
124+
Ok(None)
125+
}
126+
}
127+
128+
/// Processes the changelog contents.
129+
pub fn process_changelog(&mut self) -> Result<()> {
130+
if let Some(contents) = &self.get_changelog_contents()? {
131+
self.markdown.component = Some(md_tui::parser::parse_markdown(
132+
None,
133+
&contents,
134+
self.markdown.area.width,
135+
));
136+
}
137+
Ok(())
138+
}
139+
96140
/// Handles the tick event of the terminal.
97141
pub fn tick(&mut self) {
98142
if self.is_generating {

0 commit comments

Comments
 (0)