#+TITLE:       doc_reform default paths
#+DESCRIPTION: documents - structuring, publishing in multiple formats & search
#+FILETAGS:    :doc_reform:paths:
#+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. source paths
** 0. module template                                  :module:paths_source:

#+BEGIN_SRC d :tangle "../src/doc_reform/source/paths_source.d"
/++
  read configuration files<BR>
  - read config files<BR>
  meta_config_files.d
+/
module doc_reform.source.paths_source;
import std.array,
  std.file,
  std.path,
  std.regex,
  std.stdio,
  std.conv : to;
import doc_reform.meta.rgx;
<<template_paths_src>>
<<template_paths_pod>>
<<template_paths_pod_shallow>>
<<template_paths_pods>>
#+END_SRC

** _manifest_                                                       :manifest:

#+name: template_paths_src
#+BEGIN_SRC d
template PodManifest() {
  mixin DocReformRgxInit;
  static auto rgx = Rgx();
  auto PodManifest(P)(
    P   _pth
  ) {
    struct ManifestFile_ {
      string pod_manifest_filename() {
        return "pod.manifest";
      }
      string pod_manifest_path() {
        string _manifest_path;
        if ((isValidPath(_pth) && exists(_pth)!=0 && _pth.isDir)
        && (exists(_pth.chainPath(pod_manifest_filename).array)!=0
        && (_pth.chainPath(pod_manifest_filename).array).isFile)) {
          _manifest_path = _pth;
        } else if (_pth.match(rgx.src_pth_contents)
        && exists(_pth)!=0 && _pth.isFile) {
          _manifest_path = _pth.dirName;
        } else if (_pth.match(rgx.src_pth_pod_sst_or_ssm)
        && exists(_pth)!=0 && (_pth.isFile)) {
          if (auto m = _pth.match(rgx.src_pth_pod_sst_or_ssm)) {
            _manifest_path = m.captures["podpath"];
          }
        } else  {
          writeln("WARNING, issue with manifest_path: ", _pth); // remove?
          _manifest_path = null; // _manifest_path = "";
        }
        return _manifest_path;
      }
      string pod_manifest_file_with_path() {
        string _k;
        if  (exists(pod_manifest_path.chainPath(pod_manifest_filename).array)!=0) {
          _k = pod_manifest_path.chainPath(pod_manifest_filename).array;
        } else if (exists(pod_manifest_path)!=0) {
          _k = pod_manifest_path;
        }
        if (exists(_k)==0) {
          writeln("ERROR >> Processing Skipped! Manifest not found: ", _k);
          _k = null;
        }
        return _k;
      }
    }
    return ManifestFile_();
  }
}
#+END_SRC

** _path matters (pod, manifest and source files)_                  :manifest:

tree pod
pod
└─ [sisudoc filename]
    ├── conf
    │   └── sisu_document_make
    ├── media
    │   ├── audio
    │   ├── image
    │   ├── text
    │   │   └── en
    │   │      ...
    │   │       ├── [conf]
    │   │       └── [image]
    │   └── video
    └── pod.manifest

