#+TITLE:       doc_reform config & header (make & meta) extract
#+DESCRIPTION: documents - structuring, publishing in multiple formats & search
#+FILETAGS:    :doc_reform:config:
#+AUTHOR:      Ralph Amissah
#+EMAIL:       [[mailto:ralph.amissah@gmail.com][ralph.amissah@gmail.com]]
#+COPYRIGHT:   Copyright (C) 2015 - 2019 Ralph Amissah
#+LANGUAGE:    en
#+STARTUP:     indent content hideblocks hidestars
#+OPTIONS:     H:3 num:nil toc:t \n:nil @:t ::t |:t ^:nil _:nil -:t f:t *:t <:t
#+OPTIONS:     TeX:t LaTeX:t skip:nil d:nil todo:t pri:nil tags:not-in-toc
#+OPTIONS:     author:nil email:nil creator:nil timestamp:nil
#+PROPERTY:    header-args :padline no :exports code :cache no :noweb yes
#+EXPORT_SELECT_TAGS:  export
#+EXPORT_EXCLUDE_TAGS: noexport
#+TAGS: assert(a) class(c) debug(d) mixin(m) doc_reform(s) tangle(T) template(t) WEB(W) noexport(n)

[[./doc_reform.org][doc_reform]]  [[./][org/]]
* 0. generic
** imports

#+name: meta_defaults_imports
#+BEGIN_SRC d
import
  std.algorithm,
  std.array,
  std.container,
  std.exception,
  std.file,
  std.getopt,
  std.json,
  std.path,
  std.process,
  std.range,
  std.regex,
  std.stdio,
  std.string,
  std.traits,
  std.typecons,
  std.uni,
  std.utf,
  std.conv : to;
import doc_reform.meta.conf_make_meta_structs;
#+END_SRC

** struct ConfComposite

#+BEGIN_SRC d :tangle "../src/doc_reform/meta/conf_make_meta_structs.d"
module doc_reform.meta.conf_make_meta_structs;
<<meta_defaults_template_structs>>
#+END_SRC

** initialize, imports etc.

#+name: meta_defaults_template_structs
#+BEGIN_SRC d
import
  std.exception,
  std.json,
  std.regex,
  std.stdio,
  std.string,
  std.traits,
  std.typecons,
  std.utf,
  std.conv : to;
import
  doc_reform.meta.defaults,
  doc_reform.meta.rgx;
mixin DocReformRgxInit;
static auto rgx = Rgx();
mixin InternalMarkup;
auto mkup = InlineMarkup();
#+END_SRC

** struct Generic ConfComposite

#+name: meta_defaults_template_structs
#+BEGIN_SRC d
string url_markup(string line) {
  string line_ = line
    .replaceAll(
      rgx.smid_inline_link_markup_regular,
      ("$1"
        ~ mkup.lnk_o ~ "$2" ~ mkup.lnk_c
        ~ mkup.url_o ~ "$3" ~ mkup.url_c
      ) // ("$1{ $2 }$3$4")
    )
    .replaceAll(
        rgx.smid_inline_link_naked_url,
        ("$1"
          ~ mkup.lnk_o ~ "$2" ~ mkup.lnk_c
          ~ mkup.url_o ~ "$2" ~ mkup.url_c
        ) // ("$1{ $2 }$2$3")
    )
    .replaceAll(
       rgx.arr_delimiter,
       mkup.br_line
    );
  return line_;
}
struct ConfCompositeMakeStr {
  string     bold;
  string     breaks;
  string     cover_image;
  string     css;
  string     emphasis;
  string[]   footer;
  string[]   headings;
  string[]   home_button_image;
  string     home_button_text = "{Doc Reform}http://www.doc-reform.org;"
    ~ " {www.doc-reform.org}http://www.doc-reform.org;"
    ~ " {sources / git}https://git.doc-reform.org/software/doc-reform";
  string     italics;
  string     auto_num_top_at_level;
  int        auto_num_top_lv           = 9;
  int        auto_num_depth            = 2;
  string[][] substitute;
  string     texpdf_font;
}
struct confCompositeMakeBuild {
  string[] bold(string _mk) {
    string[] _out;
    if (_mk) {
      _out = [ (cast(string) (`(` ~ _mk.dup ~ `)`)), "*{$1}*", "<b>$1</b>"];
    }
    return _out;
  }
  string breaks(string _mk) {
    return _mk;
  }
  string cover_image(string _mk) {
    return _mk;
  }
  string css(string _mk) {
    return _mk;
  }
  string[] emphasis(string _mk) {
    string[] _out;
    if (_mk) {
      _out = [ (cast(string) (`(` ~ _mk.dup ~ `)`)), "!{$1}!", "<em>$1</em>" ];
    }
    return _out;
  }
  string[] footer(string[] _mk) {
    string line_;
    string[] _mk2;
    foreach (line; _mk) {
      _mk2 ~= url_markup(line);
    }
    return _mk2;
  }
  string[] headings(string[] _mk) {
    return _mk;
  }
  string[] home_button_image(string[] _mk) {
    return _mk;
  }
  string home_button_text(string _mk) {
    return url_markup(_mk);
  }
  string[] italics(string _mk) {
    string[] _out;
    if (_mk) {
      _out = [ (cast(string) (`(` ~ _mk.dup ~ `)`)), "/{$1}/", "<i>$1</i>" ];
    }
    return _out;
  }
  string auto_num_top_at_level(string _mk) {
    return _mk;
  }
  int auto_num_top_lv(int _mk) {
    return _mk;
  }
  int auto_num_depth(int _mk) {
    return _mk;
  }
  string[][] substitute(string[][] _mk) {
    return _mk;
  }
  string texpdf_font(string _mk) {
    return _mk;
  }
}
#+END_SRC

