diff options
Diffstat (limited to 'src/sisudoc')
| -rw-r--r-- | src/sisudoc/ocda/abstraction/load.d | 205 | ||||
| -rw-r--r-- | src/sisudoc/ocda/abstraction/package.d | 1 | ||||
| -rw-r--r-- | src/sisudoc/spine.d | 18 |
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() { |