#+name: template_paths_src
#+BEGIN_SRC d
template PathMatters() {
  mixin DocReformRgxInit;
  static auto rgx = Rgx();
  auto PathMatters(O,E)(
    O        _opt_actions,
    E        _env,
    string   _pth,
    string   _fns              = "",
    char[][] _manifest_fn_list = [[]],
  ) {
    auto _manifest = PodManifest!()(_pth);
    struct ManifestMatters_ {
      auto env() {
        auto _env = _env;
        struct Env_ {
          auto pwd() {
            return _env["pwd"];
          }
          auto home() {
            return _env["home"];
          }
        }
        return Env_();
      }
      auto opt() {
        auto _opt_actions = _opt_actions;
        struct Opt_ {
          auto action() { // redundant
            return _opt_actions;
          }
        }
        return Opt_();
      }
      bool src_is_pod() {
        return (_manifest.pod_manifest_path.length > 0) ? true : false;
      }
      auto pod() {
        struct Pod_ {
          bool src_is_pod() {
            return (_manifest.pod_manifest_path.length > 0) ? true : false;
          }
          auto collection_root() {
            auto _collection_root = asNormalizedPath(chainPath(_manifest.pod_manifest_path, "..")).array;
            if (auto m = (_collection_root).match(rgx.src_pth_pod_root)) {
            // consider testing for last dir in path name being pod, and giving warning if not
            } else {
              writeln("WARNING, collection_root not named \"pod\"");
            }
            return _collection_root;
          }
          string manifest_filename() {
            return _manifest.pod_manifest_filename;
          }
          string manifest_path() {
            return _manifest.pod_manifest_path;
          }
          string pod_name_with_path() {
            return _manifest.pod_manifest_path.baseName;
          }
          string manifest_file_with_path() {
            return _manifest.pod_manifest_file_with_path;
          }
          string[] config_sisu_document_make_dirs() {
            string[] _config_dirs;
            return _config_dirs;
          }
          string[] config_local_site_dirs() {
            string[] _config_dirs;
            return _config_dirs;
          }
          string[] image_dirs() {
            string[] _image_dirs;
            return _image_dirs;
          }
          auto manifest_list_of_filenames() {
            return _manifest_fn_list;
          }
          string[] manifest_list_of_languages() {
            string[] _lngs;
            foreach (filename_; manifest_list_of_filenames) {
              string _k = "en";
              if (auto m = (filename_).match(rgx.language_code_and_filename)) {
                _k = m.captures[1].to!string;
              }
              _lngs ~= _k; // all the languages from the manifest list of filenames with paths
            }
            return _lngs;
          }
        }
        return Pod_();
      }
      auto src() {
        string _fns = _fns; // required here by dmd & not by ldc (for D:2078)
        auto _opt_actions = _opt_actions;
        auto _env = _env;
        string _sep = "␣";
        struct SRC_ {
          bool is_pod() {
            return (_manifest.pod_manifest_path.length > 0) ? true : false;
          }
          string path_and_fn() {
            return _fns;
          }
          string pod_name_with_path() {
            return (is_pod) ? _manifest.pod_manifest_path : "";
          }
          string pod_name() {
            return pod_name_with_path.baseName;
          }
          string filename() {
            return path_and_fn.baseName;
          }
          string filename_base() {
            return filename.stripExtension;
          }
          string filename_extension() {
            return filename.match(rgx.src_pth_sst_or_ssm).captures["extension"];
          }
          string lng() {
            string _k;
            if (auto m = path_and_fn.match(rgx.language_code_and_filename)) {
              _k = m.captures[1];
            } else {_k = "en"; }
            return _k;
          }
          string doc_uid() {
            string _uid;
            if (is_pod && !(pod_name_with_path.empty)) {
              if (pod_name_with_path.baseName == filename_base) {
                _uid = filename_base ~ "." ~ filename_extension ~ _sep ~ lng;
              } else {
                _uid = pod_name_with_path.baseName ~ _sep ~ filename_base ~ "." ~ filename_extension ~ _sep ~ lng;
              }
            } else {
              _uid = _sep ~ filename_base ~ "." ~ filename_extension ~ _sep ~ lng;
            }
            return _uid;
          }
          string docname_composite_unique_per_src_doc() {
          /+
            z pod name if any + src filename + lng code
             filename ~ "." ~  lng
             * unique per src doc
             used by
             - sqlite discrete index (multilingual, each language of a document)
          +/
            string _fn;
            if (pod_name_with_path.baseName == filename_base) {
              _fn = filename_base ~ _sep ~ filename_extension ~ _sep ~ lng;
            } else if (!(pod_name_with_path.empty)) {
              _fn = pod_name_with_path.baseName ~ _sep ~ filename_base ~ _sep ~ filename_extension ~ _sep ~ lng;
            } else {
              _fn = "_" ~ _sep ~ filename_base ~ _sep ~ filename_extension ~ _sep ~ lng;
            }
            return _fn;
          }
          string docname_composite_unique_per_src_pod() {
          /+
            z pod name if any + src filename (without lng code)
             filename ~ _sep ~ lng
             * unique per src pod
             used by
             - pod (multilingual collection)
             - sqlite discrete index (multilingual collection)
          +/
            string _fn;
            if (pod_name_with_path.baseName == filename_base) {
              _fn = filename_base ~ _sep ~ filename_extension;
            } else if (!(pod_name_with_path.empty)) {
              _fn = pod_name_with_path.baseName ~ _sep ~ filename_base ~ _sep ~ filename_extension;
            } else {
              _fn = "_" ~ _sep ~ filename_base ~ _sep ~ filename_extension;
            }
            return _fn;
          }
          string language() {
            return lng();
          }
          string file_with_absolute_path() {
            return _env["pwd"].chainPath(path_and_fn).array;
          }
          string absolute_path_to_src() {
            return (_env["pwd"].chainPath(path_and_fn)).dirName.array;
          }
          string base_dir() {
            string _dir;
            if (
              auto m = (absolute_path_to_src)
              .match(regex(r"[/](?P<dir>(?:[a-zA-Z0-9._-]+))/pod/" ~ filename.stripExtension))
            ) {
              _dir = asNormalizedPath(path_and_fn.chainPath("../../")).array;
              assert(_dir == m.captures["dir"]);
            } else {
              _dir = asNormalizedPath(path_and_fn.chainPath("../../../")).array;
              assert(_dir == absolute_path_to_src
                .match(rgx.src_base_parent_dir_name).captures["dir"]);
            }
            if (_opt_actions.debug_do) {
              writeln("--> (base_dir)  ", _dir);
            }
            return _dir;
          }
          string base_parent_dir_path() {
            string _dir;
            if (
              auto m = (absolute_path_to_src)
              .match(regex(r"[/](?P<dir>(?:[a-zA-Z0-9._-]+))/pod/" ~ filename.stripExtension))
            ) {
              _dir = asNormalizedPath(path_and_fn.chainPath("../../")).array;
            } else {
              _dir = asNormalizedPath(path_and_fn.chainPath("../../../")).array;
            }
            return _dir;
          }
          string base_dir_path() {
            string _dir;
            if (
              auto m = (absolute_path_to_src)
              .match(rgx.src_formalised_file_path_parts)
            ) {
              _dir = asNormalizedPath(m.captures["pth"]).array;
            } else if (
             auto m = (absolute_path_to_src)
             .match(regex(r"[/](?P<dir>(?:[a-zA-Z0-9._-]+))/pod/" ~ filename.stripExtension))
            ) {
              _dir = asNormalizedPath(path_and_fn.chainPath("../")).array;
            } else {
              _dir = asNormalizedPath(path_and_fn.chainPath("../../")).array;
            }
            if (_opt_actions.debug_do) {
              writeln("--> (base_dir_path) ", _dir);
            }
            return _dir;
          }
          string media_dir_path() {
            string _dir = asNormalizedPath(base_dir_path.chainPath("media")).array;
            return _dir;
          }
          string image_dir_path() {
            string _paths;
            string[] _possible_img_pths = [ "./image", "../image", "../../image" ];
            string _img_pth_found = "";
            if (is_pod) {
              _img_pth_found = asNormalizedPath(file_with_absolute_path.dirName ~ "/../../image").array;
            } else {
              string _img_pth(string _possible_img_pth) {
                return asNormalizedPath(file_with_absolute_path.dirName ~ "/" ~ _possible_img_pth).array;
              }
              foreach(_possible_img_pth; _possible_img_pths) {
                if (exists(_img_pth(_possible_img_pth))) {
                  _img_pth_found = _img_pth(_possible_img_pth);
                  break;
                } else {
                  _paths ~= " " ~ _img_pth(_possible_img_pth);
                }
              }
            }
            if (_img_pth_found.empty) {
              writeln("WARNING not image path found, searched: ", _paths);
            }
            return _img_pth_found;
          }
          auto conf_dir_path() {
            return asNormalizedPath(base_dir_path.chainPath("conf")).array;
          }
          auto base_parent_dir() {
            string _dir;
            if (
              auto m = (absolute_path_to_src)
              .match(regex(r"[/](?P<dir>(?:[a-zA-Z0-9._-]+))/pod/" ~ filename.stripExtension))
            ) {
              _dir = m.captures["dir"];
            } else {
              _dir = (absolute_path_to_src).match(rgx.src_base_parent_dir_name).captures["dir"];
            }
            if (_opt_actions.debug_do) {
              writeln("--> (base_parent_dir) ", _dir);
            }
            return _dir;
          }
          string[] config_dirs() {
            string[] _config_dirs;
            if (is_pod) {
            } else {}
            return _config_dirs;
          }
          string[] image_dirs() {
            string[] _image_dirs;
            if (is_pod) {
            } else {}
            return _image_dirs;
          }
        }
        return SRC_();
      }
      auto output() {
        auto _opt_actions = _opt_actions;
        auto _env = _env;
        struct Out_ {
          auto path() {
            auto _output_path = _env["pwd"];
            if ((_opt_actions.output_dir_set.length > 0)
              && isValidPath(_opt_actions.output_dir_set)
            ) {
              _output_path = asNormalizedPath(_opt_actions.output_dir_set).array;
              if (!exists(_output_path)) {
                try {
                  _output_path.mkdirRecurse;
                }
                // catch (ErrnoException ex) {
                catch (Exception ex) {
                  // Handle error
                }
              }
              assert(_output_path.isDir,
                "not a directory: " ~ _output_path);
              // TODO always test that is a directory and it is writable
            }
            return _output_path;
          }
        }
        return Out_();
      }
    }
    return ManifestMatters_();
  }
}
#+END_SRC

