Skip to content

Commit e8d5749

Browse files
committed
show node error
1 parent 2f5b5f8 commit e8d5749

File tree

13 files changed

+338
-244
lines changed

13 files changed

+338
-244
lines changed

tiron-common/src/action.rs

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ impl ActionId {
1818

1919
#[derive(Deserialize, Serialize)]
2020
pub enum ActionMessage {
21+
NodeStartFailed { reason: String },
2122
ActionStarted { id: ActionId },
2223
ActionStdout { id: ActionId, content: String },
2324
ActionStderr { id: ActionId, content: String },

tiron-node/src/action/data.rs

+1-67
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
1-
use std::{collections::HashMap, path::Path};
2-
3-
use anyhow::{anyhow, Result};
4-
use rcl::runtime::Value;
5-
use tiron_common::action::{ActionData, ActionId};
6-
7-
use crate::job::Job;
1+
use std::collections::HashMap;
82

93
use super::{copy::CopyAction, package::PackageAction, Action};
104

@@ -19,63 +13,3 @@ pub fn all_actions() -> HashMap<String, Box<dyn Action>> {
1913
.into_iter()
2014
.collect()
2115
}
22-
23-
pub fn parse_value(cwd: &Path, value: &Value) -> Result<Vec<ActionData>> {
24-
let Value::List(action_values) = value else {
25-
return Err(anyhow!("actions should be a list"));
26-
};
27-
28-
let all_actions = all_actions();
29-
30-
let mut actions = Vec::new();
31-
for action_value in action_values.iter() {
32-
let Value::Dict(dict) = action_value else {
33-
return Err(anyhow!("action should be a dict"));
34-
};
35-
let Some(action) = dict.get(&Value::String("action".into())) else {
36-
return Err(anyhow!("missing action key in action"));
37-
};
38-
let Value::String(action_name) = action else {
39-
return Err(anyhow!("action key should be string"));
40-
};
41-
42-
let name = if let Some(name) = dict.get(&Value::String("name".into())) {
43-
let Value::String(name) = name else {
44-
return Err(anyhow!("name should be string"));
45-
};
46-
Some(name.to_string())
47-
} else {
48-
None
49-
};
50-
51-
if action_name.as_ref() == "job" {
52-
let Some(params) = dict.get(&Value::String("params".into())) else {
53-
return Err(anyhow!("job needs params"));
54-
};
55-
let Value::Dict(params) = params else {
56-
return Err(anyhow!("params should be a dict"));
57-
};
58-
let Some(job_name) = params.get(&Value::String("name".into())) else {
59-
return Err(anyhow!("missing job name in action"));
60-
};
61-
let Value::String(job_name) = job_name else {
62-
return Err(anyhow!("job name should be string"));
63-
};
64-
let mut job_actions = Job::load(cwd, job_name)?;
65-
actions.append(&mut job_actions);
66-
} else {
67-
let Some(action) = all_actions.get(action_name.as_ref()) else {
68-
return Err(anyhow!("action {action_name} can't be found"));
69-
};
70-
let params = dict.get(&Value::String("params".into()));
71-
let input = action.input(cwd, params)?;
72-
actions.push(ActionData {
73-
id: ActionId::new(),
74-
name: name.unwrap_or_else(|| action_name.to_string()),
75-
action: action_name.to_string(),
76-
input,
77-
});
78-
}
79-
}
80-
Ok(actions)
81-
}

tiron-node/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
pub mod action;
2-
mod job;
32
pub mod node;
43
pub mod stdio;

tiron-tui/src/app.rs

+3
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ impl App {
156156
ActionMessage::NodeShutdown { success } => {
157157
host.success = Some(success);
158158
}
159+
ActionMessage::NodeStartFailed { reason } => {
160+
host.start_failed = Some(reason);
161+
}
159162
}
160163
Ok(())
161164
}

tiron-tui/src/run.rs

+78-58
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub struct HostSection {
1919
pub actions: Vec<ActionSection>,
2020
pub scroll: u16,
2121
pub success: Option<bool>,
22+
pub start_failed: Option<String>,
2223
}
2324

2425
impl HostSection {
@@ -42,6 +43,19 @@ impl HostSection {
4243

4344
let mut y = 0;
4445

46+
if let Some(reason) = &self.start_failed {
47+
render_line(
48+
area,
49+
buf,
50+
&mut y,
51+
self.scroll,
52+
&format!("host start failed: {reason}"),
53+
Some(Color::Red),
54+
None,
55+
);
56+
y += 1;
57+
}
58+
4559
for action in &self.actions {
4660
action.render(area, buf, &mut y, self.scroll);
4761
y += 1;
@@ -106,8 +120,14 @@ impl RunPanel {
106120
}
107121
}
108122

123+
fn get_active_host(&self) -> Result<&HostSection> {
124+
let active = self.active.min(self.hosts.len().saturating_sub(1));
125+
let host = self.hosts.get(active).ok_or_else(|| anyhow!("no host"))?;
126+
Ok(host)
127+
}
128+
109129
pub fn render(&self, area: Rect, buf: &mut Buffer) {
110-
if let Some(host) = self.hosts.first() {
130+
if let Ok(host) = self.get_active_host() {
111131
host.render(area, buf);
112132
}
113133
}
@@ -146,6 +166,7 @@ impl HostSection {
146166
actions,
147167
scroll: 0,
148168
success: None,
169+
start_failed: None,
149170
}
150171
}
151172
}
@@ -172,7 +193,7 @@ impl ActionSection {
172193
} else {
173194
Color::Gray
174195
};
175-
self.render_line(area, buf, y, scroll, &self.name, None, Some(bg));
196+
render_line(area, buf, y, scroll, &self.name, None, Some(bg));
176197
*y += 1;
177198
if self.folded {
178199
return;
@@ -186,71 +207,70 @@ impl ActionSection {
186207
ActionOutputLevel::Warn => Some(Color::Yellow),
187208
ActionOutputLevel::Error => Some(Color::Red),
188209
};
189-
self.render_line(area, buf, y, scroll, &line.content, fg, None);
210+
render_line(area, buf, y, scroll, &line.content, fg, None);
190211
if *y >= area.height + scroll {
191212
return;
192213
}
193214
}
194215
}
216+
}
195217

