1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// Copyright 2020 Ian Jackson
// SPDX-License-Identifier: GPL-3.0-or-later
// There is NO WARRANTY.

use crate::imports::*;
use crate::utils::ErrorMap;

use percent_encoding::{utf8_percent_encode,NON_ALPHANUMERIC};

#[derive(Debug,Clone,Eq,PartialEq)]
#[derive(Serialize,Deserialize)]
pub struct Html(pub String);

#[derive(Debug,Eq,PartialEq)]
#[derive(Serialize,Deserialize)]
pub struct HtmlRef<'r>(pub &'r str);

impl Html {
  pub fn from_literal(lit : &str) -> Html {
    Html(htmlescape::encode_minimal(lit))
  }
  pub fn as_ref(&self) -> HtmlRef { HtmlRef(self.0.as_ref()) }
}

impl HtmlRef<'_> {
  pub fn to_owned(&self) -> Html { Html(self.0.to_owned()) }
}

pub const INDEX_BASENAME : &str = "index";
pub const INDEX          : &str = "index.html";
    
pub struct IndexWriter {
  f : BufWriter<File>,
  deferred_error : Result<(),io::Error>,
}

enum TableState {
  Unstarted(Html),
  Working,
}
use TableState::*;

pub struct TableWriter<'i> {
  i : &'i mut IndexWriter,
  s : TableState,
}

impl IndexWriter {
  #[throws(E)]
  pub fn new(outdir : &str, title : &HtmlRef) -> IndexWriter {
    let f = File::create(outdir.to_owned() + "/" + INDEX).cxm(||format!(
      "create index file {}", INDEX))?;
    let mut f = BufWriter::new(f);

    writeln!(&mut f,
             r#"<html><head><title>{}</title><head><body>"#,
             title.0)?;
    
    IndexWriter { f, deferred_error : Ok(()) }
  }

  #[throws(E)]
  fn check(&mut self) {
    replace(&mut self.deferred_error, Ok(()))?;
  }

  #[throws(E)]
  pub fn table(&mut self, heading : Html) -> TableWriter {
    self.check()?;
    TableWriter { i : self, s : Unstarted(heading) }
  }

  #[throws(E)]
  pub fn finish(mut self) {
    self.check()?;
    writeln!(&mut self.f,
             r#"</body></html>"#)?;
    self.f.flush()?;
  }
}
  
impl TableWriter<'_> {
  #[throws(E)]
  pub fn entry(&mut self, filename : &str, desc : &HtmlRef) {
    match &mut self.s {
      Unstarted(ref mut heading) => {
        writeln!(&mut self.i.f,
                 r#"<h1>{}</h1><table>"#,
                 heading.0)?;
        self.s = Working;
      },
      Working => { },
    }

    writeln!(&mut self.i.f,
             r#"<tr><th align="left"><a href="{}">{}</a></th><td>{}</td>"#,
             utf8_percent_encode(filename, NON_ALPHANUMERIC),
             Html::from_literal(filename).0,
             desc.0
    )?;
  }
}

impl Drop for TableWriter<'_> {
  fn drop(&mut self) {
    match self.s {
      Unstarted(_) => { }
      Working => {
        self.i.deferred_error =
          writeln!(&mut self.i.f, r#"</table>"#);
      }
    }
  }
}