** initialize make & meta
*** composite make

#+name: meta_defaults_template_structs
#+BEGIN_SRC d
struct ConfCompositeMakeInit {
  string[]   bold;
  string     breaks;
  string     cover_image;
  string     css;
  string[]   emphasis;
  string[]   footer;
  string[]   headings;
  string[]   home_button_image;
  string     home_button_text = "{Doc Reform}http://www.doc-reform.org;"
    ~ " {www.doc-reform.org}http://www.doc-reform.org;"
    ~ " {sources / git}https://git.doc-reform.org/software/doc-reform";
  string[] italics;
  string     auto_num_top_at_level;
  int        auto_num_top_lv               = 9;
  int        auto_num_depth                = 2;
  string[][] substitute;
  string     texpdf_font;
}
#+END_SRC

*** conf site local

#+name: meta_defaults_template_structs
#+BEGIN_SRC d
struct ConfCompositeSiteLocal {
  string webserv_url_doc_root;
  string webserv_url_domain;
  string webserv_url_doc_path;
  string webserv_images;
  string webserv_cgi;
  string webserv_cgi_host;
  string webserv_cgi_host_path;
  string webserv_cgi_port;
  string webserv_cgi_user;
  string webserv_cgi_file_links;
  string processing_path;
  string processing_dir;
  string processing_concord_max;
  string flag_act0;
  string flag_act1;
  string flag_act2;
  string flag_act3;
  string flag_act4;
  string flag_act5;
  string flag_act6;
  string flag_act7;
  string flag_act8;
  string flag_act9;
  string default_papersize;
  string default_text_wrap;
  string default_emphasis;
  string default_language;
  string default_digest;
  string permission_share_source;
  string search_flag;
  string search_action;
  string search_db;
  string search_title;
}
#+END_SRC

*** composite meta

#+name: meta_defaults_template_structs
#+BEGIN_SRC d
struct MetaComposite {
  string   classify_dewey;
  string   classify_keywords;
  string   classify_loc;
  string   classify_subject;
  string   classify_topic_register;
  string   creator_author;
  string   creator_author_email;
  string   creator_illustrator;
  string   creator_translator;
  string   date_added_to_site;
  string   date_available;
  string   date_created;
  string   date_issued;
  string   date_modified;
  string   date_published;
  string   date_valid;
  string   identifier_isbn;
  string   identifier_oclc;
  string   identifier_pg;
  string   language_document;
  string   language_document_char;
  string   links;
  string   notes_abstract;
  string   notes_description;
  string   original_language;
  string   original_language_char;
  string   original_publisher;
  string   original_source;
  string   original_title;
  string   publisher;
  string   rights_copyright;
  string   rights_copyright_audio;
  string   rights_copyright_cover;
  string   rights_copyright_illustrations;
  string   rights_copyright_photographs;
  string   rights_copyright_text;
  string   rights_copyright_translation;
  string   rights_copyright_video;
  string   rights_license;
  string   title_edition;
  string   title_full;
  string   title_language;
  string   title_main;
  string   title_note;
  string   title_short;
  string   title_sub;
  string   title_subtitle;
}
#+END_SRC

*** composite structs

#+name: meta_defaults_template_structs
#+BEGIN_SRC d
struct ConfComposite {
  MetaComposite               meta;
  ConfCompositeMakeInit       make;
  ConfCompositeSiteLocal      conf;
}
struct ConfCompositePlus {
  MetaComposite               meta;
  ConfCompositeMakeInit       make;
  ConfCompositeMakeStr        make_str;
  ConfCompositeSiteLocal      conf;
}
#+END_SRC

*** metadata associative array indexes                             :header:

#+name: meta_defaults_template_structs
#+BEGIN_SRC d
static string[] ptr_head_main
  = [
    "classify",
    "creator",
    "date",
    "identifier",
    "links",
    "make",
    "original",
    "notes",
    "rights",
    "title"
  ];
static string[] ptr_head_sub_classify
  = [
    "dewey",
    "keywords",
    "loc",
    "subject",
    "topic_register"
  ];
static string[] ptr_head_sub_creator
  = [
    "author",
    "author_email",
    "cover",
    "illustrator",
    "translator"
  ];
static string[] ptr_head_sub_date
  = [
    "added_to_site",
    "available",
    "created",
    "issued",
    "modified",
    "published",
    "valid"
  ];
static string[] ptr_head_sub_identifier
  = [
    "isbn",
    "oclc",
    "pg"
  ];
/+ make +/
static string[] ptr_head_sub_make
  = [
    "cover_image",
    "home_button_image",
    "home_button_text",
    "footer", "headings",
    "auto_num_top_at_level", "auto_num_top_lv", "auto_num_depth",
    "breaks",
    "substitute",
    "bold",
    "italics",
    "emphasis",
    "texpdf_font",
    "css"
  ];
static string[] ptr_head_sub_notes
  = [
    "abstract",
    "description"
  ];
static string[] ptr_head_sub_original
  = [
    "language",
    "source",
    "title"
  ];
static string[] ptr_head_sub_publisher
  = [ "name" ];
static string[] ptr_head_sub_rights
  = [
    "copyright",
    "cover",
    "illustrations",
    "license"
  ];
static string[] ptr_head_sub_title
  = [
    "edition",
    "full",
    "language",
    "main",
    "note",
    "sub"
  ];
JSONValue config_jsonstr = `{
}`;
#+END_SRC

