#!/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 - 2020 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 [http://www.gnu.org/licenses/].

    If you have Internet connection, the latest version of the AGPL should be
    available at these locations:
    [http://www.fsf.org/licensing/licenses/agpl.html]
    [http://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:
    [http://www.doc_reform.org]
    [http://www.sisudoc.org]

  - Git
    [http://git.sisudoc.org/gitweb/?p=code/sisu.git;a=summary]
    [http://git.sisudoc.org/gitweb/?p=code/sisu.git;a=blob;f=lib/sisu/html.rb;hb=HEAD]

+/
module doc_reform.sisu_document_parser;
/++
name        "spine"
description "A SiSU inspired document parser writen in D."
homepage    "http://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 CompileTimeInfo;
string project_name = "spine";
string program_name = "spine";
@system void main(string[] args) {
  mixin spineRgxIn;
  mixin contentJSONtoSpineStruct;
  mixin spineBiblio;
  mixin spineRgxDocStructFlags;
  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,
    "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 = [
    "cgi-sqlite-search-filename"  : "", // "spine-search"
    "config"             : "",
    "output"             : "",
    "lang"               : "all",
    "sqlite-db-filename"    : "",          // "spine-search.db"
  ];
  auto helpInfo = getopt(args,
    std.getopt.config.passThrough,
    "abstraction",                "--abstraction document abstraction ",                                      &opts["abstraction"],
    "assert",                     "--assert set optional assertions on",                                      &opts["assertions"],
    "cgi-search-form-codegen",    "--cgi-search-form-codegen pre-compiled d code search of specified db",     &opts["cgi-search-form-codegen"],
    "concordance",                "--concordance file for document",                                          &opts["concordance"],
    "dark",                       "--dark alternative dark theme",                                            &opts["dark"],
    "debug",                      "--debug",                                                                  &opts["debug"],
    "digest",                     "--digest hash digest for each object",                                     &opts["digest"],
    "epub",                       "--epub process epub output",                                               &opts["epub"],
    "harvest",                    "--harvest extract info on authors & topics from document header metadata", &opts["harvest"],
    "harvest-authors",            "--harvest-authors extract info on authors from document header metadata",  &opts["harvest-authors"],
    "harvest-topics",             "--harvest-topics extract info on topics from document header metadata",    &opts["harvest-topics"],
    "html",                       "--html process html output",                                               &opts["html"],
    "html-link-harvest",          "--html-link-harvest place links back to harvest in segmented html",        &opts["html-link-harvest"],
    "html-link-search",           "--html-link-search html embedded search submission",                       &opts["html-link-search"],
    "html-seg",                   "--html-seg process html output",                                           &opts["html-seg"],
    "html-scroll",                "--html-seg process html output",                                           &opts["html-scroll"],
    "latex",                      "--latex output for pdfs",                                                  &opts["latex"],
    "latex-color-links",          "--latex-color-links mono or color links for pdfs",                         &opts["latex-color-links"],
    "light",                      "--light default light theme",                                              &opts["light"],
    "manifest",                   "--manifest process manifest output",                                       &opts["manifest"],
    "hide-ocn",                   "--hide-ocn object cite numbers",                                           &opts["hide-ocn"],
    "ocn-off",                    "--ocn-off object cite numbers",                                            &opts["ocn-off"],
    "odf",                        "--odf open document format text (--odt)",                                  &opts["odf"],
    "odt",                        "--odt open document format text",                                          &opts["odt"],
    "parallel",                   "--parallel parallelisation",                                               &opts["parallel"],
    "parallel-subprocesses",      "--parallel-subprocesses nested parallelisation",                        &opts["parallel-subprocesses"],
    "quiet|q",                    "--quiet output to terminal",                                               &opts["quiet"],
    "pdf",                        "--pdf latex output for pdfs",                                              &opts["pdf"],
    "pdf-color-links",            "--pdf-color-links mono or color links for pdfs",                           &opts["pdf-color-links"],
    "pod",                        "--pod spine (doc reform) pod source content bundled",                      &opts["pod"],
    "serial",                     "--serial serial processing",                                               &opts["serial"],
    "show-summary",               "--show-summary",                                                           &opts["show-summary"],
    "show-make",                  "--show-make",                                                              &opts["show-make"],
    "show-metadata",              "--show-metadata",                                                          &opts["show-metadata"],
    "show-config",                "--show-config",                                                            &opts["show-config"],
    "source",                     "--source markup source text content",                                      &opts["source"],
    "sqlite-discrete",            "--sqlite process discrete sqlite output",                                  &opts["sqlite-discrete"],
    "sqlite-db-create",           "--sqlite-db-create create db, create tables",                              &opts["sqlite-db-create"],
    "sqlite-db-drop",             "--sqlite-db-drop drop tables & db",                                        &opts["sqlite-db-drop"],
    "sqlite-db-recreate",         "--sqlite-db-recreate create db, create tables",                            &opts["sqlite-db-recreate"],
    "sqlite-delete",              "--sqlite-delete process sqlite output",                                    &opts["sqlite-delete"],
    "sqlite-insert",              "--sqlite-insert process sqlite output",                                    &opts["sqlite-insert"],
    "sqlite-update",              "--sqlite-update process sqlite output",                                    &opts["sqlite-update"],
    "text",                       "--text process text output",                                               &opts["text"],
    "txt",                        "--txt process text output",                                                &opts["text"],
    "verbose|v",                  "--verbose output to terminal",                                             &opts["verbose"],
    "very-verbose",               "--very-verbose output to terminal",                                        &opts["very-verbose"],
    "xhtml",                      "--xhtml process xhtml output",                                             &opts["xhtml"],
    "section-toc",                "--section-toc process table of contents (default)",                        &opts["section_toc"],
    "section-body",               "--section-body process document body (default)",                           &opts["section_body"],
    "section-endnotes",           "--section-endnotes process document endnotes (default)",                   &opts["section_endnotes"],
    "section-glossary",           "--section-glossary process document glossary (default)",                   &opts["section_glossary"],
    "section-biblio",             "--section-biblio process document biblio (default)",                       &opts["section_biblio"],
    "section-bookindex",          "--section-bookindex process document bookindex (default)",                 &opts["section_bookindex"],
    "section-blurb",              "--section-blurb process document blurb (default)",                         &opts["section_blurb"],
    "backmatter",                 "--section-backmatter process document backmatter (default)",               &opts["backmatter"],
    "skip-output",                "--skip-output",                                                            &opts["skip-output"],
    "theme-dark",                 "--theme-dark alternative dark theme",                                      &opts["theme-dark"],
    "theme-light",                "--theme-light default light theme",                                        &opts["theme-light"],
    "workon",                     "--workon (reserved for some matters under development & testing)",         &opts["workon"],
    "cgi-sqlite-search-filename", "--cgi-sqlite-search-filename=[filename]",                         &settings["cgi-sqlite-search-filename"],
    "config",                     "--config=/path/to/config/file/including/filename",                         &settings["config"],
    "output",                     "--output=/path/to/output/dir specify where to place output",               &settings["output"],
    "sqlite-db-filename",         "--sqlite-db-filename=[filename].sql.db",                                      &settings["sqlite-db-filename"],
    "lang",                       "--lang=[lang code e.g. =en or =en,es]",                                    &settings["lang"],
  );
  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 assertions() {
      return opts["assertions"];
    }
    @trusted bool cgi_search_form_codegen() {
      return opts["cgi-search-form-codegen"];
    }
    @trusted bool concordance() {
      return opts["concordance"];
    }
    auto 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 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"];
    }
    auto languages_set() {
      return settings["lang"].split(",");
    }
    auto output_dir_set() {
      return settings["output"];
    }
    auto sqlite_filename() {
      return settings["sqlite-db-filename"];
    }
    auto cgi_sqlite_search_filename() {
      return settings["cgi-sqlite-search-filename"];
    }
    auto cgi_sqlite_search_filename_d() {
      return (settings["cgi-sqlite-search-filename"].translate(['-' : "_"]) ~ ".d");
    }
    @trusted bool parallelise() {
      bool _is;
      if (opts["parallel"] == true) {
        _is = true;
        if (sqlite_shared_db_action) { _is = false; }
      } else if (opts["parallel"] == false
      && opts["serial"] == true) {
        _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 meta_processing_general() {
      return (
        opts["abstraction"]
        || 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;
    }
  }
  auto _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 _make_and_meta_struct;
  if (_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);
          import doc_reform.meta.conf_make_meta_yaml;
          _make_and_meta_struct = _config_local_site_struct.configParseYAMLreturnSpineStruct!()(_make_and_meta_struct, _manifested); // - get local site config
          break;
        }
      }
    }
  } else {
    { /+ local site config +/
      auto _config_local_site_struct = readConfigSite!()(_conf_file_details);
      import doc_reform.meta.conf_make_meta_yaml;
      _make_and_meta_struct = _config_local_site_struct.configParseYAMLreturnSpineStruct!()(_make_and_meta_struct, _manifested); // - get local site config
    }
  }
  foreach(arg; args[1..$]) { // refigure how args relate to _opt_action, need path from _opt_action or args early _manifested too late, work on (search for PathMatters and .harvest,
    auto _manifest_start = PodManifest!()(_opt_action, arg);
    if (arg.match(rgx.flag_action)) { /+ cli instruction, flag do +/
      flag_action ~= " " ~ arg;   // flags not taken by getopt
    } else { /+ cli, assumed to be path to source files +/
      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 = Loader.fromFile(sisudoc_txt_).load();
                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 (!(_opt_action.skip_output)) {
    if ((_opt_action.debug_do)
    || (_opt_action.very_verbose)
    ) {
      writeln("step0 commence → (without processing files)");
    }
    outputHubOp!()(_env, _opt_action, _make_and_meta_struct);
    if ((_opt_action.debug_do)
    || (_opt_action.very_verbose)
    ) {
      writeln("- step0 complete");
    }
  }
  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..$]) {
        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"); }
}