diff options
| author | Ralph Amissah <ralph.amissah@gmail.com> | 2020-04-14 15:48:25 -0400 | 
|---|---|---|
| committer | Ralph Amissah <ralph.amissah@gmail.com> | 2020-05-20 11:27:26 -0400 | 
| commit | 3cceed4af466d11e0735246bbd09f8451f6f9383 (patch) | |
| tree | 7b38bffe46a1d913faaebdfac4b95466846038ea | |
| parent | trust more, else minor (diff) | |
cgi search form: path; theme; download remote src
- config output path
- dark theme
- allow downloads flag for download of remote file
  cgi.d
| -rw-r--r-- | dub.selections.json | 2 | ||||
| -rw-r--r-- | misc/util/d/cgi/search/dub.sdl | 2 | ||||
| -rw-r--r-- | org/out_cgi_search_sqlite.org | 116 | ||||
| -rw-r--r-- | org/output_hub.org | 7 | ||||
| -rw-r--r-- | org/spine.org | 6 | ||||
| -rw-r--r-- | org/util_cgi_d_sqlite_search.org | 2 | ||||
| -rw-r--r-- | src/doc_reform/io_out/cgi_sqlite_search_form.d | 116 | ||||
| -rw-r--r-- | src/doc_reform/io_out/hub.d | 7 | ||||
| -rwxr-xr-x | src/doc_reform/spine.d | 6 | 
9 files changed, 177 insertions, 87 deletions
| diff --git a/dub.selections.json b/dub.selections.json index c8cd421..b4ef190 100644 --- a/dub.selections.json +++ b/dub.selections.json @@ -2,7 +2,7 @@  	"fileVersion": 1,  	"versions": {  		"d2sqlite3": "0.18.3", -		"dyaml": "0.8.0", +		"dyaml": "0.8.1",  		"imageformats": "7.0.2",  		"tinyendian": "0.2.0"  	} diff --git a/misc/util/d/cgi/search/dub.sdl b/misc/util/d/cgi/search/dub.sdl index b859f42..f598147 100644 --- a/misc/util/d/cgi/search/dub.sdl +++ b/misc/util/d/cgi/search/dub.sdl @@ -4,6 +4,8 @@ authors "ralph"  copyright "Copyright © 2020, ralph"  license "GPL-3.0+"  dependency "d2sqlite3" version="~>0.18.3" +dependency "arsd-official": "7.2.0" +  subConfiguration "arsd-official:cgi" "cgi"  targetType "executable"  targetPath "./cgi-bin"  mainSourceFile "src/spine_cgi_sqlite_search.d" diff --git a/org/out_cgi_search_sqlite.org b/org/out_cgi_search_sqlite.org index 3145870..852b93f 100644 --- a/org/out_cgi_search_sqlite.org +++ b/org/out_cgi_search_sqlite.org @@ -115,9 +115,32 @@ void cgi_function_intro(Cgi cgi) {  mixin GenericMain!cgi_function_intro;  ≓",    _sqlite_db_fn, +  (opt_action.css_theme_default) ? "FFFFFF" : "000000", +  (opt_action.css_theme_default) ? "000000" : "CCCCCC", +  (opt_action.css_theme_default) ? "FFFFFF" : "000000", +  (opt_action.css_theme_default) ? "FFFFFF" : "000000", +  (opt_action.css_theme_default) ? "003399" : "FFFFFF", +  (opt_action.css_theme_default) ? "003399" : "999999", +  "000000", +  (opt_action.css_theme_default) ? "F9F9AA" : "555555", +  (opt_action.css_theme_default) ? "777777" : "BBBBBB", +  (opt_action.css_theme_default) ? "32CD32" : "9ACD32", +  (opt_action.css_theme_default) ? "777777" : "BBBBBB", +  (opt_action.css_theme_default) ? "FFFFFF" : "000000", +  (opt_action.css_theme_default) ? "003399" : "888888", +  (opt_action.css_theme_default) ? "000000" : "FFFFFF", +  (opt_action.css_theme_default) ? "FFFFFF" : "777777", +  (opt_action.css_theme_default) ? "000000" : "FFFF48", +  (opt_action.css_theme_default) ? "FFFF48" : "777748", +  (opt_action.css_theme_default) ? "222222" : "AAAAAA",    _cgi_search_script,  ).strip; -    auto pth_sqlite_cgi = spinePathsSQLiteCGI!()(_cgi_search_script_raw_fn_d, _cgi_search_script, opt_action.output_dir_set); // TODO +    string _cgi_path = (opt_action.output_dir_set.length > 0) +    ? opt_action.output_dir_set +    : (make_and_meta_struct.conf.w_srv_data_root_path.length > 0) +      ? make_and_meta_struct.conf.w_srv_data_root_path +      : ""; +    auto pth_sqlite_cgi = spinePathsSQLiteCGI!()(_cgi_search_script_raw_fn_d, _cgi_search_script, _cgi_path);      { // cgi-bin search form src d        try {          if (!exists(pth_sqlite_cgi.src)) { @@ -142,6 +165,7 @@ mixin GenericMain!cgi_function_intro;  <<cgi_dub_sdl>>  ≓",    "~>0.18.3", // d2sqlite3 dependency version +  "~>7.2.0",  // arsd-official:cgi dependency version    "src/" ~ _cgi_search_script_raw_fn_d,    _cgi_search_script  ).strip; @@ -156,25 +180,27 @@ mixin GenericMain!cgi_function_intro;          // Handle error        }      } -    { // get cgi.d -      import std.net.curl, std.stdio; -      char[] cgi_d; -      try { -        cgi_d = get!HTTP("https://raw.githubusercontent.com/adamdruppe/arsd/master/cgi.d"); -      } catch (ErrnoException ex) { -        // Handle error -        // CurlCode perform(ThrowOnError throwOnError = Yes.throwOnError); -        CurlCode perform(ThrowOnError throwOnError = No.throwOnError); -      } -      if (cgi_d && cgi_d.length > 0) { -        try { -          auto f = File(pth_sqlite_cgi.cgi_d_path_out, "w"); -          f.write(cgi_d); -        } catch (ErrnoException ex) { -          // Handle error -        } -      } -    } +    // { // get cgi.d +    //   // import std.net.curl, std.stdio; +    //   // char[] cgi_d; +    //   // if (opt_action.allow_downloads) { +    //   //   try { +    //   //     cgi_d = get!HTTP("https://raw.githubusercontent.com/adamdruppe/arsd/master/cgi.d"); +    //   //   } catch (ErrnoException ex) { +    //   //     // Handle error +    //   //     // CurlCode perform(ThrowOnError throwOnError = Yes.throwOnError); +    //   //     CurlCode perform(ThrowOnError throwOnError = No.throwOnError); +    //   //   } +    //   //   if (cgi_d && cgi_d.length > 0) { +    //   //     try { +    //   //       auto f = File(pth_sqlite_cgi.cgi_d_path_out, "w"); +    //   //       f.write(cgi_d); +    //   //     } catch (ErrnoException ex) { +    //   //       // Handle error +    //   //     } +    //   //   } +    //   // } +    // }    }  }  #+END_SRC @@ -791,44 +817,52 @@ string show_matched_objects (string fn) {    }    body {      height                   : 100vh; -    background-color         : #FFFFFF; +    background-color         : #%s;    }    body { -    color                    : #000000; -    background               : #FFFFFF; -    background-color         : #FFFFFF; +    color                    : #%s; +    background               : #%s; +    background-color         : #%s;    }    a:link { -    color                    : #003399; +    color                    : #%s;      text-decoration          : none;    }    a:visited { -    color                    : #003399; +    color                    : #%s;      text-decoration          : none;    }    a:hover { -    color                    : #000000; -    background-color         : #F9F9AA; +    color                    : #%s; +    background-color         : #%s;    }    a.lnkocn:link { -    color                    : #777777; +    color                    : #%s;      text-decoration          : none;    }    a.lnkocn:visited { -    color                    : #32CD32; +    color                    : #%s;      text-decoration          : none;    }    a.lnkocn:hover { -    color                    : #777777; +    color                    : #%s;      font-size                : 15px;    }    a:hover img { -    background-color         : #FFFFFF; +    background-color         : #%s;    }    a:active { -    color                    : #003399; +    color                    : #%s;      text-decoration          : underline;    } +  textarea { +    color                    : #%s; +    background-color         : #%s; +  } +  span.match { +    color                    : #%s; +    background-color         : #%s; +  }    p.norm { }    p.i1 { padding-left        : 1em; }    p.i2 { padding-left        : 2em; } @@ -1267,7 +1301,7 @@ string show_matched_objects (string fn) {    <table summary="home button / home information" border="0" cellpadding="2" cellspacing="0">    <tr><td align="left">     <br><a href="http://sisudoc.org/" target="_top"> -     <b>SiSU</b> +     <label for="find"><b>≅ SiSU spine</b></label>     </a>     <br><a href="http://git.sisudoc.org/" target="_top">       git @@ -1276,7 +1310,7 @@ string show_matched_objects (string fn) {    </table>   </td>   <td> -   <label for="find"><b>SiSU spine (generated) search form</b></label> +   <label for="find">(generated) search form</label>   </td></tr>   </table>   ┃"); @@ -1417,7 +1451,7 @@ string show_matched_objects (string fn) {      %%s    </td></tr></table>    <td valign=\"top\"><tr><td> -    <font size="2" color="#222222"> +    <font size="2" color="#%s">      <input type="hidden" name="db" value="%%s">      <input type="submit" value="SiSU search">      <input type="radio" name="rt" id="results_type_index" value="idx" %%s> index @@ -1552,9 +1586,7 @@ auto db = Database(conf.db_path ~ cv.db_selected);        string highlight_text_matched(string _txt, string search_field) {          string _mark_open   = "┤";          string _mark_close  = "├"; -        string _span_yellow = "<span style=\"background-color: #ffff48\">"; -        string _span_red    = "<span style=\"background-color: #ff48ff\">"; -        string _span_blue   = "<span style=\"background-color: #48ffff\">"; +        string _span_match = "<span class=\"match\">";          string _span_close  = "</span>";          string _sf_str      = search_field.strip.split("%%20").join(" ").strip;          string[] _sf_arr    = _sf_str.split(regex(r"\s+AND\s+|\s+OR\s+")); @@ -1574,13 +1606,13 @@ auto db = Database(conf.db_path ~ cv.db_selected);                  )(m.hit, rgx_marked_pair)                )(_txt, rgx_url);              _txt = replaceAll!(m => -                _span_yellow +                _span_match                  ~  m["keep"]                  ~ _span_close                )(_txt, rgx_marked_pair);            } else {              _txt = replaceAll!(m => -                _span_yellow +                _span_match                  ~  m.captures[0]                  ~ _span_close                )(_txt, rgx_matched_text); @@ -1816,6 +1848,8 @@ authors "Ralph Amissah"  copyright "Copyright © 2020, Ralph Amissah"  license "GPL-3.0+"  dependency "d2sqlite3" version="%s" +dependency "arsd-official:cgi" version="%s" +  subConfiguration "arsd-official:cgi" "cgi"  targetType "executable"  targetPath "./cgi-bin"  mainSourceFile "%s" diff --git a/org/output_hub.org b/org/output_hub.org index ba39e2e..3e66b29 100644 --- a/org/output_hub.org +++ b/org/output_hub.org @@ -265,7 +265,12 @@ if ((opt_action.cgi_search_form_codegen)) {      string _cgi_search_script_raw_fn_d = (opt_action.cgi_sqlite_search_filename_d.empty)        ? config.conf.w_srv_cgi_search_script_raw_fn_d        : opt_action.cgi_sqlite_search_filename_d; -    auto pth_sqlite_cgi = spinePathsSQLiteCGI!()(_cgi_search_script_raw_fn_d, _cgi_search_script, opt_action.output_dir_set); +    string _cgi_path = (opt_action.output_dir_set.length > 0) +    ? opt_action.output_dir_set +    : (config.conf.w_srv_data_root_path.length > 0) +      ? config.conf.w_srv_data_root_path +      : ""; +    auto pth_sqlite_cgi = spinePathsSQLiteCGI!()(_cgi_search_script_raw_fn_d, _cgi_search_script, _cgi_path);      writeln("sqlite cgi search form...");      writeln(" ", pth_sqlite_cgi.search_form_path_out);    } diff --git a/org/spine.org b/org/spine.org index f6dd0b2..bd8437a 100644 --- a/org/spine.org +++ b/org/spine.org @@ -429,7 +429,8 @@ string[string] settings = [  #+BEGIN_SRC d  auto helpInfo = getopt(args,    std.getopt.config.passThrough, -  "abstraction",                "document abstraction ",                                          &opts["abstraction"], +  "abstraction",                "document abstraction",                                           &opts["abstraction"], +  "allow-downloads",            "allow downloads (includes cgi.d from github)",                   &opts["allow-downloads"],    "assert",                     "set optional assertions on",                                     &opts["assertions"],    "cgi-search-form-codegen",    "generates (pre-compiled) d code for search of specified db",     &opts["cgi-search-form-codegen"],    "cgi-sqlite-search-filename", "=[filename]",                                                    &settings["cgi-sqlite-search-filename"], @@ -507,6 +508,9 @@ if (helpInfo.helpWanted) {  #+BEGIN_SRC d  enum outTask { source_or_pod, sqlite, sqlite_multi, latex, odt, epub, html_scroll, html_seg, html_stuff }  struct OptActions { +  @trusted bool allow_downloads() { +    return opts["allow-downloads"]; +  }    @trusted bool assertions() {      return opts["assertions"];    } diff --git a/org/util_cgi_d_sqlite_search.org b/org/util_cgi_d_sqlite_search.org index 07a6d15..86242f4 100644 --- a/org/util_cgi_d_sqlite_search.org +++ b/org/util_cgi_d_sqlite_search.org @@ -1141,6 +1141,8 @@ authors "ralph"  copyright "Copyright © 2020, ralph"  license "GPL-3.0+"  dependency "d2sqlite3" version="~>0.18.3" +dependency "arsd-official": "7.2.0" +  subConfiguration "arsd-official:cgi" "cgi"  targetType "executable"  targetPath "./cgi-bin"  mainSourceFile "src/spine_cgi_sqlite_search.d" diff --git a/src/doc_reform/io_out/cgi_sqlite_search_form.d b/src/doc_reform/io_out/cgi_sqlite_search_form.d index 4523c2d..7bfd5ef 100644 --- a/src/doc_reform/io_out/cgi_sqlite_search_form.d +++ b/src/doc_reform/io_out/cgi_sqlite_search_form.d @@ -521,44 +521,52 @@ void cgi_function_intro(Cgi cgi) {    }    body {      height                   : 100vh; -    background-color         : #FFFFFF; +    background-color         : #%s;    }    body { -    color                    : #000000; -    background               : #FFFFFF; -    background-color         : #FFFFFF; +    color                    : #%s; +    background               : #%s; +    background-color         : #%s;    }    a:link { -    color                    : #003399; +    color                    : #%s;      text-decoration          : none;    }    a:visited { -    color                    : #003399; +    color                    : #%s;      text-decoration          : none;    }    a:hover { -    color                    : #000000; -    background-color         : #F9F9AA; +    color                    : #%s; +    background-color         : #%s;    }    a.lnkocn:link { -    color                    : #777777; +    color                    : #%s;      text-decoration          : none;    }    a.lnkocn:visited { -    color                    : #32CD32; +    color                    : #%s;      text-decoration          : none;    }    a.lnkocn:hover { -    color                    : #777777; +    color                    : #%s;      font-size                : 15px;    }    a:hover img { -    background-color         : #FFFFFF; +    background-color         : #%s;    }    a:active { -    color                    : #003399; +    color                    : #%s;      text-decoration          : underline;    } +  textarea { +    color                    : #%s; +    background-color         : #%s; +  } +  span.match { +    color                    : #%s; +    background-color         : #%s; +  }    p.norm { }    p.i1 { padding-left        : 1em; }    p.i2 { padding-left        : 2em; } @@ -991,7 +999,7 @@ void cgi_function_intro(Cgi cgi) {    <table summary="home button / home information" border="0" cellpadding="2" cellspacing="0">    <tr><td align="left">     <br><a href="http://sisudoc.org/" target="_top"> -     <b>SiSU</b> +     <label for="find"><b>≅ SiSU spine</b></label>     </a>     <br><a href="http://git.sisudoc.org/" target="_top">       git @@ -1000,7 +1008,7 @@ void cgi_function_intro(Cgi cgi) {    </table>   </td>   <td> -   <label for="find"><b>SiSU spine (generated) search form</b></label> +   <label for="find">(generated) search form</label>   </td></tr>   </table>   ┃"); @@ -1116,7 +1124,7 @@ void cgi_function_intro(Cgi cgi) {      %%s    </td></tr></table>    <td valign=\"top\"><tr><td> -    <font size="2" color="#222222"> +    <font size="2" color="#%s">      <input type="hidden" name="db" value="%%s">      <input type="submit" value="SiSU search">      <input type="radio" name="rt" id="results_type_index" value="idx" %%s> index @@ -1220,9 +1228,7 @@ void cgi_function_intro(Cgi cgi) {        string highlight_text_matched(string _txt, string search_field) {          string _mark_open   = "┤";          string _mark_close  = "├"; -        string _span_yellow = "<span style=\"background-color: #ffff48\">"; -        string _span_red    = "<span style=\"background-color: #ff48ff\">"; -        string _span_blue   = "<span style=\"background-color: #48ffff\">"; +        string _span_match = "<span class=\"match\">";          string _span_close  = "</span>";          string _sf_str      = search_field.strip.split("%%20").join(" ").strip;          string[] _sf_arr    = _sf_str.split(regex(r"\s+AND\s+|\s+OR\s+")); @@ -1242,13 +1248,13 @@ void cgi_function_intro(Cgi cgi) {                  )(m.hit, rgx_marked_pair)                )(_txt, rgx_url);              _txt = replaceAll!(m => -                _span_yellow +                _span_match                  ~  m["keep"]                  ~ _span_close                )(_txt, rgx_marked_pair);            } else {              _txt = replaceAll!(m => -                _span_yellow +                _span_match                  ~  m.captures[0]                  ~ _span_close                )(_txt, rgx_matched_text); @@ -1431,9 +1437,32 @@ LIMIT %%s OFFSET %%s  mixin GenericMain!cgi_function_intro;  ≓",    _sqlite_db_fn, +  (opt_action.css_theme_default) ? "FFFFFF" : "000000", +  (opt_action.css_theme_default) ? "000000" : "CCCCCC", +  (opt_action.css_theme_default) ? "FFFFFF" : "000000", +  (opt_action.css_theme_default) ? "FFFFFF" : "000000", +  (opt_action.css_theme_default) ? "003399" : "FFFFFF", +  (opt_action.css_theme_default) ? "003399" : "999999", +  "000000", +  (opt_action.css_theme_default) ? "F9F9AA" : "555555", +  (opt_action.css_theme_default) ? "777777" : "BBBBBB", +  (opt_action.css_theme_default) ? "32CD32" : "9ACD32", +  (opt_action.css_theme_default) ? "777777" : "BBBBBB", +  (opt_action.css_theme_default) ? "FFFFFF" : "000000", +  (opt_action.css_theme_default) ? "003399" : "888888", +  (opt_action.css_theme_default) ? "000000" : "FFFFFF", +  (opt_action.css_theme_default) ? "FFFFFF" : "777777", +  (opt_action.css_theme_default) ? "000000" : "FFFF48", +  (opt_action.css_theme_default) ? "FFFF48" : "777748", +  (opt_action.css_theme_default) ? "222222" : "AAAAAA",    _cgi_search_script,  ).strip; -    auto pth_sqlite_cgi = spinePathsSQLiteCGI!()(_cgi_search_script_raw_fn_d, _cgi_search_script, opt_action.output_dir_set); // TODO +    string _cgi_path = (opt_action.output_dir_set.length > 0) +    ? opt_action.output_dir_set +    : (make_and_meta_struct.conf.w_srv_data_root_path.length > 0) +      ? make_and_meta_struct.conf.w_srv_data_root_path +      : ""; +    auto pth_sqlite_cgi = spinePathsSQLiteCGI!()(_cgi_search_script_raw_fn_d, _cgi_search_script, _cgi_path);      { // cgi-bin search form src d        try {          if (!exists(pth_sqlite_cgi.src)) { @@ -1461,6 +1490,8 @@ authors "Ralph Amissah"  copyright "Copyright © 2020, Ralph Amissah"  license "GPL-3.0+"  dependency "d2sqlite3" version="%s" +dependency "arsd-official:cgi" version="%s" +  subConfiguration "arsd-official:cgi" "cgi"  targetType "executable"  targetPath "./cgi-bin"  mainSourceFile "%s" @@ -1471,6 +1502,7 @@ configuration "default" {  }  ≓",    "~>0.18.3", // d2sqlite3 dependency version +  "~>7.2.0",  // arsd-official:cgi dependency version    "src/" ~ _cgi_search_script_raw_fn_d,    _cgi_search_script  ).strip; @@ -1485,24 +1517,26 @@ configuration "default" {          // Handle error        }      } -    { // get cgi.d -      import std.net.curl, std.stdio; -      char[] cgi_d; -      try { -        cgi_d = get!HTTP("https://raw.githubusercontent.com/adamdruppe/arsd/master/cgi.d"); -      } catch (ErrnoException ex) { -        // Handle error -        // CurlCode perform(ThrowOnError throwOnError = Yes.throwOnError); -        CurlCode perform(ThrowOnError throwOnError = No.throwOnError); -      } -      if (cgi_d && cgi_d.length > 0) { -        try { -          auto f = File(pth_sqlite_cgi.cgi_d_path_out, "w"); -          f.write(cgi_d); -        } catch (ErrnoException ex) { -          // Handle error -        } -      } -    } +    // { // get cgi.d +    //   // import std.net.curl, std.stdio; +    //   // char[] cgi_d; +    //   // if (opt_action.allow_downloads) { +    //   //   try { +    //   //     cgi_d = get!HTTP("https://raw.githubusercontent.com/adamdruppe/arsd/master/cgi.d"); +    //   //   } catch (ErrnoException ex) { +    //   //     // Handle error +    //   //     // CurlCode perform(ThrowOnError throwOnError = Yes.throwOnError); +    //   //     CurlCode perform(ThrowOnError throwOnError = No.throwOnError); +    //   //   } +    //   //   if (cgi_d && cgi_d.length > 0) { +    //   //     try { +    //   //       auto f = File(pth_sqlite_cgi.cgi_d_path_out, "w"); +    //   //       f.write(cgi_d); +    //   //     } catch (ErrnoException ex) { +    //   //       // Handle error +    //   //     } +    //   //   } +    //   // } +    // }    }  } diff --git a/src/doc_reform/io_out/hub.d b/src/doc_reform/io_out/hub.d index 2220d33..729b569 100644 --- a/src/doc_reform/io_out/hub.d +++ b/src/doc_reform/io_out/hub.d @@ -150,7 +150,12 @@ template outputHubOp() {          string _cgi_search_script_raw_fn_d = (opt_action.cgi_sqlite_search_filename_d.empty)            ? config.conf.w_srv_cgi_search_script_raw_fn_d            : opt_action.cgi_sqlite_search_filename_d; -        auto pth_sqlite_cgi = spinePathsSQLiteCGI!()(_cgi_search_script_raw_fn_d, _cgi_search_script, opt_action.output_dir_set); +        string _cgi_path = (opt_action.output_dir_set.length > 0) +        ? opt_action.output_dir_set +        : (config.conf.w_srv_data_root_path.length > 0) +          ? config.conf.w_srv_data_root_path +          : ""; +        auto pth_sqlite_cgi = spinePathsSQLiteCGI!()(_cgi_search_script_raw_fn_d, _cgi_search_script, _cgi_path);          writeln("sqlite cgi search form...");          writeln(" ", pth_sqlite_cgi.search_form_path_out);        } diff --git a/src/doc_reform/spine.d b/src/doc_reform/spine.d index 53a4a5a..6d6d543 100755 --- a/src/doc_reform/spine.d +++ b/src/doc_reform/spine.d @@ -181,7 +181,8 @@ string program_name = "spine";    ];    auto helpInfo = getopt(args,      std.getopt.config.passThrough, -    "abstraction",                "document abstraction ",                                          &opts["abstraction"], +    "abstraction",                "document abstraction",                                           &opts["abstraction"], +    "allow-downloads",            "allow downloads (includes cgi.d from github)",                   &opts["allow-downloads"],      "assert",                     "set optional assertions on",                                     &opts["assertions"],      "cgi-search-form-codegen",    "generates (pre-compiled) d code for search of specified db",     &opts["cgi-search-form-codegen"],      "cgi-sqlite-search-filename", "=[filename]",                                                    &settings["cgi-sqlite-search-filename"], @@ -252,6 +253,9 @@ string program_name = "spine";    }    enum outTask { source_or_pod, sqlite, sqlite_multi, latex, odt, epub, html_scroll, html_seg, html_stuff }    struct OptActions { +    @trusted bool allow_downloads() { +      return opts["allow-downloads"]; +    }      @trusted bool assertions() {        return opts["assertions"];      } | 