* 1. JSON to DocReformStruct                     :module:conf_make_meta:json:
** 0. module template

#+BEGIN_SRC d :tangle "../src/doc_reform/meta/conf_make_meta_json.d"
/++
  json headers<BR>
  extract json header return json
+/
module doc_reform.meta.conf_make_meta_json;
static template contentJSONtoDocReformStruct() {
  import
    std.exception,
    std.regex,
    std.stdio,
    std.string,
    std.traits,
    std.typecons,
    std.utf,
    std.conv : to;
  import
    doc_reform.meta.conf_make_meta_structs,
    doc_reform.meta.conf_make_meta_json,
    doc_reform.meta.rgx;
  ConfCompositePlus _struct_composite;
  auto contentJSONtoDocReformStruct(C,J)(C _struct_composite, J _json, string _identifier) {
    mixin DocReformRgxInit;
    static auto rgx = Rgx();
    debug (json) {
      writeln(">> --------------------------- >>");
      foreach (tag0; _json.object.byKeyValue) {
        if (tag0.value.stringof == "string") {
          writeln(tag0.key, ": ", tag0.value);
        } else {
          // writeln(tag0.key, ":");
          foreach (tag1; tag0.value.object.byKeyValue) {
            writeln(tag0.key, ":", tag1.key, ": ", tag1.value);
          }
        }
      }
      writeln("<< --------------------------- <<");
    }
    confCompositeMakeBuild _mk;
    <<json_objects>>
    return _struct_composite;
  }
}
#+END_SRC

**  make

