bundle-sources 0.0.1

library and program for generating source code bundles (eg for AGPL compliance
Documentation
// 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>"#);
      }
    }
  }
}