** _config_ (sisu_document_make & config_local_site)                  :config:

#+name: template_paths_src
#+BEGIN_SRC d
template ConfigFilePaths() {
  mixin DocReformRgxInit;
  static auto rgx = Rgx();
  auto ConfigFilePaths(M,E)(
    M   _manifest,
    E   _env,
  ) {
    struct ConfFilePaths {
      string config_filename_document_toml() {
        return "sisu_document_make";
      }
      string config_filename_site_toml() {
        return "config_local_site";
      }
      auto possible_config_path_locations() {
        struct _ConfFilePaths {
          string[] sisu_document_make() {
            /+ FIX clean up conf paths ↓ +/
            /+ config local site (file system only, not in pod) +/
            /+ return paths +/
            string[] _possible_config_path_locations;
            if (_manifest.src.is_pod) {
              /+ config document in pod +/
              string _sisudoc_conf_pod; //
              string _sisudoc_conf_pod_text; //
              _sisudoc_conf_pod = asNormalizedPath(chainPath(
                to!string(_env["pwd"]),
                _manifest.pod.manifest_path ~ "/conf"
              )).array;
              _sisudoc_conf_pod_text = asNormalizedPath(chainPath(
                to!string(_env["pwd"]),
                _manifest.pod.manifest_path ~ "/media/text/" ~ _manifest.src.lng ~ "/conf"
              )).array;
              /+ return paths +/
              _possible_config_path_locations = [
                _sisudoc_conf_pod_text,
                _sisudoc_conf_pod,
              ];
            } else {
              /+ config document (& or local site) on filesystem +/
              string _sisudoc_conf_pwd   = asNormalizedPath(chainPath(to!string(_env["pwd"]), "sisudoc/conf")).array; // think about
              string _sisudoc_conf_pwd_a = asNormalizedPath(chainPath(to!string(_env["pwd"]), "conf")).array;
              string _sisudoc_conf_pwd_b = asNormalizedPath(chainPath(to!string(_env["pwd"]), "../conf")).array;
              string _sisudoc_conf_pwd_c = asNormalizedPath(chainPath(to!string(_env["pwd"]), "../../conf")).array;
              string _sisudoc_conf_pwd_d = asNormalizedPath(chainPath(to!string(_env["pwd"]), "../../../conf")).array;
              /+ return paths +/
              _possible_config_path_locations = [
                _sisudoc_conf_pwd,
                _sisudoc_conf_pwd_a,
                _sisudoc_conf_pwd_b,
                _sisudoc_conf_pwd_c,
                _sisudoc_conf_pwd_d,
              ];
            }
            /+ FIX clean up conf paths ↑
            (compare pwd to doc path location, and build config path)
            +/
            return _possible_config_path_locations;
          }
          string[] config_local_site() {
            /+ FIX clean up conf paths ↓ +/
            /+ config local site (file system only, not in pod) +/
            string _dot_pwd        = asNormalizedPath(chainPath(to!string(_env["pwd"]), ".sisu")).array;
            string _underscore_pwd = asNormalizedPath(chainPath(to!string(_env["pwd"]), "_sisu")).array;
            string _dot_home       = asNormalizedPath(chainPath(to!string(_env["home"]), ".sisu")).array;
            /+ return paths +/
            string[] _possible_config_path_locations;
            if (_manifest.src.is_pod) {
              string _collection_root_a = asNormalizedPath(chainPath(to!string(_manifest.pod.collection_root.to!string), ".sisu")).array;
              string _collection_root_b = asNormalizedPath(chainPath(to!string(_manifest.pod.collection_root.to!string), "_sisu")).array;
              _possible_config_path_locations = [
                _dot_pwd,
                _underscore_pwd,
                _dot_home,
                "/etc/sisu",
                _collection_root_a, // set priority higher?
                _collection_root_b // set priority higher?
              ];
            } else {
              /+ config document (& or local site) on filesystem +/
              string _sisudoc_conf_pwd   = asNormalizedPath(chainPath(to!string(_env["pwd"]), "sisudoc/conf")).array; // think about
              string _sisudoc_conf_pwd_a = asNormalizedPath(chainPath(to!string(_env["pwd"]), "conf")).array;
              string _sisudoc_conf_pwd_b = asNormalizedPath(chainPath(to!string(_env["pwd"]), "../conf")).array;
              string _sisudoc_conf_pwd_c = asNormalizedPath(chainPath(to!string(_env["pwd"]), "../../conf")).array;
              string _sisudoc_conf_pwd_d = asNormalizedPath(chainPath(to!string(_env["pwd"]), "../../../conf")).array;
              _possible_config_path_locations = [
                _sisudoc_conf_pwd,
                _sisudoc_conf_pwd_a,
                _sisudoc_conf_pwd_b,
                _sisudoc_conf_pwd_c,
                _sisudoc_conf_pwd_d,
                _dot_pwd,
                _underscore_pwd,
                _dot_home,
                "/etc/sisu"
              ];
            }
            /+ FIX clean up conf paths ↑
            (compare pwd to doc path location, and build config path)
            +/
            return _possible_config_path_locations;
          }
        }
        return _ConfFilePaths();
      }
    }
    return ConfFilePaths();
  }
}
#+END_SRC

