#!/usr/bin/env rdmd
/+
- Name: Spine, Doc Reform
  - Description: documents, structuring, processing, publishing, search
    - static content generator

  - Author: Ralph Amissah
    [ralph.amissah@gmail.com]

  - Copyright: (C) 2015 - 2021 Ralph Amissah, All Rights
    Reserved.

  - License: AGPL 3 or later:

    Spine (SiSU), a framework for document structuring, publishing and
    search

    Copyright (C) Ralph Amissah

    This program is free software: you can redistribute it and/or modify it
    under the terms of the GNU AFERO General Public License as published by the
    Free Software Foundation, either version 3 of the License, or (at your
    option) any later version.

    This program is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
    more details.

    You should have received a copy of the GNU General Public License along with
    this program. If not, see [https://www.gnu.org/licenses/].

    If you have Internet connection, the latest version of the AGPL should be
    available at these locations:
    [https://www.fsf.org/licensing/licenses/agpl.html]
    [https://www.gnu.org/licenses/agpl.html]

  - Spine (by Doc Reform, related to SiSU) uses standard:
    - docReform markup syntax
      - standard SiSU markup syntax with modified headers and minor modifications
    - docReform object numbering
      - standard SiSU object citation numbering & system

  - Hompages:
    [https://www.doc_reform.org]
    [https://www.sisudoc.org]

  - Git
    [https://git.sisudoc.org/projects/?p=software/spine.git;a=summary]

+/
module doc_reform.sisu_document_parser;
/++
name        "spine"
description "A SiSU inspired document parser writen in D."
homepage    "https://sisudoc.org"
+/
import
  std.algorithm,
  std.datetime,
  std.getopt,
  std.file,
  std.path,
  std.process;
import
  doc_reform.conf.compile_time_info,
  doc_reform.meta,
  doc_reform.meta.metadoc,
  doc_reform.meta.metadoc_harvest,
  doc_reform.meta.metadoc_harvests_authors,
  doc_reform.meta.metadoc_harvests_topics,
  doc_reform.meta.metadoc_from_src,
  doc_reform.meta.conf_make_meta_structs,
  doc_reform.meta.conf_make_meta_json,
  doc_reform.meta.defaults,
  doc_reform.meta.doc_debugs,
  doc_reform.meta.rgx,
  doc_reform.io_in.paths_source,
  doc_reform.io_in.read_config_files,
  doc_reform.io_in.read_source_files,
  doc_reform.io_out.hub;
mixin(import("version.txt"));
mixin(import("configuration.txt"));
mixin CompileTimeInfo;
string project_name = "spine";
string program_name = "spine";
@system void main(string[] args) {
  mixin spineRgxIn;
  mixin spineBiblio;
  mixin outputHub;
  auto hvst = spineHarvest!();
  string flag_action;
  string arg_unrecognized;
  enum dAM { abstraction, matters }
  static auto rgx = RgxI();
  scope(success) {
    writefln(
      "~ run complete, ok ~ (%s-%s.%s.%s, %s D:%s, %s %s)",
      program_name,
      _ver.major, _ver.minor, _ver.patch,
      __VENDOR__, __VERSION__,
      bits, os,
    );
  }
  scope(failure) {
    debug(checkdoc) {
      stderr.writefln(
        "run failure",
      );
    }
  }
  bool[string] opts = [
    "abstraction"                 : false,
    "allow-downloads"             : false,
    "assertions"                  : false,
    "cgi-search-form-codegen"     : false,
    "concordance"                 : false,
    "dark"                        : false,
    "debug"                       : false,
    "digest"                      : false,
    "epub"                        : false,
    "harvest"                     : false,
    "harvest-authors"             : false,
    "harvest-topics"              : false,
    "html"                        : false,
    "html-link-harvest"           : false,
    "html-link-search"            : false,
    "html-seg"                    : false,
    "html-scroll"                 : false,
    "latex"                       : false,
    "latex-color-links"           : false,
    "light"                       : false,
    "manifest"                    : false,
    "hide-ocn"                    : false,
    "ocn-off"                     : false,
    "odf"                         : false,
    "odt"                         : false,
    "parallel"                    : false,
    "parallel-subprocesses"       : false,
    "pdf"                         : false,
    "pdf-color-links"             : false,
    "quiet"                       : false,
    "pod"                         : false,
    "serial"                      : false,
    "show-summary"                : false,
    "show-metadata"               : false,
    "show-make"                   : false,
    "show-config"                 : false,
    "source"                      : false,
    "sqlite-discrete"             : false,
    "sqlite-db-create"            : false,
    "sqlite-db-drop"              : false,
    "sqlite-db-recreate"          : false,
    "sqlite-delete"               : false,
    "sqlite-insert"               : false,
    "sqlite-update"               : false,
    "text"                        : false,
    "verbose"                     : false,
    "very-verbose"                : false,
    "xhtml"                       : false,
    "section_toc"                 : true,
    "section_body"                : true,
    "section_endnotes"            : true,
    "section_glossary"            : true,
    "section_biblio"              : true,
    "section_bookindex"           : true,
    "section_blurb"               : true,
    "backmatter"                  : true,
    "skip-output"                 : false,
    "theme-dark"                  : false,
    "theme-light"                 : false,
    "workon"                      : false,
  ];
  string[string] settings = [
    "output"                      : "",    //_cfg.www_doc_root,          // "/srv/www/spine/static" or "/var/www/html"
    "www-http"                    : "",    //_cfg.www_http,              // "http" or "https"
    "www-domain"                  : "",    //_cfg.www_domain,            // start with "localhost" ?
    "www-domain-doc-root"         : "",    //_cfg.www_domain_doc_root,   // start with "localhost" ?
    "www-url-doc-root"            : "",    //_cfg.www_url_doc_root,      // start with "http://localhost" ?
    "cgi-bin-root"                : "",    //_cfg.cgi_bin_root,          // suggest "/var/www/cgi/cgi-bin"
    "cgi-sqlite-search-filename"  : "",    //_cfg.cgi_filename,          // suggest "spine_search" (though "spine-search" would have been preferable?)
    "cgi-url-root"                : "",    //_cfg.cgi_url_root,          // start with "http://localhost/cgi-bin" ?
    "cgi-url-action"              : "",    //_cfg.cgi_url_action,        // start with "http://localhost/cgi-bin/spine-search"
    "cgi-search-title"            : "",    //_cfg.cgi_title,             // e.g. "≅ SiSU Spine search"
    "config"                      : "",
    "lang"                        : "all", //_cfg.default_language,      // suggest "all" or "en" (default which language to process)
    "set-papersize"               : "",    //_cfg.default_papersize,     // suggest: a4
    "set-textwrap"                : "",    //_cfg.default_text_wrap,     // suggest: 80
    "set-digest"                  : "",    //_cfg.default_hash_digest,   // suggest: sha256
    "sqlite-db-path"              : "",    //_cfg.db_sqlite_path,        // suggest: "/var/www/sqlite"
    "sqlite-db-filename"          : "",    //_cfg.db_sqlite_filename,    // suggest: "spine-search.db"
  ];
  auto helpInfo = getopt(args,
    std.getopt.config.passThrough,
    "abstraction",                "document abstraction",                                           &opts["abstraction"],
    "allow-downloads",            "allow downloads (includes cgi.d from github)",                   &opts["allow-downloads"],
    "assert",                     "set optional assertions on",                                     &opts["assertions"],
    "cgi-search-form-codegen",    "generates (pre-compiled) d code for search of specified db",     &opts["cgi-search-form-codegen"],
    "cgi-bin-root",               "path to cgi-bin directory",                                      &settings["cgi-bin-root"],
    "cgi-sqlite-search-filename", "cgi-bin sqlite search form name",                                &settings["cgi-sqlite-search-filename"],
    "cgi-url-root",               "url to cgi-bin (to find cgi-bin)",                               &settings["cgi-url-root"],
    "cgi-url-action",             "url to post to cgi-bin search form",                             &settings["cgi-url-action"],
    "cgi-search-title",           "title for cgi-bin search form",                                  &settings["cgi-search-title"],
    "concordance",                "file for document",                                              &opts["concordance"],
    "dark",                       "alternative dark theme",                                         &opts["dark"],
    "debug",                      "debug",                                                          &opts["debug"],
    "digest",                     "hash digest for each object",                                    &opts["digest"],
    "epub",                       "process epub output",                                            &opts["epub"],
    "harvest",                    "extract info on authors & topics from document header metadata", &opts["harvest"],
    "harvest-authors",            "extract info on authors from document header metadata",          &opts["harvest-authors"],
    "harvest-topics",             "extract info on topics from document header metadata",           &opts["harvest-topics"],
    "hide-ocn",                   "object cite numbers",                                            &opts["hide-ocn"],
    "html",                       "process html output",                                            &opts["html"],
    "html-link-harvest",          "place links back to harvest in segmented html",                  &opts["html-link-harvest"],
    "html-link-search",           "html embedded search submission",                                &opts["html-link-search"],
    "html-seg",                   "process html output",                                            &opts["html-seg"],
    "html-scroll",                "process html output",                                            &opts["html-scroll"],
    "latex",                      "output for pdfs",                                                &opts["latex"],
    "latex-color-links",          "mono or color links for pdfs",                                   &opts["latex-color-links"],
    "light",                      "default light theme",                                            &opts["light"],
    "manifest",                   "process manifest output",                                        &opts["manifest"],
    "ocn-off",                    "object cite numbers",                                            &opts["ocn-off"],
    "odf",                        "open document format text (--odt)",                              &opts["odf"],
    "odt",                        "open document format text",                                      &opts["odt"],
    "parallel",                   "parallelisation",                                                &opts["parallel"],
    "parallel-subprocesses",      "nested parallelisation",                                         &opts["parallel-subprocesses"],
    "pdf",                        "latex output for pdfs",                                          &opts["pdf"],
    "pdf-color-links",            "mono or color links for pdfs",                                   &opts["pdf-color-links"],
    "pod",                        "spine (doc reform) pod source content bundled",                  &opts["pod"],
    "quiet|q",                    "output to terminal",                                             &opts["quiet"],
    "section-backmatter",         "document backmatter (default)" ,                                 &opts["backmatter"],
    "section-biblio",             "document biblio (default)",                                      &opts["section_biblio"],
    "section-blurb",              "document blurb (default)",                                       &opts["section_blurb"],
    "section-body",               "document body (default)",                                        &opts["section_body"],
    "section-bookindex",          "document bookindex (default)",                                   &opts["section_bookindex"],
    "section-endnotes",           "document endnotes (default)",                                    &opts["section_endnotes"],
    "section-glossary",           "document glossary (default)",                                    &opts["section_glossary"],
    "section-toc",                "table of contents (default)",                                    &opts["section_toc"],
    "serial",                     "serial processing",                                              &opts["serial"],
    "skip-output",                "skip output",                                                    &opts["skip-output"],
    "show-config",                "show config",                                                    &opts["show-config"],
    "show-make",                  "show make",                                                      &opts["show-make"],
    "show-metadata",              "show metadata",                                                  &opts["show-metadata"],
    "show-summary",               "show summary",                                                   &opts["show-summary"],
    "source",                     "document markup source",                                         &opts["source"],
    "set-digest",                 "default hash digest type (e.g. sha256)",                         &settings["set-digest"],
    "set-papersize",              "default papersize (latex pdf eg. a4 or a5 or b4 or letter)",     &settings["set-papersize"],
    "set-textwrap",               "default textwrap (e.g. 80 (characters)",                         &settings["set-textwrap"],
    "sqlite-discrete",            "process discrete sqlite output",                                 &opts["sqlite-discrete"],
    "sqlite-db-create",           "create db, create tables",                                       &opts["sqlite-db-create"],
    "sqlite-db-drop",             "drop tables & db",                                               &opts["sqlite-db-drop"],
    "sqlite-db-recreate",         "create db, create tables",                                       &opts["sqlite-db-recreate"],
    "sqlite-delete",              "sqlite output",                                                  &opts["sqlite-delete"],
    "sqlite-insert",              "sqlite output",                                                  &opts["sqlite-insert"],
    "sqlite-update",              "sqlite output",                                                  &opts["sqlite-update"],
    "www-http",                   "http or https",                                                  &settings["www-http"],
    "www-domain",                 "web server domain name",                                         &settings["www-domain"],
    "www-domain-doc-root",        "web host domain name with path to doc root",                     &settings["www-domain-doc-root"],
    "www-url-doc-root",           "e.g. http://localhost",                                          &settings["www-url-doc-root"],
    "text",                       "text output",                                                    &opts["text"],
    "theme-dark",                 "alternative dark theme",                                         &opts["theme-dark"],
    "theme-light",                "default light theme",                                            &opts["theme-light"],
    "txt",                        "text output",                                                    &opts["text"],
    "verbose|v",                  "output to terminal",                                             &opts["verbose"],
    "very-verbose",               "output to terminal",                                             &opts["very-verbose"],
    "workon",                     "(reserved for some matters under development & testing)",        &opts["workon"],
    "xhtml",                      "xhtml output",                                                   &opts["xhtml"],
    "cgi-search-title",           "if generating a cgi search form the title to use for it",        &settings["cgi-search-title"],
    "cgi-sqlite-search-filename", "=[filename] default is spine-search",                            &settings["cgi-sqlite-search-filename"],
    "config",                     "=/path/to/config/file/including/filename",                       &settings["config"],
    "lang",                       "=[lang code e.g. =en or =en,es]",                                &settings["lang"],
    "output",                     "=/path/to/output/dir specify where to place output",             &settings["output"],
    "sqlite-db-path",             "sqlite db path",                                                 &settings["sqlite-db-path"],
    "sqlite-db-filename",         "sqlite db to create, populate & make available for search",      &settings["sqlite-db-filename"],
    // "sqlite-db-filename",         "=[filename].sql.db",                                             &settings["sqlite-db-filename"],
  );
  if (helpInfo.helpWanted) {
    defaultGetoptPrinter("Some information about the program.", helpInfo.options);
  }
  enum outTask { source_or_pod, sqlite, sqlite_multi, latex, odt, epub, html_scroll, html_seg, html_stuff }
  struct OptActions {
    @trusted bool allow_downloads() {
      return opts["allow-downloads"];
    }
    @trusted bool assertions() {
      return opts["assertions"];
    }
    @trusted bool cgi_search_form_codegen() {
      return opts["cgi-search-form-codegen"];
    }
    @trusted bool concordance() {
      return opts["concordance"];
    }
    @trusted string config_path_set() {
      return settings["config"];
    }
    @trusted bool css_theme_default() {
      bool _is_light;
      if (opts["light"] || opts["theme-light"]) {
        _is_light = true;
      } else if (opts["dark"] || opts["theme-dark"]) {
        _is_light = false;
      } else {
        _is_light = true;
      }
      return _is_light;
    }
    @trusted bool debug_do() {
      return opts["debug"];
    }
    @trusted bool digest() {
      return opts["digest"];
    }
    @trusted bool epub() {
      return opts["epub"];
    }
    @trusted bool html_harvest_link() {
      return (opts["html-link-harvest"]) ? true : false;
    }
    @trusted bool html_search_link() {
      return (opts["html-link-search"]) ? true : false;
    }
    @trusted bool harvest() {
      return (opts["harvest"] || opts["harvest-authors"] || opts["harvest-topics"]) ? true : false;
    }
    @trusted bool harvest_authors() {
      return (opts["harvest"] || opts["harvest-authors"]) ? true : false;
    }
    @trusted bool harvest_topics() {
      return (opts["harvest"] || opts["harvest-topics"]) ? true : false;
    }
    @trusted bool html() {
      return (opts["html"] || opts["html-seg"] || opts["html-scroll"]) ? true : false;
    }
    @trusted bool html_seg() {
      return (opts["html"] || opts["html-seg"]) ? true : false;
    }
    @trusted bool html_scroll() {
      return (opts["html"] || opts["html-scroll"]) ? true : false;
    }
    @trusted bool html_stuff() {
      return (opts["html"] || opts["html-scroll"] || opts["html-seg"]) ? true : false;
    }
    @trusted bool latex() {
      return (opts["latex"] || opts["pdf"]) ? true : false;
    }
    @trusted bool latex_color_links() {
      return (opts["latex-color-links"] || opts["pdf-color-links"]) ? true : false;
    }
    @trusted bool odt() {
      return (opts["odf"] || opts["odt"]) ? true : false;
    }
    @trusted bool manifest() {
      return opts["manifest"];
    }
    @trusted bool ocn_hidden() {
      return opts["hide-ocn"];
    }
    @trusted bool ocn_off() {
      return opts["ocn-off"];
    }
    @trusted bool quiet() {
      return opts["quiet"];
    }
    @trusted bool pod() {
      return opts["pod"];
    }
    @trusted bool show_summary() {
      return opts["show-summary"];
    }
    @trusted bool show_make() {
      return opts["show-make"];
    }
    @trusted bool show_metadata() {
      return opts["show-metadata"];
    }
    @trusted bool show_config() {
      return opts["show-config"];
    }
    @trusted bool source() {
      return opts["source"];
    }
    @trusted bool source_or_pod() {
      return (opts["pod"] || opts["source"]) ? true : false;
    }
    @trusted string sqlite_db_path() {
      return settings["sqlite-db-path"];
    }
    @trusted string sqlite_db_filename() {
      return settings["sqlite-db-filename"];
    }
    @trusted bool sqlite_discrete() {
      return opts["sqlite-discrete"];
    }
    @trusted bool sqlite_db_drop() {
      return (opts["sqlite-db-recreate"] || opts["sqlite-db-drop"]) ? true : false;
    }
    @trusted bool sqlite_db_create() {
      return (opts["sqlite-db-recreate"] || opts["sqlite-db-create"]) ? true : false;
    }
    @trusted bool sqlite_delete() {
      return opts["sqlite-delete"];
    }
    @trusted bool sqlite_update() {
      return (opts["sqlite-update"] || opts["sqlite-insert"]) ? true : false;
    }
    @trusted bool sqlite_shared_db_action() {
      return (
        opts["sqlite-db-recreate"]
        || opts["sqlite-db-create"]
        || opts["sqlite-delete"]
        || opts["sqlite-insert"]
        || opts["sqlite-update"]
      ) ? true : false;
    }
    @trusted bool text() {
      return opts["text"];
    }
    @trusted bool verbose() {
      return (opts["verbose"] || opts["very-verbose"]) ? true : false;
    }
    @trusted bool very_verbose() {
      return opts["very-verbose"];
    }
    @trusted bool xhtml() {
      return opts["xhtml"];
    }
    @trusted bool section_toc() {
      return opts["section_toc"];
    }
    @trusted bool section_body() {
      return opts["section_body"];
    }
    @trusted bool section_endnotes() {
      return opts["section_endnotes"];
    }
    @trusted bool section_glossary() {
      return opts["section_glossary"];
    }
    @trusted bool section_biblio() {
      return opts["section_biblio"];
    }
    @trusted bool section_bookindex() {
      return opts["section_bookindex"];
    }
    @trusted bool section_blurb() {
      return opts["section_blurb"];
    }
    @trusted bool backmatter() {
      return opts["backmatter"];
    }
    @trusted bool skip_output() {
      return opts["skip-output"];
    }
    @trusted bool workon() {
      return opts["workon"];
    }
    @trusted string[] languages_set() {
      return settings["lang"].split(",");
    }
    @trusted string output_dir_set() {
      return settings["output"];
    }
    @trusted string sqlite_filename() {
      return settings["sqlite-db-filename"];
    }
    @trusted string sqlite_path() {
      return settings["sqlite-db-path"];
    }
    @trusted string cgi_bin_root() {
      return settings["cgi-bin-root"];
    }
    @trusted string cgi_url_root() {
      return settings["cgi-url-root"];
    }
    @trusted string cgi_url_action() {
      return settings["cgi-url-action"];
    }
    @trusted string cgi_search_title() {
      return settings["cgi-search-title"];
    }
    @trusted string cgi_sqlite_search_filename() {
      return settings["cgi-sqlite-search-filename"];
    }
    @trusted string cgi_sqlite_search_filename_d() {
      return (settings["cgi-sqlite-search-filename"].length > 0)
      ? (settings["cgi-sqlite-search-filename"].translate(['-' : "_"]) ~ ".d")
      : "";
    }
    @trusted string hash_digest_type() {
      return settings["set-digest"];
    }
    @trusted string text_wrap() {
      return settings["set-textwrap"];
    }
    @trusted string latex_papersize() {
      return settings["set-papersize"];
    }
    @trusted string  webserver_domain_name() {
      return settings["www-domain"];
    }
    @trusted string  webserver_domain_doc_root() {
      return settings["www-domain-doc-root"];
    }
    @trusted string  webserver_url_doc_root() {
      return settings["www-url-root"];
    }
    @trusted string  webserver_http() {
      return settings["www-http"];
    }
    @trusted bool parallelise() {
      bool _is;
      if (opts["serial"] == true) {
        _is = false;
      } else if (sqlite_shared_db_action) {
        _is = false;
      } else if (opts["parallel"] == true) {
        _is = true;
        if (sqlite_shared_db_action) { _is = false; }
      } else if (
        opts["abstraction"]
        || concordance
        || harvest
        || html
        || epub
        || odt
        || latex
        || manifest
        || source_or_pod
        || sqlite_discrete
      ) {
        _is = true;
      } else { _is = false; }
      return _is;
    }
    @trusted bool parallelise_subprocesses() {
      return opts["parallel-subprocesses"];
    }
    auto output_task_scheduler() {
      int[] schedule;
      if (source_or_pod) {
        schedule ~= outTask.source_or_pod;
      }
      if (sqlite_discrete) {
        schedule ~= outTask.sqlite;
      }
      if (epub) {
        schedule ~= outTask.epub;
      }
      if (html_scroll) {
        schedule ~= outTask.html_scroll;
      }
      if (html_seg) {
        schedule ~= outTask.html_seg;
      }
      if (html_stuff) {
        schedule ~= outTask.html_stuff;
      }
      if (odt) {
        schedule ~= outTask.odt;
      }
      if (latex) {
        schedule ~= outTask.latex;
      }
      return schedule.sort().uniq;
    }
    @trusted bool abstraction() {
      return (
        opts["abstraction"]
        || concordance
        || source_or_pod
        || harvest
        || html
        || epub
        || odt
        || latex
        || manifest
        || sqlite_discrete
        || sqlite_delete
        || sqlite_update
      ) ? true : false;
    }
    @trusted bool require_processing_files() {
      return (
        opts["abstraction"]
        || epub
        || harvest
        || html
        || html_seg
        || html_scroll
        || latex
        || odt
        || manifest
        || show_make
        || show_metadata
        || show_summary
        || source_or_pod
        || sqlite_discrete
        || sqlite_update
        || text
        || xhtml
      ) ? true : false;
    }
    @trusted bool meta_processing_general() {
      return (
        opts["abstraction"]
        || harvest
        || html
        || epub
        || odt
        || latex
        || sqlite_discrete
        || sqlite_update
      ) ? true :false;
    }
    @trusted bool meta_processing_xml_dom() {
      return (
        opts["abstraction"]
        || html
        || epub
        || odt
        || sqlite_discrete
        || sqlite_update
      ) ? true : false;
    }
  }
  OptActions _opt_action = OptActions();
  @safe auto program_info() {
    struct ProgramInfo {
      @safe string project() {
        return project_name;
      }
      @safe string name() {
        return program_name;
      }
      @safe string ver() {
        return format("%s.%s.%s",
          _ver.major, _ver.minor, _ver.patch,
        );
      }
      @safe string compiler() {
        return format ("%s D:%s, %s %s",
          __VENDOR__, __VERSION__,
          bits, os,
        );
      }
    }
    return ProgramInfo();
  }
  auto _env = [
    "pwd" :     environment["PWD"],
    "home" :    environment["HOME"],
  ];
  auto _manifested = PathMatters!()(_opt_action, _env, "");
  auto _manifests = [ _manifested ];
  auto _conf_file_details = configFilePaths!()(_manifested, _env, _opt_action.config_path_set);
  ConfComposite _config;
  if (
    _opt_action.require_processing_files
    && _opt_action.config_path_set.empty
  ) {
    foreach(arg; args[1..$]) {
      if (!(arg.match(rgx.flag_action))) { /+ cli markup source path +/ // get first input markup source file names for processing
        _manifested = PathMatters!()(_opt_action, _env, arg);
        { /+ local site config +/
          _conf_file_details = configFilePaths!()(_manifested, _env, _opt_action.config_path_set);
          auto _config_local_site_struct = readConfigSite!()(_conf_file_details, _opt_action, _cfg);
          import doc_reform.meta.conf_make_meta_yaml;
          _config = _config_local_site_struct.configParseYAMLreturnSpineStruct!()(_config, _manifested, _opt_action); // - get local site config
          break;
        }
      }
    }
  } else { /+ local site config +/
    auto _config_local_site_struct = readConfigSite!()(_conf_file_details, _opt_action, _cfg);
    import doc_reform.meta.conf_make_meta_yaml;
    _config = _config_local_site_struct.configParseYAMLreturnSpineStruct!()(_config, _manifested, _opt_action); // - get local site config
  }
  if (_opt_action.show_config) {
    import doc_reform.meta.metadoc_show_config;
    spineShowSiteConfig!()(_opt_action, _config);
  }
  if (!(_opt_action.skip_output)) {
    if ((_opt_action.debug_do)
    || (_opt_action.very_verbose)
    ) {
      writeln("step0 commence → (without processing files)");
    }
    outputHubOp!()(_env, _opt_action, _config);
    if ((_opt_action.debug_do)
    || (_opt_action.very_verbose)
    ) {
      writeln("- step0 complete");
    }
  }
  ConfComposite _make_and_meta_struct = _config;
  destroy(_config);
  foreach(arg; args[1..$]) {
    if (arg.match(rgx.flag_action)) { /+ cli instruction, flag do +/
      flag_action ~= " " ~ arg;   // flags not taken by getopt
    } else if (_opt_action.require_processing_files) { /+ cli, assumed to be path to source files +/
      auto _manifest_start = PodManifest!()(_opt_action, arg);
      if ( /+ pod files +/
        !(arg.match(rgx.src_pth_sst_or_ssm))
        && _manifest_start.pod_manifest_file_with_path
        && _opt_action.abstraction
      ) {
        string pod_manifest_root_content_paths_to_markup_location_raw_;
        string markup_contents_location_;
        string sisudoc_txt_ = _manifest_start.pod_manifest_file_with_path;
        enforce(
          exists(sisudoc_txt_)!=0,
          "file not found: «" ~
          sisudoc_txt_ ~ "»"
        );
        if (exists(sisudoc_txt_)) {
          try {
            if (exists(sisudoc_txt_)) {
              import dyaml;
              try {
                Node pod_manifest_yaml;
                try {
                  pod_manifest_yaml = Loader.fromFile(sisudoc_txt_).load();
                } catch (ErrnoException ex) {
                } catch (FileException ex) {
                  writeln("ERROR failed to read config file");
                } catch (Throwable) {
                  writeln("ERROR failed to read config file content, not parsed as yaml");
                }
                if ("doc" in pod_manifest_yaml) {
                  if (pod_manifest_yaml["doc"].type.mapping
                    && pod_manifest_yaml["doc"].tag.match(rgx.yaml_tag_is_map)
                  ) {
                    if ("path" in pod_manifest_yaml["doc"]) {
                      if (pod_manifest_yaml["doc"]["path"].tag.match(rgx.yaml_tag_is_seq)) {
                        foreach (string _path; pod_manifest_yaml["doc"]["path"]) {
                          markup_contents_location_ ~= _path ~ "\n";
                          pod_manifest_root_content_paths_to_markup_location_raw_ ~=
                            _path ~ "\n";
                        }
                      } else if (
                        pod_manifest_yaml["doc"]["path"].type.string
                        && pod_manifest_yaml["doc"]["path"].tag.match(rgx.yaml_tag_is_str)
                      ) {
                        markup_contents_location_ = pod_manifest_yaml["doc"]["path"].get!string;
                        pod_manifest_root_content_paths_to_markup_location_raw_ =
                          pod_manifest_yaml["doc"]["path"].get!string;
                      }
                    }
                    if ("filename" in pod_manifest_yaml["doc"]) {
                      if (pod_manifest_yaml["doc"]["filename"].tag.match(rgx.yaml_tag_is_seq)) {
                        foreach (string _filename; pod_manifest_yaml["doc"]["filename"]) {
                          if ("language" in pod_manifest_yaml["doc"]) {
                            if (pod_manifest_yaml["doc"]["language"].tag.match(rgx.yaml_tag_is_seq)) {
                              foreach (string _lang; pod_manifest_yaml["doc"]["language"]) {
                                markup_contents_location_ ~=
                                  "media/text/"
                                  ~ _lang ~ "/"
                                  ~ _filename ~ "\n";
                              }
                            } else if (pod_manifest_yaml["doc"]["language"].tag.match(rgx.yaml_tag_is_str)
                            ) {
                              markup_contents_location_ =
                                "media/text/"
                                ~ pod_manifest_yaml["doc"]["language"].get!string
                                ~ "/" ~ _filename ~ "\n";
                            } else {
                              string _lang_default = "en";
                              markup_contents_location_ ~=
                                "media/text/"
                                ~ _lang_default ~ "/"
                                ~ pod_manifest_yaml["doc"]["filename"].get!string ~ "\n";
                            }
                          } else {
                            string _lang_default = "en";
                            markup_contents_location_ ~=
                              "media/text/"
                              ~ _lang_default ~ "/"
                              ~ pod_manifest_yaml["doc"]["filename"].get!string ~ "\n";
                          }
                        }
                      } else if (
                        pod_manifest_yaml["doc"]["filename"].type.string
                        && pod_manifest_yaml["doc"]["filename"].tag.match(rgx.yaml_tag_is_str)
                      ) {
                        if ("language" in pod_manifest_yaml["doc"]) {
                          if (pod_manifest_yaml["doc"]["language"].tag.match(rgx.yaml_tag_is_seq)) {
                            foreach (string _lang; pod_manifest_yaml["doc"]["language"]) {
                              markup_contents_location_ ~=
                                "media/text/"
                                ~ _lang ~ "/"
                                ~ pod_manifest_yaml["doc"]["filename"].get!string ~ "\n";
                            }
                          } else if (pod_manifest_yaml["doc"]["language"].tag.match(rgx.yaml_tag_is_str)) {
                            markup_contents_location_ =
                              "media/text/"
                              ~ pod_manifest_yaml["doc"]["language"].get!string
                              ~ "/" ~ pod_manifest_yaml["doc"]["filename"].get!string ~ "\n";
                          } else {
                            string _lang_default = "en";
                            markup_contents_location_ ~=
                              "media/text/"
                              ~ _lang_default ~ "/"
                              ~ pod_manifest_yaml["doc"]["filename"].get!string ~ "\n";
                          }
                        } else {
                          string _lang_default = "en";
                          markup_contents_location_ ~=
                            "media/text/"
                            ~ _lang_default ~ "/"
                            ~ pod_manifest_yaml["doc"]["filename"].get!string ~ "\n";
                        }
                      }
                    }
                  }
                }
              } catch (ErrnoException ex) {
              }
            }
          } catch (ErrnoException ex) {
          } catch (FileException ex) {
            // Handle errors
          }
        } else {
          writeln("manifest not found: ", sisudoc_txt_);
        }
        auto markup_contents_locations_arr
          = (cast(char[]) markup_contents_location_).split;
        auto tmp_dir_ = (sisudoc_txt_).dirName.array;
        foreach (markup_contents_location; markup_contents_locations_arr) {
          assert(markup_contents_location.match(rgx.src_pth_sst_or_ssm),
            "not a recognised file: «" ~
            markup_contents_location ~ "»"
          );
          auto markup_contents_location_pth_ = (markup_contents_location).to!string;
          Regex!(char) lang_rgx_ = regex(r"/(" ~ _opt_action.languages_set.join("|") ~ ")/");
          if (_opt_action.languages_set[0] == "all"
            || (markup_contents_location_pth_).match(lang_rgx_)
          ) {
            auto _fns = (((tmp_dir_).chainPath(markup_contents_location_pth_)).array).to!string;
            _manifested = PathMatters!()(_opt_action, _env, arg, _fns, markup_contents_locations_arr);
            _manifests ~= _manifested;
          }
        }
      } else if (arg.match(rgx.src_pth_sst_or_ssm)) { /+ markup txt files +/
        if (exists(arg)==0) {
          writeln("ERROR >> Processing Skipped! File not found: ", arg);
        } else {
          _manifested = PathMatters!()(_opt_action, _env, arg, arg);
          _manifests ~= _manifested;
        }
      } else if (arg.match(rgx.src_pth_zip)) {
        // fns_src ~= arg;          // gather input markup source file names for processing
      } else {                      // anything remaining, unused
        arg_unrecognized ~= " " ~ arg;
      }
    }
  }
  if (_manifests.length > 1                            // _manifests[0] initialized dummy element
  && _opt_action.abstraction) {
    if (_opt_action.parallelise) {                     // see else
      import std.parallelism;
      foreach(manifest; parallel(_manifests[1..$])) {
        if (!empty(manifest.src.filename)) {
          scope(success) {
            if (!(_opt_action.quiet)) {
              writefln(
                "%s",
                "-- ~ document complete, ok ~ ------------------------------------",
              );
            }
          }
          scope(failure) {
            debug(checkdoc) {
              stderr.writefln(
                "~ document run failure ~ (%s  v%s)\n\t%s\n%s",
                __VENDOR__, __VERSION__,
                manifest.src.filename,
                "------------------------------------------------------------------",
              );
            }
          }
          enforce(
            manifest.src.filename.match(rgx.src_pth_types),
            "not a sisu markup filename: «" ~
            manifest.src.filename ~ "»"
          );
          if ((_opt_action.debug_do)
          || (_opt_action.very_verbose)
          ) {
            writeln("--->\nstepX commence → (document abstraction)");
          }
          auto t = spineAbstraction!()(_env, program_info, _opt_action, manifest, _make_and_meta_struct);
          static assert(t.length==2);
          auto doc_abstraction = t[dAM.abstraction];
          auto doc_matters = t[dAM.matters];
          if ((doc_matters.opt.action.debug_do)
          || (doc_matters.opt.action.very_verbose)
          ) {
            writeln("- stepX complete");
          }
          /+ ↓ debugs +/
          if (doc_matters.opt.action.verbose
            || doc_matters.opt.action.show_summary
          ) {
            import doc_reform.meta.metadoc_show_summary;
            spineMetaDocSummary!()(doc_abstraction, doc_matters);
          }
          /+ ↓ debugs +/
          if (doc_matters.opt.action.show_metadata) {
            import doc_reform.meta.metadoc_show_metadata;
            spineShowMetaData!()(doc_matters);
          }
          /+ ↓ debugs +/
          if (doc_matters.opt.action.show_make) {
            import doc_reform.meta.metadoc_show_make;
            spineShowMake!()(doc_matters);
          }
          /+ ↓ debugs +/
          if (doc_matters.opt.action.show_config) {
            import doc_reform.meta.metadoc_show_config;
            spineShowConfig!()(doc_matters);
          }
          if (doc_matters.opt.action.harvest) {
            auto _hvst = spineMetaDocHarvest!()(doc_matters, hvst);
            if (
              _hvst.title.length > 0
              && _hvst.author_surname_fn.length > 0
            ) {
              hvst.harvests ~= _hvst;
            } else {
              if ((doc_matters.opt.action.debug_do)
              || (doc_matters.opt.action.very_verbose)
              ) {
                writeln("WARNING harvest: document header yaml does not contain information related to: title or author: ", _hvst.path_html_segtoc);
              }
            }
          }
          /+ ↓ debugs +/
          if (doc_matters.opt.action.debug_do) {
            spineDebugs!()(doc_abstraction, doc_matters);
          }
          /+ ↓ output hub +/
          if (!(doc_matters.opt.action.skip_output)) {
            if ((_opt_action.debug_do)
            || (_opt_action.very_verbose)
            ) {
              writeln("step5 commence → (process outputs)");
            }
            doc_abstraction.outputHub!()(doc_matters);
            if ((_opt_action.debug_do)
            || (_opt_action.very_verbose)
            ) {
              writeln("- step5 complete");
            }
          }
          scope(exit) {
            if (!(_opt_action.quiet)) {
              writefln(
                "processed file: %s [%s]",
                manifest.src.filename,
                manifest.src.language
              );
            }
            destroy(manifest);
          }
        } else {
          /+ no recognized filename provided +/
          writeln("no recognized filename");
          break; // terminate, stop
        }
      }
    } else {                                           // note cannot parallelise sqlite shared db
      foreach(manifest; _manifests[1..$]) {
        if (_opt_action.very_verbose) {
          writeln("parallelisation off: actions include sqlite shared db");
        }
        if (!empty(manifest.src.filename)) {
          scope(success) {
            if (!(_opt_action.quiet)) {
              writefln(
                "%s",
                "-- ~ document complete, ok ~ ------------------------------------",
              );
            }
          }
          scope(failure) {
            debug(checkdoc) {
              stderr.writefln(
                "~ document run failure ~ (%s  v%s)\n\t%s\n%s",
                __VENDOR__, __VERSION__,
                manifest.src.filename,
                "------------------------------------------------------------------",
              );
            }
          }
          enforce(
            manifest.src.filename.match(rgx.src_pth_types),
            "not a sisu markup filename: «" ~
            manifest.src.filename ~ "»"
          );
          if ((_opt_action.debug_do)
          || (_opt_action.very_verbose)
          ) {
            writeln("--->\nstepX commence → (document abstraction)");
          }
          auto t = spineAbstraction!()(_env, program_info, _opt_action, manifest, _make_and_meta_struct);
          static assert(t.length==2);
          auto doc_abstraction = t[dAM.abstraction];
          auto doc_matters = t[dAM.matters];
          if ((doc_matters.opt.action.debug_do)
          || (doc_matters.opt.action.very_verbose)
          ) {
            writeln("- stepX complete");
          }
          /+ ↓ debugs +/
          if (doc_matters.opt.action.verbose
            || doc_matters.opt.action.show_summary
          ) {
            import doc_reform.meta.metadoc_show_summary;
            spineMetaDocSummary!()(doc_abstraction, doc_matters);
          }
          /+ ↓ debugs +/
          if (doc_matters.opt.action.show_metadata) {
            import doc_reform.meta.metadoc_show_metadata;
            spineShowMetaData!()(doc_matters);
          }
          /+ ↓ debugs +/
          if (doc_matters.opt.action.show_make) {
            import doc_reform.meta.metadoc_show_make;
            spineShowMake!()(doc_matters);
          }
          /+ ↓ debugs +/
          if (doc_matters.opt.action.show_config) {
            import doc_reform.meta.metadoc_show_config;
            spineShowConfig!()(doc_matters);
          }
          if (doc_matters.opt.action.harvest) {
            auto _hvst = spineMetaDocHarvest!()(doc_matters, hvst);
            if (
              _hvst.title.length > 0
              && _hvst.author_surname_fn.length > 0
            ) {
              hvst.harvests ~= _hvst;
            } else {
              if ((doc_matters.opt.action.debug_do)
              || (doc_matters.opt.action.very_verbose)
              ) {
                writeln("WARNING harvest: document header yaml does not contain information related to: title or author: ", _hvst.path_html_segtoc);
              }
            }
          }
          /+ ↓ debugs +/
          if (doc_matters.opt.action.debug_do) {
            spineDebugs!()(doc_abstraction, doc_matters);
          }
          /+ ↓ output hub +/
          if (!(doc_matters.opt.action.skip_output)) {
            if ((_opt_action.debug_do)
            || (_opt_action.very_verbose)
            ) {
              writeln("step5 commence → (process outputs)");
            }
            doc_abstraction.outputHub!()(doc_matters);
            if ((_opt_action.debug_do)
            || (_opt_action.very_verbose)
            ) {
              writeln("- step5 complete");
            }
          }
          scope(exit) {
            if (!(_opt_action.quiet)) {
              writefln(
                "processed file: %s [%s]",
                manifest.src.filename,
                manifest.src.language
              );
            }
            destroy(manifest);
          }
        } else {
          /+ no recognized filename provided +/
          writeln("no recognized filename");
          break; // terminate, stop
        }
      }
    }
  }
  if (hvst.harvests.length > 0) {
    if (_opt_action.harvest_topics) {
      spineMetaDocHarvestsTopics!()(hvst, _make_and_meta_struct, _opt_action);
    }
    if (_opt_action.harvest_authors) {
      spineMetaDocHarvestsAuthors!()(hvst.harvests, _make_and_meta_struct, _opt_action);
    }
    if (!(_opt_action.quiet)) {
      import doc_reform.io_out.paths_output;
      auto out_pth = spinePathsHTML!()(_make_and_meta_struct.conf.output_path, "");
      if (_opt_action.harvest_authors) {
        writeln("- ", out_pth.harvest("authors.html"));
      }
      if (_opt_action.harvest_topics) {
        writeln("- ", out_pth.harvest("topics.html"));
      }
    }
  } else { writeln("NO HARVESTS"); }
}