#+name: json_objects
#+BEGIN_SRC d
/+ make ------------------------------------------------------------------- +/
if ("make" in _json.object) {
  if ("bold" in _json.object["make"]
    && (_json.object["make"]["bold"].type().to!string == "string")
  ) {
    _struct_composite.make_str.bold = _json.object["make"]["bold"].str;
  }
  if ("breaks" in _json.object["make"]
    && (_json.object["make"]["breaks"].type().to!string == "string")
  ) {
    _struct_composite.make_str.breaks = _json.object["make"]["breaks"].str;
  }
  if ("cover_image" in _json.object["make"]
    && (_json.object["make"]["cover_image"].type().to!string == "string")
  ) {
    _struct_composite.make_str.cover_image = _json.object["make"]["cover_image"].str;
  }
  if ("css" in _json.object["make"]
    && (_json.object["make"]["css"].type().to!string == "string")
  ) {
    _struct_composite.make_str.css = _json.object["make"]["css"].str;
  }
  if ("emphasis" in _json.object["make"]
    && (_json.object["make"]["emphasis"].type().to!string == "string")
  ) {
    _struct_composite.make_str.emphasis = _json.object["make"]["emphasis"].str;
  }
  if ("footer" in _json.object["make"]) {
    if (_json.object["make"]["footer"].type().to!string == "string") {
      char[][] __match_footer_array
        = (cast(char[]) _json.object["make"]["footer"].str)
          .split(rgx.make_heading_delimiter);
      _struct_composite.make_str.footer = __match_footer_array.to!(string[]);
    } else if (_json.object["make"]["footer"].type().to!string == "array") {
      string[] _match_footer_array;
      foreach (_match_heading; _json.object["make"]["footer"].arrayNoRef) {
        _match_footer_array ~= _match_heading.str;
      }
      _struct_composite.make_str.footer = _match_footer_array;
    }
  }
  if ("headings" in _json.object["make"]) {
    if (_json.object["make"]["headings"].type().to!string == "string") {
     char[][] __match_headings_array
        = (cast(char[]) _json.object["make"]["headings"].str)
          .split(rgx.make_heading_delimiter);
      _struct_composite.make_str.headings = __match_headings_array.to!(string[]);
    } else if (_json.object["make"]["headings"].type().to!string == "array") {
      string[] _match_headings_array;
      foreach (_match_heading; _json.object["make"]["headings"].arrayNoRef) {
        _match_headings_array ~= _match_heading.str;
      }
      _struct_composite.make_str.headings = _match_headings_array;
    }
  }
  if ("home_button_image" in _json.object["make"]) {
    if (_json.object["make"]["home_button_image"].type().to!string == "string") {
     char[][] __match_home_button_image_array
        = (cast(char[]) _json.object["make"]["home_button_image"].str)
          .split(rgx.make_heading_delimiter);
      _struct_composite.make_str.home_button_image = __match_home_button_image_array.to!(string[]);
    } else if (_json.object["make"]["home_button_image"].type().to!string == "array") {
      string[] _match_home_button_image_array;
      foreach (_match_heading; _json.object["make"]["home_button_image"].arrayNoRef) {
        _match_home_button_image_array ~= _match_heading.str;
      }
      _struct_composite.make_str.home_button_image = _match_home_button_image_array;
    }
  }
  if ("home_button_text" in _json.object["make"]) {
    if (_json.object["make"]["home_button_text"].type().to!string == "string") {
      _struct_composite.make_str.home_button_text = _json.object["make"]["home_button_text"].str;
    } else if (_json.object["make"]["home_button_text"].type().to!string == "array") {
      string[] _match_home_button_text_array;
      foreach (_match_heading; _json.object["make"]["home_button_text"].arrayNoRef) {
        _match_home_button_text_array ~= _match_heading.str;
      }
      string _match_home_button_text_str = (_match_home_button_text_array).join("; ");
      _struct_composite.make_str.home_button_text = _match_home_button_text_str;
    }
  }
  if ("italics" in _json.object["make"]
    && (_json.object["make"]["italics"].type().to!string == "string")
  ) {
    _struct_composite.make_str.italics = _json.object["make"]["italics"].str;
  }
  if ("auto_num_top_at_level" in _json.object["make"] // str == A - D, 1 - 4
    && (_json.object["make"]["auto_num_top_at_level"].type().to!string == "string")
  ) {
    _struct_composite.make_str.auto_num_top_at_level = _json.object["make"]["auto_num_top_at_level"].str;
    switch (_json.object["make"]["auto_num_top_at_level"].str) {
    case "A":
      break;
    case "B": _struct_composite.make_str.auto_num_top_lv = 1;
      break;
    case "C": _struct_composite.make_str.auto_num_top_lv = 2;
      break;
    case "D": _struct_composite.make_str.auto_num_top_lv = 3;
      break;
    case "1": _struct_composite.make_str.auto_num_top_lv = 4;
      break;
    case "2": _struct_composite.make_str.auto_num_top_lv = 5;
      break;
    case "3": _struct_composite.make_str.auto_num_top_lv = 6;
      break;
    case "4": _struct_composite.make_str.auto_num_top_lv = 7;
      break;
    default:
      break;
    }
  }
  if ("auto_num_depth" in _json.object["make"]) {
    if (_json.object["make"]["auto_num_depth"].type().to!string == "int") { // TODO watch this match
      _struct_composite.make_str.auto_num_depth = _json.object["make"]["auto_num_depth"].integer.to!int;
    } else if (_json.object["make"]["auto_num_depth"].type().to!string == "string") {
      _struct_composite.make_str.auto_num_depth = _json.object["make"]["auto_num_depth"].str.to!int;
    }
  }
  if ("substitute" in _json.object["make"]) {
    string[][] _sub;
    if (_json.object["make"]["substitute"].type().to!string == "array") {
      if (_json.object["make"]["substitute"][0].type().to!string == "array") {
        foreach (substitute_pair; _json.object["make"]["substitute"].arrayNoRef) {
          if ((substitute_pair.type().to!string) == "array") {
            if (!empty(substitute_pair[0].str) && !empty(substitute_pair[1].str)) {
              _sub ~= [ substitute_pair[0].str,  substitute_pair[1].str];
            }
          }
        }
      } else if (_json.object["make"]["substitute"][0].type().to!string == "string") {
         if (!empty(_json.object["make"]["substitute"][0].str) && !empty(_json.object["make"]["substitute"][1].str)) {
           _sub = [[_json.object["make"]["substitute"][0].str, _json.object["make"]["substitute"][1].str]];
         }
      }
    }
    // writeln(_sub);
    _struct_composite.make_str.substitute  = _sub;
  }
  if ("texpdf_font" in _json.object["make"]
    && (_json.object["make"]["texpdf_font"].type().to!string == "string")
  ) {
    _struct_composite.make_str.texpdf_font  = _json.object["make"]["texpdf_font"].str;
  }
  _struct_composite.make.bold                     = _mk.bold(_struct_composite.make_str.bold);
  _struct_composite.make.breaks                   = _mk.breaks(_struct_composite.make_str.breaks);
  _struct_composite.make.cover_image              = _mk.cover_image(_struct_composite.make_str.cover_image);
  _struct_composite.make.css                      = _mk.css(_struct_composite.make_str.css);
  _struct_composite.make.emphasis                 = _mk.emphasis(_struct_composite.make_str.emphasis);
  _struct_composite.make.footer                   = _mk.footer(_struct_composite.make_str.footer);
  _struct_composite.make.headings                 = _mk.headings(_struct_composite.make_str.headings);
  _struct_composite.make.home_button_image        = _mk.home_button_image(_struct_composite.make_str.home_button_image);
  _struct_composite.make.home_button_text         = _mk.home_button_text(_struct_composite.make_str.home_button_text);
  _struct_composite.make.italics                  = _mk.italics(_struct_composite.make_str.italics);
  _struct_composite.make.auto_num_top_at_level    = _mk.auto_num_top_at_level(_struct_composite.make_str.auto_num_top_at_level);
  _struct_composite.make.auto_num_top_lv          = _mk.auto_num_top_lv(_struct_composite.make_str.auto_num_top_lv);
  _struct_composite.make.auto_num_depth           = _mk.auto_num_depth(_struct_composite.make_str.auto_num_depth);
  _struct_composite.make.substitute               = _mk.substitute(_struct_composite.make_str.substitute);
  _struct_composite.make.texpdf_font              = _mk.texpdf_font(_struct_composite.make_str.texpdf_font);
}
#+END_SRC

**  conf