** _pod_ (including generated bundled source)                            :pod:
*** notes

tree pod
pod
└── sisudoc
    ├── conf
    │   └── sisu_document_make
    ├── media
    │   ├── audio
    │   ├── image
    │   ├── text
    │   │   └── en
    │   │      ...
    │   │       ├── [conf]
    │   │       └── [image]
    │   └── video
    └── pod.manifest

_replace:_

doc
├── en
│   └── the_wealth_of_networks.yochai_benkler.sst
└── _sisu
    └── sisu_document_make

filelist for processing [things to ponder]

- the plan is to have a src manifest of related .sst or .ssm files that
  can be pointed to for processing
- multilingual documents [ponder persistence for multilingual documents]
  - in the case of multilingual documents, there will be multiple
    source documents on list with different language codes, and to build
    this list, persistence is required
- inserted documents (from master .ssm or .sst) must track document root
  so inserts can be located, else not found
- consider a commandline -o --output path specifier
- steps
  - auto create filelist
    - for single source file
    - for multilinugual source files
  - process document by pointing at filelist (rather than file)
    - if necessary manually create filelist (for multilinugual source)
    - keep document root for document inserts (.ssi)

*** manual source

#+name: template_paths_src
#+BEGIN_SRC d
template DocReformPathsSRC() {
  mixin DocReformRgxInit;
  static auto rgx = Rgx();
  auto DocReformPathsSRC(D,Fn)(
    D   _pwd,
    Fn  _fn_src_and_path,
  ) {
    struct SisuSrcPaths {
      auto pwd() {
        return _pwd;
      }
      string language() {
        // use command line info as well?
        string _k;
        if (auto m = _fn_src_and_path.match(rgx.language_code_and_filename)) {
          _k = m.captures[1];
        } else { /+ unknown until doc_meta read, (could provide & use command line info?) +/
          _k = "xx"; // original default was "en" but is not known
        }
        return _k;
      }
      string doc_root() {
        return "sisudoc";
      }
      auto media_root() {
        return asNormalizedPath(doc_root.chainPath("media")).array;
      }
      auto conf_root() {
        return asNormalizedPath(doc_root.chainPath("conf")).array;
      }
      auto text_root() {
        return asNormalizedPath(media_root.chainPath("text")).array;
      }
      auto image_root() {
        return asNormalizedPath(media_root.chainPath("image")).array;
      }
      auto doc_src_fn_with_path_for_text_root_and_lng() {
        return asNormalizedPath(text_root.chainPath(language)).array;
      }
      auto doc_src_fn() {
        return asNormalizedPath(_fn_src_and_path.baseName).array;
      }
      auto doc_src_with_path() {
        return asNormalizedPath(pwd.chainPath(_fn_src_and_path)).array;
      }
    }
    return SisuSrcPaths();
  }
}
#+END_SRC

