]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5422] Initial kea-docgen implemented
authorTomek Mrugalski <tomasz@isc.org>
Tue, 12 Jun 2018 21:32:35 +0000 (23:32 +0200)
committerTomek Mrugalski <tomasz@isc.org>
Tue, 29 Oct 2019 17:57:12 +0000 (18:57 +0100)
configure.ac
doc/guide/Makefile.am
doc/guide/kea-guide.xml
tools/cmd-docgen/Makefile.am [new file with mode: 0644]
tools/cmd-docgen/cmd_docgen.cc [new file with mode: 0644]

index ba86b3e0ade097c65c211a2976863eb6fbc6297d..bf731bce1986f8844204e5f2df9ea2ffa5f6208a 100644 (file)
@@ -1648,6 +1648,7 @@ AC_CONFIG_FILES([Makefile
                  src/share/database/scripts/pgsql/upgrade_4.0_to_5.0.sh
                  tools/Makefile
                  tools/path_replacer.sh
+                 tools/cmd-docgen/Makefile
 ])
 
 AC_CONFIG_COMMANDS([permissions], [
index f13cca197b00677ae980a7533f30ab8b981a1ffc..ece9ed07fea82f6fb747b507726965b278fbc086 100644 (file)
@@ -10,6 +10,7 @@ DOCBOOK += keactrl.xml dhcp4-srv.xml dhcp6-srv.xml lease-expiration.xml logging.
 DOCBOOK += ddns.xml hooks.xml hooks-ha.xml hooks-host-cache.xml hooks-lease-cmds.xml
 DOCBOOK += hooks-radius.xml hooks-stat-cmds.xml libdhcp.xml lfc.xml stats.xml
 DOCBOOK += ctrl-channel.xml faq.xml classify.xml shell.xml agent.xml netconf.xml
+DOCBOOK += api.xml
 
 EXTRA_DIST = $(DOCBOOK)
 
index f0e42a47ed2fa698e0705a8a0699a98ce7627396..ef69b636cd8a6be51b0d8fb193673efa3a2f2f22 100644 (file)
@@ -92,6 +92,8 @@
 
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="netconf.xml"/>
 
+  <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="api.xml"/>
+
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="faq.xml"/>
 
   <chapter xml:id="acknowledgments">
diff --git a/tools/cmd-docgen/Makefile.am b/tools/cmd-docgen/Makefile.am
new file mode 100644 (file)
index 0000000..cbf1894
--- /dev/null
@@ -0,0 +1,20 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+AM_LDFLAGS = -static
+
+if GENERATE_DOCS
+noinst_PROGRAMS = cmd_docgen
+cmd_docgen_SOURCES = cmd_docgen.cc
+
+# For bare distcheck
+EXTRA_DIST = cmd_docgen
+
+cmd_docgen_LDADD = $(top_builddir)/src/lib/cc/libkea-cc.la
+cmd_docgen_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+
+endif
diff --git a/tools/cmd-docgen/cmd_docgen.cc b/tools/cmd-docgen/cmd_docgen.cc
new file mode 100644 (file)
index 0000000..aaa5c1a
--- /dev/null
@@ -0,0 +1,239 @@
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <vector>
+#include <map>
+
+#include <exceptions/exceptions.h>
+#include <cc/data.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::data;
+
+
+
+class DocGen {
+public:
+
+    const string OUTPUT = "../guide/api.xml";
+
+    void loadFiles(const vector<string>& files) {
+
+        map <string, ElementPtr> commands;
+
+        int cnt = 0;
+
+        try {
+            for (auto f : files) {
+                string cmd = f.substr(0, f.find("."));
+
+                cout << "Loading description of command " << cmd;
+                ElementPtr x = Element::fromJSONFile(f, false);
+                cout << ".";
+
+                sanityCheck(f, x);
+                cout << ".";
+                cmds_.insert(make_pair(cmd, x));
+                cout << "loaded" << endl;
+
+                cnt++;
+            }
+        } catch (const Unexpected& e) {
+            isc_throw(Unexpected, e.what() << " while processing "
+                      << cnt + 1 << " file out of " << files.size());
+        }
+
+        cout << "Loaded " << cmds_.size() << " commands out of " << files.size()
+             << " file(s)" << endl;
+    }
+
+
+    void requireString(const ElementPtr& x, const string& name, const string& fname) {
+        if (!x->contains(name)) {
+            isc_throw(Unexpected, "Mandatory '" + name + " field missing while "
+                      "processing file " + fname);
+        }
+        if (x->get(name)->getType() != Element::string) {
+            isc_throw(BadValue, "'" + name + " field is present, but is not a string"
+                      " in file " + fname);
+        }
+        if (x->get(name)->stringValue().empty()) {
+            isc_throw(BadValue, "'" + name + " field is present, is a string, but is "
+                      "empty in file " + fname);
+        }
+    }
+    
+    void requireList(const ElementPtr& x, const string& name, const string& fname) {
+        if (!x->contains(name)) {
+            isc_throw(Unexpected, "Mandatory '" + name + " field missing while "
+                      "processing file " + fname);
+        }
+        if (x->get(name)->getType() != Element::list) {
+            isc_throw(BadValue, "'" + name + " field is present, but is not a list "
+                      "in file " + fname);
+        }
+        
+        ConstElementPtr l = x->get(name);
+        
+        if (l->size() == 0) {
+            isc_throw(BadValue, "'" + name + " field is a list, but is empty in file "
+                      + fname);
+        }
+        
+        // todo: check that every element is a string
+    }
+    
+    void sanityCheck(const string& fname, const ElementPtr& x) {
+        requireString(x, "name", fname);
+        requireString(x, "brief", fname);
+        requireList  (x, "support", fname);
+        requireString(x, "avail", fname);
+        requireString(x, "brief", fname);
+        requireString(x, "cmd-syntax", fname);
+        requireString(x, "cmd-comment", fname);
+        requireString(x, "resp-comment", fname);
+        requireString(x, "resp-syntax", fname);
+    }
+
+    void generateCopyright(stringstream& f) {
+        f << "<!--" << endl;
+        f << " - Copyright (C) 2018 Internet Systems Consortium, Inc. (\"ISC\")" << endl;
+        f << " -" << endl;
+        f << " - This Source Code Form is subject to the terms of the Mozilla Public" << endl;
+        f << " - License, v. 2.0. If a copy of the MPL was not distributed with this" << endl;
+        f << " - file, You can obtain one at http://mozilla.org/MPL/2.0/." << endl;
+        f << " -->" << endl;
+        f << endl;
+        f << "<!-- autogenerated using cmd_docgen. Do not edit by hand! -->" << endl;
+    }
+    
+    void generateOutput() {
+        
+        stringstream f;
+
+        generateCopyright(f);
+        
+        f << "<chapter xmlns=\"http://docbook.org/ns/docbook\" version=\"5.0\" xml:id=\"api\">"
+          << endl;
+        f << "  <title>Management API Reference</title>" << endl;
+        
+        // Generate initial list of commands
+        f << "  <para>Kea currently supports " << cmds_.size() << " commands:" << endl
+          << "    <orderedlist>" << endl;
+        
+        for (auto cmd : cmds_) {
+            f << "      <listitem><simpara>" << cmd.first << "</simpara></listitem>" << endl;
+        }
+        
+        f << "    </orderedlist>" << endl;
+        f << "  </para>" << endl;
+        
+        // Generate actual commands references.
+        generateCommands(f);
+        
+        f << "</chapter>" << endl;
+        
+        cout << "----------------" << endl;
+        ofstream file(OUTPUT.c_str(), ofstream::trunc);
+        file << f.str();
+        cout << f.str();
+        cout << "----------------" << endl;
+    }
+    
+    void generateCommands(stringstream& f){
+        
+        for (auto cmd : cmds_) {
+            f << "<!-- start of " << cmd.first << " -->" << endl;
+            f << "<section xml:id=\"reference-" << cmd.first << "\">" << endl;
+            f << "<title>" << cmd.first << " reference</title>" << endl;
+            generateCommand(f, cmd.second);
+            f << "</section>" << endl;
+            f << "<!-- end of " << cmd.first << " -->" << endl;
+            f << endl;
+        }
+    }
+
+void replaceAll(std::string& str, const std::string& from, const std::string& to) {
+    if(from.empty())
+        return;
+    size_t start_pos = 0;
+    while((start_pos = str.find(from, start_pos)) != std::string::npos) {
+        str.replace(start_pos, from.length(), to);
+        start_pos += to.length();
+    }
+}
+    
+string escapeString(string txt) {
+
+    replaceAll(txt, "<", "&lt;");
+    replaceAll(txt, ">", "&gt;");
+    return (txt);
+}
+
+void generateCommand(stringstream& f, const ElementPtr& cmd) {
+
+    // command overview
+    f << "<para><command>" << cmd->get("name")->stringValue() << "</command> - "
+      << cmd->get("brief")->stringValue() << "</para>" << endl << endl;
+
+    // command can be issued to the following daemons
+    f << "<para>Supported by: ";
+    ConstElementPtr daemons = cmd->get("support");
+    for (int i = 0; i < daemons->size(); i++) {
+        if (i) {
+            f << ", ";
+        }
+
+        f << daemons->get(i)->stringValue();
+    }
+    f << "</para>" << endl << endl;
+
+    // availability
+    f << "<para>Availability: " << cmd->get("avail")->stringValue() << "</para>"
+      << endl << endl;
+
+    // description and examples
+    f << "<para>Description and examples: See <xref linkend=\"cmd-" 
+      << cmd->get("name")->stringValue() << "\"/></para>" << endl << endl;
+
+    // Command syntax:
+    f << "<para>Command syntax:" << endl
+      << "  <screen>" << escapeString(cmd->get("cmd-syntax")->stringValue())
+      << "</screen>"
+      << endl
+      << cmd->get("cmd-comment")->stringValue() << "</para>" << endl << endl;
+
+    // Response syntax
+    f << "<para>Response syntax:" << endl
+      << "  <screen>" << escapeString(cmd->get("resp-syntax")->stringValue())
+      << "</screen>"
+      << endl
+      << cmd->get("resp-comment")->stringValue() << "</para>" << endl << endl;
+}
+
+    map<string, ElementPtr> cmds_;
+};
+
+int main(int argc, const char*argv[]) {
+
+    vector<string> files;
+
+    for (int i = 1; i < argc; i++) {
+        files.push_back(string(argv[i]));
+    }
+
+    cout << "Loading " << files.size() << " files(s)." << endl;
+
+    try {
+        DocGen doc_gen;
+
+        doc_gen.loadFiles(files);
+
+        doc_gen.generateOutput();
+    } catch (const exception& e) {
+        cerr << "ERROR: " << e.what() << endl;
+    }
+
+    return (0);
+}