#+name: json_objects
#+BEGIN_SRC d
/+ conf ------------------------------------------------------------------- +/
if ("webserv" in _json.object) {
  if ("url_root" in _json.object["webserv"]
    && (_json.object["webserv"]["url_root"].type().to!string == "string")
  ) {
    _struct_composite.conf.webserv_url_doc_root = _json.object["webserv"]["url_root"].str;
    if (auto m = _struct_composite.conf.webserv_url_doc_root.match(rgx.webserv_url_doc_root)) {
      _struct_composite.conf.webserv_url_domain = m.captures[2].to!string;
      _struct_composite.conf.webserv_url_doc_path = m.captures[3].to!string;
    }
  }
  if ("images" in _json.object["webserv"]
    && (_json.object["webserv"]["images"].type().to!string == "string")
  ) {
    _struct_composite.conf.webserv_images = _json.object["webserv"]["images"].str;
  }
  if ("cgi" in _json.object["webserv"]
    && (_json.object["webserv"]["cgi"].type().to!string == "string")
  ) {
    _struct_composite.conf.webserv_cgi = _json.object["webserv"]["cgi"].str;
  }
  if ("cgi_host" in _json.object["webserv"]
    && (_json.object["webserv"]["cgi_host"].type().to!string == "string")
  ) {
    _struct_composite.conf.webserv_cgi_host = _json.object["webserv"]["cgi_host"].str;
  }
  if ("cgi_host_path" in _json.object["webserv"]
    && (_json.object["webserv"]["cgi_host_path"].type().to!string == "string")
  ) {
    _struct_composite.conf.webserv_cgi_host_path = _json.object["webserv"]["cgi_host_path"].str;
  }
  if ("cgi_port" in _json.object["webserv"]
    && (_json.object["webserv"]["cgi_port"].type().to!string == "string")
  ) {
    _struct_composite.conf.webserv_cgi_port = _json.object["webserv"]["cgi_port"].str;
  }
  if ("cgi_user" in _json.object["webserv"]
    && (_json.object["webserv"]["cgi_user"].type().to!string == "string")
  ) {
    _struct_composite.conf.webserv_cgi_user = _json.object["webserv"]["cgi_user"].str;
  }
  if ("cgi_file_links" in _json.object["webserv"]
    && (_json.object["webserv"]["cgi_file_links"].type().to!string == "string")
  ) {
    _struct_composite.conf.webserv_cgi_file_links = _json.object["webserv"]["cgi_file_links"].str;
  }
}
if ("processing" in _json.object) {
  if ("path" in _json.object["processing"]
    && (_json.object["processing"]["path"].type().to!string == "string")
  ) {
    _struct_composite.conf.processing_path = _json.object["processing"]["path"].str;
  }
  if ("dir" in _json.object["processing"]
    && (_json.object["processing"]["dir"].type().to!string == "string")
  ) {
    _struct_composite.conf.processing_dir = _json.object["processing"]["dir"].str;
  }
  if ("concord_max" in _json.object["processing"]
    && (_json.object["processing"]["concord_max"].type().to!string == "string")
  ) {
    _struct_composite.conf.processing_concord_max = _json.object["processing"]["concord_max"].str;
  }
}
if ("flag" in _json.object) {
  if ("act0" in _json.object["flag"]
    && (_json.object["flag"]["act0"].type().to!string == "string")
  ) {
    _struct_composite.conf.flag_act0 = _json.object["flag"]["act0"].str;
  }
  if ("act1" in _json.object["flag"]
    && (_json.object["flag"]["act1"].type().to!string == "string")
  ) {
    _struct_composite.conf.flag_act1 = _json.object["flag"]["act1"].str;
  }
  if ("act2" in _json.object["flag"]
    && (_json.object["flag"]["act2"].type().to!string == "string")
  ) {
    _struct_composite.conf.flag_act2 = _json.object["flag"]["act2"].str;
  }
  if ("act3" in _json.object["flag"]
    && (_json.object["flag"]["act3"].type().to!string == "string")
  ) {
    _struct_composite.conf.flag_act3 = _json.object["flag"]["act3"].str;
  }
  if ("act4" in _json.object["flag"]
    && (_json.object["flag"]["act4"].type().to!string == "string")
  ) {
    _struct_composite.conf.flag_act4 = _json.object["flag"]["act4"].str;
  }
  if ("act5" in _json.object["flag"]
    && (_json.object["flag"]["act5"].type().to!string == "string")
  ) {
    _struct_composite.conf.flag_act5 = _json.object["flag"]["act5"].str;
  }
  if ("act6" in _json.object["flag"]
    && (_json.object["flag"]["act6"].type().to!string == "string")
  ) {
    _struct_composite.conf.flag_act6 = _json.object["flag"]["act6"].str;
  }
  if ("act7" in _json.object["flag"]
    && (_json.object["flag"]["act7"].type().to!string == "string")
  ) {
    _struct_composite.conf.flag_act7 = _json.object["flag"]["act7"].str;
  }
  if ("act8" in _json.object["flag"]
    && (_json.object["flag"]["act8"].type().to!string == "string")
  ) {
    _struct_composite.conf.flag_act8 = _json.object["flag"]["act8"].str;
  }
  if ("act9" in _json.object["flag"]
    && (_json.object["flag"]["act9"].type().to!string == "string")
  ) {
    _struct_composite.conf.flag_act9 = _json.object["flag"]["act9"].str;
  }
}
if ("default" in _json.object) {
  if ("papersize" in _json.object["default"]
    && (_json.object["default"]["papersize"].type().to!string == "string")
  ) {
    _struct_composite.conf.default_papersize = _json.object["default"]["papersize"].str;
  }
  if ("text_wrap" in _json.object["default"]
    && (_json.object["default"]["text_wrap"].type().to!string == "string")
  ) {
    _struct_composite.conf.default_text_wrap = _json.object["default"]["text_wrap"].str;
  }
  if ("emphasis" in _json.object["default"]
    && (_json.object["default"]["emphasis"].type().to!string == "string")
  ) {
    _struct_composite.conf.default_emphasis = _json.object["default"]["emphasis"].str;
  }
  if ("language" in _json.object["default"]
    && (_json.object["default"]["language"].type().to!string == "string")
  ) {
    _struct_composite.conf.default_language = _json.object["default"]["language"].str;
  }
  if ("digest" in _json.object["default"]
    && (_json.object["default"]["digest"].type().to!string == "string")
  ) {
    _struct_composite.conf.default_digest = _json.object["default"]["digest"].str;
  }
}
if ("search" in _json.object) {
  if ("flag" in _json.object["search"]
    && (_json.object["search"]["flag"].type().to!string == "string")
  ) {
    _struct_composite.conf.search_flag = _json.object["search"]["flag"].str;
  }
  if ("action" in _json.object["search"]
    && (_json.object["search"]["action"].type().to!string == "string")
  ) {
    _struct_composite.conf.search_action = _json.object["search"]["action"].str;
  }
  if ("db" in _json.object["search"]
    && (_json.object["search"]["db"].type().to!string == "string")
  ) {
    _struct_composite.conf.search_db = _json.object["search"]["db"].str;
  }
  if ("title" in _json.object["search"]
    && (_json.object["search"]["title"].type().to!string == "string")
  ) {
    _struct_composite.conf.search_title = _json.object["search"]["title"].str;
  }
}
#+END_SRC