*** pods internal, zipped, unzipped

#+name: template_paths_pods
#+BEGIN_SRC d
template DocReformPathsPods() {
  mixin DocReformRgxInit;
  static auto rgx = Rgx();
  string _suffix = ".zip";
  auto DocReformPathsPods(M)(M doc_matters) {
    string _base_dir_pod = (doc_matters.output_path.length > 0)
    ? doc_matters.output_path ~ "/pod"
    : "/pod";
    string _base_dir_doc = "sisudoc";
    struct _PodPaths {
      string base_filename_(string fn_src) {
        auto pth = fn_src.baseName.stripExtension;
        return pth;
      }
      string pod_dir_() {
        auto pth = _base_dir_pod;
        return pth;
      }
      string sisudoc_dir_() {
        auto pth = _base_dir_doc;
        return pth;
      }
      string pod_filename_(string fn_src) {
        string pth = _base_dir_pod.chainPath(base_filename_(fn_src) ~ _suffix).array;
        return pth;
      }
      string base_filesystem_(string fn_src) {
        string pth = _base_dir_pod.chainPath(base_filename_(fn_src)).array;
        assert(pth == _base_dir_pod ~ "/"  ~ base_filename_(fn_src),
          pth ~ " == "  ~ _base_dir_pod ~ "/" ~ base_filename_(fn_src) ~ "?");
        return pth;
      }
      string base_pod_(string fn_src) {
        string pth = _base_dir_pod.chainPath(base_filename_(fn_src)).array; // change this
        return pth;
      }
      auto base_filename(string fn_src) {
        auto pth_1_ = base_filename_(fn_src);
        auto pth_2_ = base_filename_(fn_src);
        struct _pods {
          auto zpod() {
            return pth_1_;
          }
          auto filesystem_open_zpod() {
            return pth_2_;
          }
        }
        return _pods();
      }
      auto pod_filename(string fn_src) {
        auto pth_1_ = pod_filename_(fn_src);
        auto pth_2_ = pod_filename_(fn_src);
        struct _pods {
          auto zpod() {
            return pth_1_;
          }
          auto filesystem_open_zpod() {
            return pth_2_;
          }
        }
        return _pods();
      }
      auto base(string fn_src) {
        auto pth_1_ = "";
        auto pth_2_ = base_filesystem_(fn_src);
        struct _pods {
          auto zpod() {
            return pth_1_;
          }
          auto filesystem_open_zpod() {
            return pth_2_;
          }
        }
        return _pods();
      }
      auto pod_root(string fn_src) {
        auto pth_1_ = "";
        auto pth_2_ = asNormalizedPath(base(fn_src).filesystem_open_zpod.chainPath("")).array; // "sisudoc"
        struct _pods {
          auto zpod() {
            return pth_1_;
          }
          auto filesystem_open_zpod() {
            return pth_2_;
          }
        }
        return _pods();
      }
      auto conf_root(string fn_src) {
        auto pod_root_ = pod_root(fn_src);
        auto pth_1_ = "conf";
        auto pth_2_ = asNormalizedPath(pod_root(fn_src).filesystem_open_zpod.chainPath("conf")).array;
        struct _pods {
          auto zpod() {
            return pth_1_;
          }
          auto filesystem_open_zpod() {
            assert(pod_root_.filesystem_open_zpod.chainPath(zpod).array == pth_2_);
            return pth_2_;
          }
        }
        return _pods();
      }
      auto css(string fn_src) {
        auto pod_root_ = pod_root(fn_src);
        auto pth_1_ = asNormalizedPath(conf_root(fn_src).zpod.chainPath("css")).array;
        auto pth_2_ = asNormalizedPath(conf_root(fn_src).filesystem_open_zpod.chainPath("css")).array;
        struct _pods {
          auto zpod() {
            return pth_1_;
          }
          auto filesystem_open_zpod() {
            assert(pod_root_.filesystem_open_zpod.chainPath(zpod).array == pth_2_);
            return pth_2_;
          }
        }
        return _pods();
      }
      auto media_root(string fn_src) {
        auto pod_root_ = pod_root(fn_src);
        auto pth_1_ = "media";
        auto pth_2_ = asNormalizedPath(pod_root(fn_src).filesystem_open_zpod.chainPath("media")).array;
        struct _pods {
          auto zpod() {
            return pth_1_;
          }
          auto filesystem_open_zpod() {
            assert(pod_root_.filesystem_open_zpod.chainPath(zpod).array == pth_2_);
            return pth_2_;
          }
        }
        return _pods();
      }
      auto text_root(string fn_src) {
        auto pod_root_ = pod_root(fn_src);
        auto pth_1_ = asNormalizedPath(media_root(fn_src).zpod.chainPath("text")).array;
        auto pth_2_ = asNormalizedPath(media_root(fn_src).filesystem_open_zpod.chainPath("text")).array;
        struct _pods {
          auto zpod() {
            return pth_1_;
          }
          auto filesystem_open_zpod() {
            assert(pod_root_.filesystem_open_zpod.chainPath(zpod).array == pth_2_);
            return pth_2_;
          }
        }
        return _pods();
      }
      auto doc(string fn_src) {
        auto pod_root_ = pod_root(fn_src);
        auto pth_1_ = text_root(fn_src).zpod;
        auto pth_2_ = text_root(fn_src).filesystem_open_zpod;
        struct _pods {
          auto zpod() {
            return pth_1_;
          }
          auto filesystem_open_zpod() {
            assert(pod_root_.filesystem_open_zpod.chainPath(zpod).array == pth_2_);
            return pth_2_;
          }
        }
        return _pods();
      }
      auto doc_lng(string fn_src, string lng) {
        auto pod_root_ = pod_root(fn_src);
        auto pth_1_ = asNormalizedPath(text_root(fn_src).zpod.chainPath(lng)).array;
        auto pth_2_ = asNormalizedPath(text_root(fn_src).filesystem_open_zpod.chainPath(lng)).array;
        struct _pods {
          auto zpod() {
            return pth_1_;
          }
          auto filesystem_open_zpod() {
            assert(pod_root_.filesystem_open_zpod.chainPath(zpod).array == pth_2_);
            return pth_2_;
          }
        }
        return _pods();
      }
      auto image_root(string fn_src) {
        auto pod_root_ = pod_root(fn_src);
        auto pth_1_ = asNormalizedPath(media_root(fn_src).zpod.chainPath("image")).array;
        auto pth_2_ = asNormalizedPath(media_root(fn_src).filesystem_open_zpod.chainPath("image")).array;
        struct _pods {
          auto zpod() {
            return pth_1_;
          }
          auto filesystem_open_zpod() {
            assert(pod_root_.filesystem_open_zpod.chainPath(zpod).array == pth_2_);
            return pth_2_;
          }
        }
        return _pods();
      }
      auto fn_pod_filelist(string fn_src) {
        auto pod_root_ = pod_root(fn_src);
        auto _manifest = PodManifest!()(fn_src).pod_manifest_filename;
        auto pth_1_ = _manifest;
        auto pth_2_ = asNormalizedPath(pod_root(fn_src).filesystem_open_zpod.chainPath(_manifest)).array;
        struct _pods {
          auto zpod() {
            return pth_1_;
          }
          auto filesystem_open_zpod() {
            assert(pod_root_.filesystem_open_zpod.chainPath(zpod).array == pth_2_);
            return pth_2_;
          }
        }
        return _pods();
      }
      auto fn_doc(string fn_src, string lng) {
        auto pod_root_ = pod_root(fn_src);
        auto pth_1_ = asNormalizedPath(doc_lng(fn_src, lng).zpod.chainPath(fn_src.baseName)).array;
        auto pth_2_ = asNormalizedPath(doc_lng(fn_src, lng).filesystem_open_zpod.chainPath(fn_src.baseName)).array;
        struct _pods {
          auto zpod() {
            return pth_1_;
          }
          auto filesystem_open_zpod() {
            assert(pod_root_.filesystem_open_zpod.chainPath(zpod).array == pth_2_);
            return pth_2_;
          }
        }
        return _pods();
      }
      auto fn_doc_insert(string fn_src, string fn_insert, string lng) {
        auto pod_root_ = pod_root(fn_src);
        auto pth_1_ = asNormalizedPath(doc_lng(fn_src, lng).zpod.chainPath(fn_insert.baseName)).array;
        auto pth_2_ = asNormalizedPath(doc_lng(fn_src, lng).filesystem_open_zpod.chainPath(fn_insert.baseName)).array;
        struct _pods {
          auto zpod() {
            return pth_1_;
          }
          auto filesystem_open_zpod() {
            assert(pod_root_.filesystem_open_zpod.chainPath(zpod).array == pth_2_);
            return pth_2_;
          }
        }
        return _pods();
      }
    }
    return _PodPaths();
  }
}
#+END_SRC

