+// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
#include <iostream>
#include <fstream>
#include <sstream>
using namespace isc::data;
-
+/// @brief API documentation generator
class DocGen {
public:
+ /// Output location of a file.
const string OUTPUT = "guide/api.xml";
+ /// Controls whether to print out extra information.
bool verbose = false;
+ /// @brief Load JSON files that each contain description of an API command
+ ///
+ /// @param files a vector with names of files.
void loadFiles(const vector<string>& files) {
map <string, ElementPtr> commands;
<< " file(s)" << endl;
}
-
+ /// @brief checks if mandatory string parameter is specified
+ ///
+ /// @param x a map that is being checked
+ /// @param name name of the string element expected to be there
+ /// @param fname name of the file (used in error reporting)
+ /// @throw Unexpected if missing, different type or empty
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"
+ isc_throw(Unexpected, "'" + 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 "
+ isc_throw(Unexpected, "'" + name + " field is present, is a string, but is "
"empty in file " + fname);
}
}
+ /// @brief checks if mandatory list parameter is specified
+ ///
+ /// @param x a map that is being checked
+ /// @param name name of the list element expected to be there
+ /// @param fname name of the file (used in error reporting)
+ /// @throw Unexpected if missing, different type or empty
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 "
+ isc_throw(Unexpected, "'" + 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 "
+ isc_throw(Unexpected, "'" + name + " field is a list, but is empty in file "
+ fname);
}
// todo: check that every element is a string
}
+ /// @brief Checks that the essential parameters for each command are defined
+ ///
+ /// @param fname name of the file the data was read from (printed if error is detected)
+ /// @param x a JSON map that contains content of the file
void sanityCheck(const string& fname, const ElementPtr& x) {
requireString(x, "name", fname);
requireString(x, "brief", fname);
//requireString(x, "resp-comment", fname);
}
+ /// @brief Writes ISC copyright note to the stream
+ ///
+ /// @param f stream to write copyrights to
void generateCopyright(stringstream& f) {
f << "<!--" << endl;
f << " - Copyright (C) 2018 Internet Systems Consortium, Inc. (\"ISC\")" << endl;
f << "<!-- autogenerated using cmd_docgen. Do not edit by hand! -->" << endl;
}
+ /// @brief generates a link to command
+ ///
+ /// @param f stream to write the generated link to
+ /// @param cmd name of the command
void generateCmdLink(stringstream& f, const string& cmd) {
- f << "<command><link linkend=\"ref-" << cmd << "\">" << cmd << "</link></command>" << endl;
+ f << "<command><link linkend=\"ref-" << cmd << "\">" << cmd
+ << "</link></command>" << endl;
}
+ /// @brief generates lists of all commands.
+ ///
+ /// Currently there are several lists (or rather lists of lists). They all enumerate
+ /// commands, but each list serving a different purpose:
+ /// - list of commands supported by a daemon
+ /// - list of commands provided by a hook
+ ///
+ /// @param f stream to write the generated lists to
void generateLists(stringstream& f) {
// Generate a list of all commands
f << " <para>Kea currently supports " << cmds_.size() << " commands:" << endl;
}
}
- cout << "### " << all_daemons.size() << " daemon(s) detected." << endl;
- cout << "### " << all_hooks.size() << " hook lib(s) detected." << endl;
+ cout << all_daemons.size() << " daemon(s) detected." << endl;
+ cout << all_hooks.size() << " hook lib(s) detected." << endl;
for (auto daemon : all_daemons) {
f << "<para xml:id=\"commands-" << daemon << "\">"
}
+ /// @brief generates the whole API documentation
void generateOutput() {
stringstream f;
cout << "Output written to " << OUTPUT << endl;
}
+ /// @brief generate sections for all commands
+ ///
+ /// @param f stream to write the commands to
void generateCommands(stringstream& f){
for (auto cmd : cmds_) {
}
}
-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();
+ /// @brief replace all strings
+ ///
+ /// @param str [in,out] this string will have some replacements
+ /// @param from what to replace
+ /// @param to what to replace with
+ 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, "<", "<");
- replaceAll(txt, ">", ">");
- return (txt);
-}
-string standardResponseSyntax() {
- stringstream t;
+ /// @brief escapes string to be safe for XML (docbook)
+ ///
+ /// @param txt string to be escaped
+ /// @return escaped string
+ string escapeString(string txt) {
- t << "{" << endl
- << " \"result\": <integer>," << endl
- << " \"text\": <string>" << endl
- << "}" << endl;
- return (t.str());
-}
+ replaceAll(txt, "<", "<");
+ replaceAll(txt, ">", ">");
+ return (txt);
+ }
-string standardResponseComment() {
- stringstream t;
-
- t << "Result is an integer representation of the status. Currently supported"
- << " statuses are:" << endl
- << "<itemizedlist>" << endl
- << " <listitem><para>0 - success</para></listitem>" << endl
- << " <listitem><para>1 - error</para></listitem>" << endl
- << " <listitem><para>2 - unsupported</para></listitem>" << endl
- << " <listitem><para>3 - empty (command was completed successfully, but "
- << "no data was affected or returned)</para>"
- << "</listitem>" << endl
- << "</itemizedlist>" << endl;
- return (t.str());
-}
+ /// @brief generates standard description of command's response
+ ///
+ /// If a command doesn't have response syntax specified, we will
+ /// assume it follows the usual syntax and provide the default description.
+ string standardResponseSyntax() {
+ stringstream t;
+
+ t << "{" << endl
+ << " \"result\": <integer>," << endl
+ << " \"text\": <string>" << endl
+ << "}" << endl;
+ return (t.str());
+ }
-void generateCommand(stringstream& f, const ElementPtr& cmd) {
+ /// @brief generates standard description of command's comment
+ ///
+ /// If a command doesn't have response syntax comment specified, we will
+ /// assume it follows the usual syntax and provide the default description.
+ string standardResponseComment() {
+ stringstream t;
+
+ t << "Result is an integer representation of the status. Currently supported"
+ << " statuses are:" << endl
+ << "<itemizedlist>" << endl
+ << " <listitem><para>0 - success</para></listitem>" << endl
+ << " <listitem><para>1 - error</para></listitem>" << endl
+ << " <listitem><para>2 - unsupported</para></listitem>" << endl
+ << " <listitem><para>3 - empty (command was completed successfully, but "
+ << "no data was affected or returned)</para>"
+ << "</listitem>" << endl
+ << "</itemizedlist>" << endl;
+ return (t.str());
+ }
- // command overview
- f << "<para xml:id=\"ref-" << cmd->get("name")->stringValue() << "\"><command>"
- << cmd->get("name")->stringValue() << "</command> - "
- << cmd->get("brief")->stringValue() << "</para>" << endl << endl;
+ /// @brief generates command description
+ ///
+ /// @param f stream to write the description to
+ /// @param cmd pointer to JSON structure that describes the command
+ void generateCommand(stringstream& f, const ElementPtr& cmd) {
+
+ // command overview
+ f << "<para xml:id=\"ref-" << cmd->get("name")->stringValue() << "\"><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 << ", ";
+ }
- // 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 << "<command><link linkend=\"commands-" << daemons->get(i)->stringValue()
+ << "\">" << daemons->get(i)->stringValue() << "</link></command>";
+ }
+ f << "</para>" << endl << endl;
+
+ // availability
+ f << "<para>Availability: " << cmd->get("avail")->stringValue();
+ auto hook = cmd->get("hook");
+ if (hook) {
+ f << " (<link linkend=\"commands-" << hook->stringValue() << "-lib\">"
+ << hook->stringValue() << "</link>)";
+ } else {
+ f << " (built-in)";
}
- f << "<command><link linkend=\"commands-" << daemons->get(i)->stringValue()
- << "\">" << daemons->get(i)->stringValue() << "</link></command>";
- }
- f << "</para>" << endl << endl;
-
- // availability
- f << "<para>Availability: " << cmd->get("avail")->stringValue();
- auto hook = cmd->get("hook");
- if (hook) {
- f << " (<link linkend=\"commands-" << hook->stringValue() << "-lib\">"
- << hook->stringValue() << "</link>)";
- } else {
- f << " (built-in)";
- }
-
- f << "</para>" << endl << endl;
-
- // description and examples
- f << "<para>Description and examples: See <xref linkend=\"command-"
- << cmd->get("name")->stringValue() << "\"/></para>" << endl << endl;
-
- // Command syntax:
- f << "<para>Command syntax:" << endl;
- if (cmd->contains("cmd-syntax")) {
- f << " <screen>" << escapeString(cmd->get("cmd-syntax")->stringValue())
- << "</screen>" << endl;
- } else {
- f << " <screen>{" << endl
- << " \"command\": \"" << cmd->get("name")->stringValue() << "\"" << endl
- << "}</screen>" << endl;
- }
- if (cmd->contains("cmd-comment")) {
- f << cmd->get("cmd-comment")->stringValue();
- }
- f << "</para>" << endl << endl;
+ f << "</para>" << endl << endl;
+
+ // description and examples
+ f << "<para>Description and examples: See <xref linkend=\"command-"
+ << cmd->get("name")->stringValue() << "\"/></para>" << endl << endl;
+
+ // Command syntax:
+ f << "<para>Command syntax:" << endl;
+ if (cmd->contains("cmd-syntax")) {
+ f << " <screen>" << escapeString(cmd->get("cmd-syntax")->stringValue())
+ << "</screen>" << endl;
+ } else {
+ f << " <screen>{" << endl
+ << " \"command\": \"" << cmd->get("name")->stringValue() << "\"" << endl
+ << "}</screen>" << endl;
+ }
+ if (cmd->contains("cmd-comment")) {
+ f << cmd->get("cmd-comment")->stringValue();
+ }
+ f << "</para>" << endl << endl;
- // Response syntax
- f << "<para>Response syntax:" << endl
- << " <screen>";
+ // Response syntax
+ f << "<para>Response syntax:" << endl
+ << " <screen>";
- if (cmd->contains("resp-syntax")) {
- f << escapeString(cmd->get("resp-syntax")->stringValue());
- } else {
- f << escapeString(standardResponseSyntax());
- }
- f << "</screen>" << endl;
+ if (cmd->contains("resp-syntax")) {
+ f << escapeString(cmd->get("resp-syntax")->stringValue());
+ } else {
+ f << escapeString(standardResponseSyntax());
+ }
+ f << "</screen>" << endl;
- if (cmd->contains("resp-comment")) {
- f << cmd->get("resp-comment")->stringValue();
- } else {
- f << standardResponseComment();
+ if (cmd->contains("resp-comment")) {
+ f << cmd->get("resp-comment")->stringValue();
+ } else {
+ f << standardResponseComment();
+ }
+ f << "</para>" << endl << endl;
}
- f << "</para>" << endl << endl;
-}
map<string, ElementPtr> cmds_;
};