**  meta

#+name: json_objects
#+BEGIN_SRC d
/+ meta ------------------------------------------------------------------- +/
if ("classify" in _json.object) {
  if ("dewey" in _json.object["classify"]
    && (_json.object["classify"]["dewey"].type().to!string == "string")
  ) {
    _struct_composite.meta.classify_dewey = _json.object["classify"]["dewey"].str;
  }
  if ("keywords" in _json.object["classify"]
    && (_json.object["classify"]["keywords"].type().to!string == "string")
  ) {
    _struct_composite.meta.classify_keywords = _json.object["classify"]["keywords"].str;
  }
  if ("loc" in _json.object["classify"]
    && (_json.object["classify"]["loc"].type().to!string == "string")
  ) {
    _struct_composite.meta.classify_loc = _json.object["classify"]["loc"].str;
  }
  if ("subject" in _json.object["classify"]
    && (_json.object["classify"]["subject"].type().to!string == "string")
  ) {
    _struct_composite.meta.classify_subject = _json.object["classify"]["subject"].str;
  }
  if ("topic_register" in _json.object["classify"]
    && (_json.object["classify"]["topic_register"].type().to!string == "string")
  ) {
    _struct_composite.meta.classify_topic_register = _json.object["classify"]["topic_register"].str;
  }
}
if ("date" in _json.object) {
  if ("added_to_site" in _json.object["date"]
    && (_json.object["date"]["added_to_site"].type().to!string == "string")
  ) {
    _struct_composite.meta.date_added_to_site = _json.object["date"]["added_to_site"].str;
  }
  if ("available" in _json.object["date"]
    && (_json.object["date"]["available"].type().to!string == "string")
  ) {
    _struct_composite.meta.date_available = _json.object["date"]["available"].str;
  }
  if ("created" in _json.object["date"]
    && (_json.object["date"]["created"].type().to!string == "string")
  ) {
    _struct_composite.meta.date_created = _json.object["date"]["created"].str;
  }
  if ("issued" in _json.object["date"]
    && (_json.object["date"]["issued"].type().to!string == "string")
  ) {
    _struct_composite.meta.date_issued = _json.object["date"]["issued"].str;
  }
  if ("modified" in _json.object["date"]
    && (_json.object["date"]["modified"].type().to!string == "string")
  ) {
    _struct_composite.meta.date_modified = _json.object["date"]["modified"].str;
  }
  if ("published" in _json.object["date"]
    && (_json.object["date"]["published"].type().to!string == "string")
  ) {
    _struct_composite.meta.date_published = _json.object["date"]["published"].str;
  }
  if ("valid" in _json.object["date"]
    && (_json.object["date"]["valid"].type().to!string == "string")
  ) {
    _struct_composite.meta.date_valid = _json.object["date"]["valid"].str;
  }
}
if ("links" in _json.object) {}
if ("notes" in _json.object) {
  if ("abstract" in _json.object["notes"]
    && (_json.object["notes"]["abstract"].type().to!string == "string")
  ) {
    _struct_composite.meta.notes_abstract = _json.object["notes"]["abstract"].str;
  }
  if ("description" in _json.object["notes"]
    && (_json.object["notes"]["description"].type().to!string == "string")
  ) {
    _struct_composite.meta.notes_description = _json.object["notes"]["description"].str;
  }
}
if ("original" in _json.object) {
  if ("language" in _json.object["original"]
    && (_json.object["original"]["language"].type().to!string == "string")
  ) {
    _struct_composite.meta.original_language = _json.object["original"]["language"].str;
  }
  if ("language_char" in _json.object["original"]
    && (_json.object["original"]["language_char"].type().to!string == "string")
  ) {
    _struct_composite.meta.original_language_char = _json.object["original"]["language_char"].str;
  }
  if ("source" in _json.object["original"]
    && (_json.object["original"]["source"].type().to!string == "string")
  ) {
    _struct_composite.meta.original_source = _json.object["original"]["source"].str;
  }
  if ("title" in _json.object["original"]
    && (_json.object["original"]["title"].type().to!string == "string")
  ) {
    _struct_composite.meta.original_title = _json.object["original"]["title"].str;
  }
}
if ("publisher" in _json.object) {}
if ("rights" in _json.object) {
  if ("copyright" in _json.object["rights"]
    && (_json.object["rights"]["copyright"].type().to!string == "string")
  ) {
    _struct_composite.meta.rights_copyright = _json.object["rights"]["copyright"].str;
  }
  if ("copyright_text" in _json.object["rights"]
    && (_json.object["rights"]["copyright_text"].type().to!string == "string")
  ) {
    _struct_composite.meta.rights_copyright_text = _json.object["rights"]["copyright_text"].str;
  }
  if ("copyright_audio" in _json.object["rights"]
    && (_json.object["rights"]["copyright_audio"].type().to!string == "string")
  ) {
    _struct_composite.meta.rights_copyright_audio = _json.object["rights"]["copyright_audio"].str;
  }
  if ("copyright_cover" in _json.object["rights"]
    && (_json.object["rights"]["copyright_cover"].type().to!string == "string")
  ) {
    _struct_composite.meta.rights_copyright_cover = _json.object["rights"]["copyright_cover"].str;
  }
  if ("copyright_illustrations" in _json.object["rights"]
    && (_json.object["rights"]["copyright_illustrations"].type().to!string == "string")
  ) {
    _struct_composite.meta.rights_copyright_illustrations = _json.object["rights"]["copyright_illustrations"].str;
  }
  if ("copyright_photographs" in _json.object["rights"]
    && (_json.object["rights"]["copyright_photographs"].type().to!string == "string")
  ) {
    _struct_composite.meta.rights_copyright_photographs = _json.object["rights"]["copyright_photographs"].str;
  }
  if ("copyright_translation" in _json.object["rights"]
    && (_json.object["rights"]["copyright_translation"].type().to!string == "string")
  ) {
    _struct_composite.meta.rights_copyright_translation = _json.object["rights"]["copyright_translation"].str;
  }
  if ("copyright_video" in _json.object["rights"]
    && (_json.object["rights"]["copyright_video"].type().to!string == "string")
  ) {
    _struct_composite.meta.rights_copyright_video = _json.object["rights"]["copyright_video"].str;
  }
  if ("license" in _json.object["rights"]
    && (_json.object["rights"]["license"].type().to!string == "string")
  ) {
    _struct_composite.meta.rights_license = _json.object["rights"]["license"].str;
  }
}
if (_struct_composite.meta.creator_author.empty) {
  if ("creator" in _json.object) {
    if ("author" in _json.object["creator"]
      && (_json.object["creator"]["author"].type().to!string == "string")
    ) {
      _struct_composite.meta.creator_author = _json.object["creator"]["author"].str;
    }
    if ("email" in _json.object["creator"]
      && (_json.object["creator"]["email"].type().to!string == "string")
    ) {
      _struct_composite.meta.creator_author_email = _json.object["creator"]["email"].str;
    }
    if ("illustrator" in _json.object["creator"]
      && (_json.object["creator"]["illustrator"].type().to!string == "string")
    ) {
      _struct_composite.meta.creator_illustrator = _json.object["creator"]["illustrator"].str;
    }
    if ("translator" in _json.object["creator"]
      && (_json.object["creator"]["translator"].type().to!string == "string")
    ) {
      _struct_composite.meta.creator_translator = _json.object["creator"]["translator"].str;
    }
  }
  string[] authors_arr;
  string[] authors_raw_arr
    = _struct_composite.meta.creator_author.split(rgx.arr_delimiter);
  foreach (author_raw; authors_raw_arr) {
    authors_arr ~= author_raw.replace(rgx.raw_author_munge, "$2 $1");
  }
  _struct_composite.meta.creator_author = join(authors_arr, ", ").chomp.chomp;
}
if (_struct_composite.meta.title_main.empty) {
  if ("title" in _json.object) {
    if ((_json.object["title"].type().to!string) == "string") {
      _struct_composite.meta.title_main = _json.object["title"].str;
    } else {
      if ("edition" in _json.object["title"]
        && (_json.object["title"]["edition"].type().to!string == "string")
      ) {
        _struct_composite.meta.title_edition = _json.object["title"]["edition"].str;
      }
      if ("full" in _json.object["title"]
        && (_json.object["title"]["full"].type().to!string == "string")
      ) {}
      if ("language" in _json.object["title"]
        && (_json.object["title"]["language"].type().to!string == "string")
      ) {
        _struct_composite.meta.title_language = _json.object["title"]["language"].str;
      }
      if ("main" in _json.object["title"]
        && (_json.object["title"]["main"].type().to!string == "string")
      ) {
        _struct_composite.meta.title_main = _json.object["title"]["main"].str;
      } else if ("title" in _json.object["title"]
        && (_json.object["title"]["title"].type().to!string == "string")
      ) {
        _struct_composite.meta.title_main = _json.object["title"]["title"].str;
      }
      if ("note" in _json.object["title"]
        && (_json.object["title"]["note"].type().to!string == "string")
      ) {
        _struct_composite.meta.title_note = _json.object["title"]["note"].str;
      }
      if ("sub" in _json.object["title"]
        && (_json.object["title"]["sub"].type().to!string == "string")
      ) {
        _struct_composite.meta.title_sub = _json.object["title"]["sub"].str;
      }
      if ("subtitle" in _json.object["title"]
        && (_json.object["title"]["subtitle"].type().to!string == "string")
      ) {
        _struct_composite.meta.title_subtitle = _json.object["title"]["subtitle"].str;
      }
    }
  }
  if ((!(_struct_composite.meta.title_subtitle.empty))
  && (_struct_composite.meta.title_sub.empty)) {
    _struct_composite.meta.title_sub = _struct_composite.meta.title_subtitle;
  }
  _struct_composite.meta.title_full = (_struct_composite.meta.title_sub.empty)
  ? _struct_composite.meta.title_main
  : format(
      "%s - %s",
      _struct_composite.meta.title_main,
      _struct_composite.meta.title_sub,
    );
}
#+END_SRC