* 1. output paths
** 0. module template                                  :module:paths_output:

#+BEGIN_SRC d :tangle "../src/doc_reform/output/paths_output.d"
/++
  default settings
+/
module doc_reform.output.paths_output;
import std.array,
  std.path,
  std.regex,
  std.stdio;
import doc_reform.meta.rgx;
<<template_paths_out>>
<<template_paths_url>>
<<template_paths_html>>
<<template_paths_epub>>
<<template_paths_sqlite>>
#+END_SRC

** shared out path, base directory                                     :out:
- output paths for content, minimize chance of filename/directory collisions
  in particular pods can contain markup that is published under a name that may
  be used elsewere
  - if the pod directory has a different name from the file append the pod
    directory name to the output file name: pod_directory.markup_filename e.g.
    sisu-manual.sisu_markup
  - if pod and file have same name, keep name (makes no sense to repeat pod
    name) NO sisu_markup.sisu_markup should be sisu_markup

#+name: template_paths_out
#+BEGIN_SRC d
template DocReformOutPaths() {
  auto DocReformOutPaths(Po,Lng)(
    Po  output_pth_root,
    Lng lng,
  ) {
    struct _PathsStruct {
      string output_root() {
        return (output_pth_root.length > 0)
        ? output_pth_root
        : "sisugen";
      }
      string output_base() {
        return asNormalizedPath(output_root.chainPath(lng)).array;
      }
    }
    return _PathsStruct();
  }
}
template DocReformOutPathSQLite() {
  auto DocReformOutPathSQLite(Po)(
    Po  output_pth_root,
  ) {
    struct _PathsStruct {
      string output_root() {
        return (output_pth_root.length > 0)
        ? output_pth_root
        : "sisugen";
      }
      string output_base() {
        return asNormalizedPath(output_root).array;
      }
    }
    return _PathsStruct();
  }
}
#+END_SRC

** set

