feat: add a table of contents to the run log

Signed-off-by: Lars Wirzenius <liw@liw.fi>
This commit is contained in:
Lars Wirzenius
2024-03-31 14:51:56 +03:00
parent 7746b37c33
commit 2fa62ab846
2 changed files with 67 additions and 24 deletions
+7
View File
@@ -17,6 +17,13 @@ fn main() {
"This is stdout".as_bytes(),
"Error messages go here".as_bytes(),
);
run_log.runcmd(
&["cargo", "check", "--all-targets", "--workspace"],
Path::new("/tmp"),
0,
"This is stdout".as_bytes(),
"Error messages go here".as_bytes(),
);
run_log.write().expect("write html log");
}
+60 -24
View File
@@ -67,7 +67,6 @@ impl RunLog {
let mut doc = Document::default();
doc.push_to_head(&Element::new(Tag::Title).with_text(title));
doc.push_to_body(&Element::new(Tag::H1).with_text(title));
let mut ul = Element::new(Tag::Ul);
@@ -95,15 +94,20 @@ impl RunLog {
doc.push_to_body(&error);
}
for cmd in self.commands.iter() {
cmd.format(&mut doc);
}
if self.timeout {
let timeout = Element::new(Tag::P).with_text("Last command timed out");
doc.push_to_body(&timeout);
}
let mut toc = ToC::default();
let mut body = Element::new(Tag::Div);
for cmd in self.commands.iter() {
cmd.format(&mut body, &mut toc);
}
doc.push_to_body(&toc.as_html());
doc.push_to_body(&body);
let html = format!("{}", doc);
std::fs::write(&self.filename, html.as_bytes())
.map_err(|e| RunLogError::Write(self.filename.clone(), e))?;
@@ -134,14 +138,16 @@ impl Command {
self.argv.as_slice().join(" ")
}
fn format(&self, doc: &mut Document) {
doc.push_to_body(
&Element::new(Tag::H2)
.with_text("Run: ")
.with_child(Element::new(Tag::Code).with_text(&self.command())),
fn format(&self, body: &mut Element, toc: &mut ToC) {
body.push_child(
&toc.push(
Element::new(Tag::Span)
.with_text("Run: ")
.with_child(Element::new(Tag::Code).with_text(&self.command())),
),
);
doc.push_to_body(&Element::new(Tag::P).with_text("Command arguments:"));
body.push_child(&Element::new(Tag::P).with_text("Command arguments:"));
let mut ul = Element::new(Tag::Ul);
for arg in self.argv.iter() {
ul.push_child(
@@ -149,34 +155,64 @@ impl Command {
.with_child(Element::new(Tag::Code).with_text(&format!("{:?}", arg))),
);
}
doc.push_to_body(&ul);
body.push_child(&ul);
doc.push_to_body(
body.push_child(
&Element::new(Tag::P)
.with_text("In directory: ")
.with_child(Element::new(Tag::Code).with_text(&format!("{}", self.cwd.display()))),
);
doc.push_to_body(&Element::new(Tag::P).with_text(&format!("Exit code: {}", self.exit)));
body.push_child(&Element::new(Tag::P).with_text(&format!("Exit code: {}", self.exit)));
self.output(doc, "Stdout:", &self.stdout);
self.output(doc, "Stderr:", &self.stderr);
self.output(body, "Stdout:", &self.stdout);
self.output(body, "Stderr:", &self.stderr);
}
fn output(&self, doc: &mut Document, stream: &str, data: &[u8]) {
fn output(&self, body: &mut Element, stream: &str, data: &[u8]) {
if !data.is_empty() {
doc.push_to_body(&Element::new(Tag::P).with_text(stream));
doc.push_to_body(
&Element::new(Tag::P).with_child(
Element::new(Tag::Blockquote).with_child(
Element::new(Tag::Pre).with_text(&String::from_utf8_lossy(data)),
),
),
body.push_child(&Element::new(Tag::P).with_text(stream));
body.push_child(
&Element::new(Tag::Blockquote)
.with_child(Element::new(Tag::Pre).with_text(&String::from_utf8_lossy(data))),
);
}
}
}
#[derive(Debug, Default)]
struct ToC {
counter: usize,
headings: Vec<(String, Element)>,
}
impl ToC {
fn push(&mut self, content: Element) -> Element {
self.counter += 1;
let anchor = format!("h{}", self.counter);
self.headings.push((anchor.clone(), content.clone()));
Element::new(Tag::H2)
.with_attribute("id", &format!("h{}", self.counter))
.with_child(content)
}
fn as_html(&self) -> Element {
let mut toc =
Element::new(Tag::Div).with_child(Element::new(Tag::H2).with_text("Table of contents"));
let mut list = Element::new(Tag::Ul);
for (anchor, content) in self.headings.iter() {
let entry = Element::new(Tag::Li).with_child(
Element::new(Tag::A)
.with_attribute("href", &format!("#{anchor}"))
.with_child(content.clone()),
);
list.push_child(&entry);
}
toc.push_child(&list);
toc
}
}
#[derive(Debug, thiserror::Error)]
pub enum RunLogError {
#[error("programming error: missing field for run log {0}")]