* 2. TOML returns DocReformStruct (via JSON)   :module:conf_make_meta:struct:
** 0. parse TOML config return JSON

#+BEGIN_SRC d :tangle "../src/doc_reform/meta/conf_make_meta_toml.d"
/++
  extract native/orig header return associative array<BR>

  the header is passed as text (lopped off top of a sisu markup file until the
  required first heading ^A~), determine whether is a native header or sdlang one
  with a regex check if whether it contains the "native header" required tag/field
  @title: then process accordingly as a "native header" or "sdlang header"
  converting the metadata and make instructions to a common json format used by
  program internally. Moved to associative array.
+/
module doc_reform.meta.conf_make_meta_toml;
static template configParseTOMLreturnJSON() {
  import
    toml,
    toml.json;
  auto configParseTOMLreturnJSON(T)(
    T _text
  ){
    TOMLDocument _doc;
    _doc = parseTOML(cast(string)(_text.content));
    auto _doc_json = _doc.toJSON;
    return _doc_json;
  }
}
#+END_SRC

** 1. parse TOML config to JSON return DocReformStruct

#+BEGIN_SRC d :tangle "../src/doc_reform/meta/conf_make_meta_toml.d"
static template configParseTOMLreturnDocReformStruct() {
  import
    toml,
    toml.json;
  import
    doc_reform.meta.conf_make_meta_structs,
    doc_reform.meta.conf_make_meta_json;
  mixin contentJSONtoDocReformStruct;
  auto configParseTOMLreturnDocReformStruct(CCm, T)(
    CCm     _make_and_meta_struct,
    T       _document_struct
  ){
    TOMLDocument _doc = parseTOML(cast(string)(_document_struct.content));
    auto _doc_json = _doc.toJSON;
    _make_and_meta_struct
      = contentJSONtoDocReformStruct!()(_make_and_meta_struct, _doc_json, _document_struct.filename); // struct from json
    return _make_and_meta_struct;
  }
}
#+END_SRC