#+name: template_paths_out
#+BEGIN_SRC d
template DocReformOutPathsFnPd() {
  /+ TODO stuff to work out here +/
  auto DocReformOutPathsFnPd(Fn,Pn)(
    Fn  fn_src_pth,
    Pn  pod_name_with_path
  ) {
    struct _PathsStruct {
      string base_filename() {
        return fn_src_pth.baseName.stripExtension;
      }
      string base_pod_and_filename() { // TODO
        /+
          - if pod,
            - pod_name
            - file_name
            - if pod_name == file_name
              - file_name
            - else if pod_name != file_name
              - pod_name.file_name
        +/
        string _fn_src = fn_src_pth.baseName.stripExtension;
        string _output_base_name;
        if (!(pod_name_with_path.empty)) {
          if (pod_name_with_path == _fn_src) {
            _output_base_name = _fn_src;
          } else {
            _output_base_name = pod_name_with_path ~ "." ~ _fn_src;
          }
        } else {
          _output_base_name = _fn_src;
        }
        return _output_base_name;
      }
    }
    return _PathsStruct();
  }
}
#+END_SRC

** _url_                                                                 :url:

#+name: template_paths_url
#+BEGIN_SRC d
template DocReformPathsUrl() {
  import std.format;
  mixin DocReformRgxInit;
  static auto rgx = Rgx();
  auto DocReformPathsUrl(M)(M doc_matters) {
    struct _UrlPathsStruct {
      string doc_root() {
        string _doc_root = (doc_matters.conf_make_meta.conf.webserv_url_doc_path.length > 0)
        ? doc_matters.conf_make_meta.conf.webserv_url_doc_root
        : "file://" ~ doc_matters.output_path;
        return _doc_root;
      }
      string html_seg(string html_segname, string obj_id) { // TODO bespoke for sqlite
        string _url = format(q"┃%s/%s/html/%s/%s.html#%s┃",
          doc_root,
          doc_matters.src.language,
          doc_matters.src.filename_base,
          html_segname,
          obj_id
        );
        if (html_segname.empty || obj_id.empty) {
          if (
            doc_matters.opt.action.debug_do
            && doc_matters.opt.action.verbose
          ) {
            writeln("sqlite object missing segname or object id: ", _url);
          }
          _url = "";
        }
        return _url;
      }
    }
    return _UrlPathsStruct();
  }
}
#+END_SRC

** _html_                                                               :html:

#+name: template_paths_html
#+BEGIN_SRC d
template DocReformPathsHTML() {
  mixin DocReformRgxInit;
  static auto rgx = Rgx();
  auto DocReformPathsHTML(Po,Lng)(
    Po  output_pth_root,
    Lng lng,
  ) {
    auto out_pth = DocReformOutPaths!()(output_pth_root, lng);
    string base_dir = "html";
    string suffix = ".html";
    struct _PathsStruct {
      string base_filename(string fn_src) {
        return fn_src.baseName.stripExtension;
      }
      string base() {
        return asNormalizedPath((out_pth.output_base).chainPath(base_dir)).array;
      }
      string image() {
        return asNormalizedPath((out_pth.output_root).chainPath("image")).array;
      }
      string css() {
        return asNormalizedPath((out_pth.output_root).chainPath("css")).array;
      }
      string fn_seg_css() {
        return asNormalizedPath(css.chainPath("html_seg.css")).array;
      }
      string fn_scroll_css() {
        return asNormalizedPath(css.chainPath("html_scroll.css")).array;
      }
      string seg(string fn_src) {
        return asNormalizedPath(base.chainPath(base_filename(fn_src))).array;
      }
      string fn_scroll(string fn_src) {
        return asNormalizedPath(base.chainPath(base_filename(fn_src) ~ suffix)).array;
      }
      string fn_seg(string fn_src, string seg_filename) {
        return asNormalizedPath(seg(fn_src).chainPath(seg_filename ~ suffix)).array;
      }
      string tail_seg(string fn_src) {
        return lng ~ "/html/" ~ base_filename(fn_src);
      }
      string tail_fn_scroll(string fn_src) {
        return lng ~ "/html/" ~ base_filename(fn_src) ~ suffix;
      }
      string tail_fn_seg(string fn_src, string seg_filename) {
        return lng ~ "/html/" ~ seg(fn_src) ~ "/" ~ seg_filename ~ suffix;
      }
    }
    return _PathsStruct();
  }
}
#+END_SRC

** _epub_                                                               :epub:

