aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/sisudoc/ocda/abstraction/load.d205
-rw-r--r--src/sisudoc/ocda/abstraction/package.d1
-rw-r--r--src/sisudoc/spine.d18
3 files changed, 224 insertions, 0 deletions
diff --git a/src/sisudoc/ocda/abstraction/load.d b/src/sisudoc/ocda/abstraction/load.d
new file mode 100644
index 0000000..f429595
--- /dev/null
+++ b/src/sisudoc/ocda/abstraction/load.d
@@ -0,0 +1,205 @@
+/+
+- Name: SisuDoc Spine, Doc Reform [a part of]
+ - Description: documents, structuring, processing, publishing, search
+ - static content generator
+
+ - Author: Ralph Amissah
+ [ralph.amissah@gmail.com]
+
+ - Copyright: (C) 2015 (continuously updated, current 2026) 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
+
+ - Homepages:
+ [https://www.sisudoc.org]
+ [https://www.doc-reform.org]
+
+ - Git
+ [https://git.sisudoc.org/]
+
++/
+module sisudoc.ocda.abstraction.load;
+@safe:
+/+ ↓ one way in, whatever the document is being read from
+
+ spine's pipeline is markup -> abstraction -> output. once the abstraction
+ is serialised, there is more than one thing an abstraction can be read
+ from, and a consumer should not have to know which it was handed:
+
+ .sst / .ssm + images the markup source
+ pod (dir) + images the same, bundled
+ pod .zip the same, zipped
+ .ssp + images the abstraction, as text
+ .ocda.db the abstraction, sqlite, images inside
+
+ This module names those sources, tells them apart, and loads the two
+ that are self-describing artefacts, returning the value the parser
+ produces.
+
+ The three source forms are deliberately *not* loaded here. They are the
+ parser's job (sisudoc.ocda.meta.metadoc, spineAbstraction), and it needs
+ the environment, the options and the configuration that spine.d
+ assembles, none of which belongs in a loader. What this module gives
+ that case is the dispatch and a plain statement of where to go.
++/
+template spineAbstractionLoad() {
+ import std.algorithm : endsWith;
+ import std.conv : to;
+ import std.file;
+ import std.path;
+ import std.stdio;
+ import std.string;
+ import sisudoc.ocda.abstraction.db_in;
+ mixin spineAbstractionDbRead; // brings the .ssp reader with it
+
+ enum AbstractionSource {
+ unknown,
+ markup, // .sst or .ssm, with its images beside it
+ pod, // a directory holding pod.manifest
+ pod_zip, // that directory, zipped
+ ssp, // .ssp, the abstraction as text, images beside it
+ ocda_db, // .ocda.db, the abstraction as sqlite, images inside it
+ }
+
+ string abstractionSourceName(AbstractionSource _s) {
+ final switch (_s) {
+ case AbstractionSource.unknown: return "unknown";
+ case AbstractionSource.markup: return "markup source (.sst/.ssm)";
+ case AbstractionSource.pod: return "pod directory";
+ case AbstractionSource.pod_zip: return "pod zip";
+ case AbstractionSource.ssp: return ".ssp (abstraction as text)";
+ case AbstractionSource.ocda_db: return ".ocda.db (abstraction as sqlite)";
+ }
+ }
+
+ /+ ↓ what is this? by name, and for a directory by what it holds +/
+ AbstractionSource abstractionSourceOf(string _path) {
+ if (_path.length == 0) { return AbstractionSource.unknown; }
+ if (_path.isValidPath && _path.exists && _path.isDir) {
+ return (_path.chainPath("pod.manifest").array.exists)
+ ? AbstractionSource.pod : AbstractionSource.unknown;
+ }
+ if (_path.endsWith(".ocda.db")) { return AbstractionSource.ocda_db; }
+ if (_path.endsWith(".ssp")) { return AbstractionSource.ssp; }
+ if (_path.endsWith(".sst") || _path.endsWith(".ssm")) { return AbstractionSource.markup; }
+ if (_path.endsWith(".zip")) { return AbstractionSource.pod_zip; }
+ if (_path.endsWith(".db")) { return AbstractionSource.ocda_db; }
+ return AbstractionSource.unknown;
+ }
+
+ struct LoadedAbstraction {
+ AbstractionSource source;
+ string path;
+ bool loaded; // is .doc filled
+ string note; // why not, when it is not
+ SSPdocument doc;
+ }
+
+ /+ ↓ load what can be loaded from the path alone.
+
+ .ssp and .ocda.db come back filled. the three source forms come back
+ with loaded = false and a note saying where they are handled, because
+ reading them needs the manifest, environment and configuration that
+ spine.d builds, not a file path.
+ +/
+ LoadedAbstraction abstractionLoad(string _path) {
+ LoadedAbstraction _out;
+ _out.path = _path;
+ _out.source = abstractionSourceOf(_path);
+ if (!_path.exists) {
+ _out.note = "no such file or directory";
+ return _out;
+ }
+ final switch (_out.source) {
+ case AbstractionSource.ssp:
+ _out.doc = sspReadFile(_path);
+ _out.loaded = (_out.doc.section_order.length > 0);
+ if (!_out.loaded) { _out.note = "no object sections found"; }
+ break;
+ case AbstractionSource.ocda_db:
+ _out.doc = dbReadFile(_path);
+ _out.loaded = (_out.doc.section_order.length > 0);
+ if (!_out.loaded) { _out.note = "no object sections found"; }
+ break;
+ case AbstractionSource.markup:
+ case AbstractionSource.pod:
+ case AbstractionSource.pod_zip:
+ _out.note = "a source form: read by the parser,"
+ ~ " sisudoc.ocda.meta.metadoc spineAbstraction, which needs the"
+ ~ " manifest, environment and configuration spine assembles";
+ break;
+ case AbstractionSource.unknown:
+ _out.note = "not a document source spine knows";
+ break;
+ }
+ return _out;
+ }
+
+ /+ ↓ what was loaded, in a few lines: for the eye, and for a check that a
+ given artefact really does hold what it should +/
+ string[] abstractionLoadSummary(LoadedAbstraction _l) {
+ string[] _out;
+ _out ~= "source: " ~ abstractionSourceName(_l.source);
+ _out ~= "path: " ~ _l.path;
+ if (!_l.loaded) {
+ _out ~= "loaded: no";
+ if (_l.note.length > 0) { _out ~= "note: " ~ _l.note; }
+ return _out;
+ }
+ _out ~= "loaded: yes";
+ if (_l.doc.source.length > 0) { _out ~= "document: " ~ _l.doc.source; }
+ if ("title.main" in _l.doc.meta) {
+ _out ~= "title: " ~ _l.doc.meta["title.main"];
+ }
+ if ("creator.author" in _l.doc.meta) {
+ _out ~= "author: " ~ _l.doc.meta["creator.author"];
+ }
+ _out ~= "header: " ~ _l.doc.meta.length.to!string ~ " meta, "
+ ~ _l.doc.make.length.to!string ~ " make, "
+ ~ _l.doc.doc_has.length.to!string ~ " doc_has";
+ int _objs, _citable, _headings;
+ string[] _sections;
+ foreach (section; _l.doc.section_order) {
+ int _n;
+ foreach (obj; _l.doc.abstraction[section]) {
+ ++_objs; ++_n;
+ if (obj.metainfo.ocn > 0) { ++_citable; }
+ if (obj.metainfo.is_a == "heading") { ++_headings; }
+ }
+ _sections ~= section ~ " " ~ _n.to!string;
+ }
+ _out ~= "objects: " ~ _objs.to!string ~ " (" ~ _citable.to!string
+ ~ " citable, " ~ _headings.to!string ~ " headings)";
+ _out ~= "sections: " ~ _sections.join(", ");
+ return _out;
+ }
+}
diff --git a/src/sisudoc/ocda/abstraction/package.d b/src/sisudoc/ocda/abstraction/package.d
index 3a58b2e..4131a90 100644
--- a/src/sisudoc/ocda/abstraction/package.d
+++ b/src/sisudoc/ocda/abstraction/package.d
@@ -86,3 +86,4 @@ public import sisudoc.ocda.meta.metadoc_from_src; // docAbstraction (B-layer)
public import sisudoc.ocda.abstraction.ssp; // spineAbstractionTxt (.ssp)
public import sisudoc.ocda.abstraction.ssp_in; // spineAbstractionRead (.ssp back in)
public import sisudoc.ocda.abstraction.db_in; // spineAbstractionDbRead (.db back in)
+public import sisudoc.ocda.abstraction.load; // spineAbstractionLoad (one way in)
diff --git a/src/sisudoc/spine.d b/src/sisudoc/spine.d
index a1b30bd..1c68045 100644
--- a/src/sisudoc/spine.d
+++ b/src/sisudoc/spine.d
@@ -215,6 +215,7 @@ string program_name = "spine";
"output" : "",
"ssp-round-trip" : "",
"db-round-trip" : "",
+ "abstraction-source" : "",
"www-http" : "",
"www-host" : "",
"www-host-doc-root" : "",
@@ -277,6 +278,7 @@ string program_name = "spine";
"output", "=/path/to/output/dir specify where to place output", &settings["output"],
"ssp-round-trip", "=/path/to/file.ssp read a .ssp back and re-emit it on stdout", &settings["ssp-round-trip"],
"db-round-trip", "=/path/to/file.ocda.db read it back and emit .ssp on stdout", &settings["db-round-trip"],
+ "abstraction-source", "=/path/to/(.sst|pod|.ssp|.ocda.db) identify it, and load it if it is an abstraction", &settings["abstraction-source"],
"parallel", "parallelisation", &opts["parallel"],
"parallel-subprocesses", "nested parallelisation", &opts["parallel-subprocesses"],
"pdf", "latex output for pdfs", &opts["pdf"],
@@ -438,6 +440,22 @@ string program_name = "spine";
stdout.flush;
exit(0);
}
+ /+ ↓ say what a path is and, where it is an abstraction artefact, load it
+ and report what came back. one way in for any of the sources spine
+ knows, which is what the loader package is for.
+ exit 0 when an abstraction was loaded, 1 when it was not, with the
+ reason given: an unknown path, or a source form that goes through the
+ parser rather than through a file read
+ +/
+ if (settings["abstraction-source"].length > 0) {
+ import sisudoc.ocda.abstraction.load;
+ mixin spineAbstractionLoad;
+ auto _loaded = abstractionLoad(settings["abstraction-source"]);
+ foreach (line; abstractionLoadSummary(_loaded)) { writeln(line); }
+ import core.stdc.stdlib : exit;
+ stdout.flush;
+ exit(_loaded.loaded ? 0 : 1);
+ }
enum outTask { source_or_pod, sqlite, sqlite_multi, latex, odt, epub, html_scroll, html_seg, html_stuff, text, skel }
struct OptActions {
@trusted bool allow_downloads() {