** 2. parse TOML header to JSON then Struct

#+BEGIN_SRC d :tangle "../src/doc_reform/meta/conf_make_meta_toml.d"
static template docHeaderMakeAndMetaTupTomlExtractAndConvertToStruct() {
  import
    std.exception,
    std.regex,
    std.stdio,
    std.traits,
    std.typecons,
    std.utf,
    std.conv : to;
  import
    toml,
    toml.json;
  import
    doc_reform.meta.conf_make_meta_structs,
    doc_reform.meta.conf_make_meta_json,
    doc_reform.meta.rgx;
  mixin DocReformRgxInit;
  mixin contentJSONtoDocReformStruct;
  static auto rgx = Rgx();
  auto docHeaderMakeAndMetaTupTomlExtractAndConvertToStruct(CCm, Src)(
    CCm     _make_and_meta_struct,
    Src     header_src,
  ) {
    TOMLDocument _doc;
    if (header_src.match(rgx.toml_header_meta_title)) {
      debug (json) {
        writeln(">>> document header is toml, convert to JSON");
      }
      _doc = parseTOML(cast(string)(header_src));
    }
    auto _doc_json = _doc.toJSON;
    auto _header_and_make_and_meta_struct
      = contentJSONtoDocReformStruct!()(_make_and_meta_struct, _doc_json, "header");
    return _header_and_make_and_meta_struct;
  }
}
#+END_SRC

* __END__
** notes headers

#+BEGIN_SRC d
/+
  /+
    unify internal representation of header info for native & sdlang document headers
    represent either using struct, hashes or possibly json
    doc_reform internal representation should be identical for native & sdlang variants
  +/
header.
  ├── make                         // make instructions
  │   ├── bold
  │   ├── breaks
  │   ├── cover_image
  │   ├── css
  │   ├── emphasis
  │   ├── footer
  │   ├── headings
  │   ├── home_button_image
  │   ├── home_button_text
  │   ├── italics
  │   ├── auto_num_top_at_level
  │   ├── substitute
  │   └── texpdf_font
  └── meta                         // metadata
     ├── author                    // move author to creator:author
     ├── classify
     │   ├── dewey
     │   ├── keyword
     │   ├── loc
     │   ├── subject
     │   └── topic_register
     ├── creator
     │   ├── author
     │   │     ├── [ [first_name: x0, last_name: y0], [first_name: x1, last_name: y1] ]
     │   │     └── [ full_name0, full_name1 ]
     │   ├── author_email
     │   ├── illustrator
     │   └── translator
     ├── date
     │   ├── added_to_site
     │   ├── available
     │   ├── created
     │   ├── issued
     │   ├── modified
     │   ├── published
     │   └── valid
     ├── identifier
     │   ├── isbn
     │   ├── oclc
     │   └── pg
     ├── links
     ├── notes
     │   ├── abstract
     │   └── description
     ├── original
     │   ├── language
     │   ├── source
     │   └── title
     ├── publisher
     │   └── name
     ├── rights
     │   ├── copyright
     │   ├── cover
     │   ├── illustrations
     │   └── license
     └── title                    // move title: to title:main
         ├── edition
         ├── [ full (main + sub) ]
         ├── language
         ├── main
         ├── note
         ├── sub
         └── subtitle              // move title:subtitle to title:sub

61 leaves
+/
#+END_SRC

** dlang rgx example

#+BEGIN_SRC d
import std.conv, std.regex, std.range, std.file, std.stdio;
import std.string : format;
void main(string[] argv) {
  immutable ratio = 1.5824;  // UK pounds to US dollar as of this writing
  auto toDollars(Captures!string price) {
    real value = to!real(price["integer"]);
    if (!price["fraction"].empty)
      value += 0.01*to!real(price["fraction"]);
    return format("$%.2f",value * ratio);
  }
  string text = std.file.readText(argv[1]);
  auto converted = replaceAll!toDollars(text,
    regex(r"£\s*(?P<integer>[0-9]+)(\.(?P<fraction>[0-9]{2}))?","g"));
  write(converted);
}
#+END_SRC