#+name: template_paths_epub
#+BEGIN_SRC d
template DocReformPathsEPUB() {
  mixin DocReformRgxInit;
  static auto rgx = Rgx();
  auto DocReformPathsEPUB(Po,Lng)(
    Po  output_pth_root,
    Lng lng,
  ) {
    auto out_pth = DocReformOutPaths!()( output_pth_root, lng);
    string base_dir = "epub";
    struct _PathsStruct {
      string base() {
        return asNormalizedPath((out_pth.output_base).chainPath(base_dir)).array;
      }
      string base_filename(string fn_src) {
        return fn_src.baseName.stripExtension;
      }
      string epub_file(string fn_src) {
        return asNormalizedPath(base.chainPath(base_filename(fn_src) ~ ".epub")).array;
      }
      string dirtop() {
        return "".chainPath("").array;
      }
      string doc_meta_inf(string fn_src) {
        return asNormalizedPath(dirtop.chainPath("META-INF")).array;
      }
      string doc_oebps(string fn_src) {
        return asNormalizedPath(dirtop.chainPath("OEBPS")).array;
      }
      string doc_oebps_css(string fn_src) {
        return asNormalizedPath(doc_oebps(fn_src).chainPath("css")).array;
      }
      string doc_oebps_image(string fn_src) {
        return asNormalizedPath(doc_oebps(fn_src).chainPath("image")).array;
      }
      string fn_mimetypes(string fn_src) {
        return asNormalizedPath(dirtop.chainPath("mimetypes")).array;
      }
      string fn_dmi_container_xml(string fn_src) {
        return asNormalizedPath(doc_meta_inf(fn_src).chainPath("container.xml")).array;
      }
      string fn_oebps_toc_nav_xhtml(string fn_src) {
        return asNormalizedPath(doc_oebps(fn_src).chainPath("toc_nav.xhtml")).array;
      }
      string fn_oebps_toc_ncx(string fn_src) {
        return asNormalizedPath(doc_oebps(fn_src).chainPath("toc.ncx")).array;
      }
      string fn_oebps_content_opf(string fn_src) {
        return asNormalizedPath(doc_oebps(fn_src).chainPath("content.opf")).array;
      }
      string fn_oebps_content_xhtml(string fn_src, string seg_filename) {
        return asNormalizedPath(doc_oebps(fn_src).chainPath(seg_filename ~ ".xhtml")).array;
      }
      string fn_oebps_css(string fn_src) {
        return asNormalizedPath(doc_oebps_css(fn_src).chainPath("epub.css")).array;
      }
      /+ debug +/
      string dbg_docdir(string fn_src) {
        return base.chainPath(base_filename(fn_src)).array;
      }
      string dbg_docdir_oebps(string fn_src) {
        return dbg_docdir(fn_src).chainPath("OEBPS").array;
      }
      string dbg_doc_meta_inf(string fn_src) {
        return dbg_docdir(fn_src).chainPath("META-INF").array;
      }
      string dbg_doc_oebps(string fn_src) {
        return dbg_docdir(fn_src).chainPath("OEBPS").array;
      }
      string dbg_doc_oebps_css(string fn_src) {
        return dbg_doc_oebps(fn_src).chainPath("css").array;
      }
      string dbg_doc_oebps_image(string fn_src) {
        return dbg_doc_oebps(fn_src).chainPath("image").array;
      }
      string dbg_fn_mimetypes(string fn_src) {
        return dbg_docdir(fn_src).chainPath("mimetypes").array;
      }
      string dbg_fn_dmi_container_xml(string fn_src) {
        return dbg_doc_meta_inf(fn_src).chainPath("container.xml").array;
      }
      string dbg_fn_oebps_toc_nav_xhtml(string fn_src) {
        return dbg_docdir_oebps(fn_src).chainPath("toc_nav.xhtml").array;
      }
      string dbg_fn_oebps_toc_ncx(string fn_src) {
        return dbg_docdir_oebps(fn_src).chainPath("toc.ncx").array;
      }
      string dbg_fn_oebps_content_opf(string fn_src) {
        return dbg_docdir_oebps(fn_src).chainPath("content.opf").array;
      }
      string dbg_fn_oebps_content_xhtml(string fn_src, string seg_filename) {
        return dbg_docdir_oebps(fn_src).chainPath(seg_filename ~ ".xhtml").array;
      }
      string dbg_fn_oebps_css(string fn_src) {
        return dbg_doc_oebps_css(fn_src).chainPath("epub.css").array;
      }
    }
    return _PathsStruct();
  }
}
#+END_SRC

** _sqlite_                                                           :sqlite:
*** discrete

#+name: template_paths_sqlite
#+BEGIN_SRC d
template DocReformPathsSQLiteDiscrete() {
  mixin DocReformRgxInit;
  static auto rgx = Rgx();
  auto DocReformPathsSQLiteDiscrete(Po,Lng)(
    Po  output_pth_root,
    Lng lng,
  ) {
    struct _PathsStruct {
      string base_filename(string fn_src) {
        return fn_src.baseName.stripExtension;
      }
      string base() {
        auto out_pth = DocReformOutPaths!()(output_pth_root, lng);
        string base_dir = "sqlite";
        return asNormalizedPath((out_pth.output_base).chainPath(base_dir)).array;
      }
      string seg(string fn_src) {
        return asNormalizedPath(base.chainPath(base_filename(fn_src))).array;
      }
      string sqlite_file(string fn_src) {
        return asNormalizedPath(base.chainPath(base_filename(fn_src) ~ ".sql.db")).array;
      }
    }
    return _PathsStruct();
  }
}
#+END_SRC

*** TODO collection

#+name: template_paths_sqlite
#+BEGIN_SRC d
template DocReformPathsSQLite() {
  mixin DocReformRgxInit;
  static auto rgx = Rgx();
  auto DocReformPathsSQLite(DbN, Po)(
    DbN db_name,
    Po  output_pth_root,
  ) {
    struct _PathsStruct {
      string base_filename(string fn_src) {
        return fn_src.baseName.stripExtension;
      }
      string base() {
        auto out_pth = DocReformOutPathSQLite!()(output_pth_root); // decide whether to have separate files for each language
        string base_dir = "sqlite";
        return asNormalizedPath((out_pth.output_root).chainPath(base_dir)).array;
      }
      string sqlite_file() {
        return asNormalizedPath(base.chainPath(base_filename(db_name) ~ ".sql.db")).array;
      }
    }
    return _PathsStruct();
  }
}
#+END_SRC

* __END__