196-
#[allow(clippy::too_many_arguments)]
197-
fn render_line(
198-
&self,
199-
area: Rect,
200-
buf: &mut Buffer,
201-
y: &mut u16,
202-
scroll: u16,
203-
line: &str,
204-
fg: Option<Color>,
205-
bg: Option<Color>,
206-
) {
207-
let style = Style::default();
208-
let style = if let Some(fg) = fg {
209-
style.fg(fg)
210-
} else {
211-
style
212-
};
213-
let mut line_composer = WordWrapper::new(
214-
vec![(
215-
line.graphemes(true)
216-
.map(move |g| StyledGrapheme { symbol: g, style }),
217-
Alignment::Left,
218-
)]
219-
.into_iter(),
220-
area.width,
221-
false,
222-
);
218+
#[allow(clippy::too_many_arguments)]
219+
fn render_line(
220+
area: Rect,
221+
buf: &mut Buffer,
222+
y: &mut u16,
223+
scroll: u16,
224+
line: &str,
225+
fg: Option<Color>,
226+
bg: Option<Color>,
227+
) {
228+
let style = Style::default();
229+
let style = if let Some(fg) = fg {
230+
style.fg(fg)
231+
} else {
232+
style
233+
};
234+
let mut line_composer = WordWrapper::new(
235+
vec![(
236+
line.graphemes(true)
237+
.map(move |g| StyledGrapheme { symbol: g, style }),
238+
Alignment::Left,
239+
)]
240+
.into_iter(),
241+
area.width,
242+
false,
243+
);
223244

224-
while let Some(WrappedLine {
225-
line: current_line,
226-
width: current_line_width,
227-
alignment: current_line_alignment,
228-
}) = line_composer.next_line()
229-
{
230-
if *y >= scroll {
231-
if let Some(bg) = bg {
232-
let area = Rect::new(area.left(), area.top() + *y - scroll, area.width, 1);
233-
buf.set_style(area, Style::default().bg(bg));
234-
}
235-
let mut x = get_line_offset(current_line_width, area.width, current_line_alignment);
236-
for StyledGrapheme { symbol, style } in current_line {
237-
let width = symbol.width();
238-
if width == 0 {
239-
continue;
240-
}
241-
// If the symbol is empty, the last char which rendered last time will
242-
// leave on the line. It's a quick fix.
243-
let symbol = if symbol.is_empty() { " " } else { symbol };
244-
buf.get_mut(area.left() + x, area.top() + *y - scroll)
245-
.set_symbol(symbol)
246-
.set_style(*style);
247-
x += width as u16;
248-
}
245+
while let Some(WrappedLine {
246+
line: current_line,
247+
width: current_line_width,
248+
alignment: current_line_alignment,
249+
}) = line_composer.next_line()
250+
{
251+
if *y >= scroll {
252+
if let Some(bg) = bg {
253+
let area = Rect::new(area.left(), area.top() + *y - scroll, area.width, 1);
254+
buf.set_style(area, Style::default().bg(bg));
249255
}
250-
*y += 1;
251-
if *y >= area.height + scroll {
252-
break;
256+
let mut x = get_line_offset(current_line_width, area.width, current_line_alignment);
257+
for StyledGrapheme { symbol, style } in current_line {
258+
let width = symbol.width();
259+
if width == 0 {
260+
continue;
261+
}
262+
// If the symbol is empty, the last char which rendered last time will
263+
// leave on the line. It's a quick fix.
264+
let symbol = if symbol.is_empty() { " " } else { symbol };
265+
buf.get_mut(area.left() + x, area.top() + *y - scroll)
266+
.set_symbol(symbol)
267+
.set_style(*style);
268+
x += width as u16;
253269
}
254270
}
271+
*y += 1;
272+
if *y >= area.height + scroll {
273+
break;
274+
}
255275
}
256276
}

tiron/src/action.rs

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use std::{collections::HashMap, path::Path};
2+
3+
use anyhow::{anyhow, Result};
4+
use rcl::runtime::Value;
5+
use tiron_common::action::{ActionData, ActionId};
6+
use tiron_node::action::data::all_actions;
7+
8+
use crate::job::Job;
9+
10+
pub fn parse_actions(
11+
cwd: &Path,
12+
value: &Value,
13+
vars: &HashMap<String, String>,
14+
) -> Result<Vec<ActionData>> {
15+
let Value::List(action_values) = value else {
16+
return Err(anyhow!("actions should be a list"));
17+
};
18+
19+
let all_actions = all_actions();
20+
21+
let mut actions = Vec::new();
22+
for action_value in action_values.iter() {
23+
let Value::Dict(dict) = action_value else {
24+
return Err(anyhow!("action should be a dict"));
25+
};
26+
let Some(action) = dict.get(&Value::String("action".into())) else {
27+
return Err(anyhow!("missing action key in action"));
28+
};
29+
let Value::String(action_name) = action else {
30+
return Err(anyhow!("action key should be string"));
31+
};
32+
33+
let name = if let Some(name) = dict.get(&Value::String("name".into())) {
34+
let Value::String(name) = name else {
35+
return Err(anyhow!("name should be string"));
36+
};
37+
Some(name.to_string())
38+
} else {
39+
None
40+
};
41+
42+
if action_name.as_ref() == "job" {
43+
let Some(params) = dict.get(&Value::String("params".into())) else {
44+
return Err(anyhow!("job needs params"));
45+
};
46+
let Value::Dict(params) = params else {
47+
return Err(anyhow!("params should be a dict"));
48+
};
49+
let Some(job_name) = params.get(&Value::String("name".into())) else {
50+
return Err(anyhow!("missing job name in action"));
51+
};
52+
let Value::String(job_name) = job_name else {
53+
return Err(anyhow!("job name should be string"));
54+
};
55+
let mut job_actions = Job::load(cwd, job_name, vars)?;
56+
actions.append(&mut job_actions);
57+
} else {
58+
let Some(action) = all_actions.get(action_name.as_ref()) else {
59+
return Err(anyhow!("action {action_name} can't be found"));
60+
};
61+
let params = dict.get(&Value::String("params".into()));
62+
let input = action.input(cwd, params)?;
63+
actions.push(ActionData {
64+
id: ActionId::new(),
65+
name: name.unwrap_or_else(|| action_name.to_string()),
66+
action: action_name.to_string(),
67+
input,
68+
});
69+
}
70+
}
71+
Ok(actions)
72+
}

0 commit comments

Comments
 (0)