You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

519 lines
20 KiB

2 years ago
  1. /*****************************************************************************
  2. *
  3. * CLIPP - command line interfaces for modern C++
  4. *
  5. * released under MIT license
  6. *
  7. * (c) 2017-2018 André Müller; foss@andremueller-online.de
  8. *
  9. *****************************************************************************/
  10. #include "testing.h"
  11. using namespace clipp;
  12. //-------------------------------------------------------------------
  13. void test(int lineNo, const doc_formatting& fmt,
  14. const group& cli, const std::string& expected)
  15. {
  16. auto doc = documentation(cli,fmt).str();
  17. // std::cout << "\n====================================\n\n" << doc << "\n";
  18. // return;
  19. if(doc != expected) {
  20. std::cout << "\nEXPECTED:\n" << expected << "\nOBSERVED:\n" << doc << '\n';
  21. throw std::runtime_error{"failed " + std::string( __FILE__ ) +
  22. " in line " + std::to_string(lineNo)};
  23. }
  24. }
  25. //-------------------------------------------------------------------
  26. int main()
  27. {
  28. try {
  29. auto fmt = doc_formatting{}
  30. .first_column(3)
  31. .doc_column(45)
  32. .last_column(80)
  33. .empty_label("?")
  34. .param_separator("%")
  35. .group_separator("#")
  36. .alternative_param_separator("|")
  37. .alternative_group_separator("^")
  38. .flag_separator("~")
  39. .surround_labels("<", ">")
  40. .surround_optional("[", "]")
  41. .surround_repeat(",,,", "...")
  42. .surround_alternatives("§(§", "§)§")
  43. .surround_alternative_flags("^(^", "^)^")
  44. .surround_group("(", ")")
  45. .surround_joinable("$($", "$)$")
  46. .max_flags_per_param_in_doc(2)
  47. .max_flags_per_param_in_usage(1)
  48. .line_spacing(1)
  49. .paragraph_spacing(3)
  50. .alternatives_min_split_size(3)
  51. .merge_alternative_flags_with_common_prefix(false)
  52. .merge_joinable_with_common_prefix(true)
  53. .split_alternatives(true);
  54. test(__LINE__, fmt, (
  55. (option("-o", "--out") & value("output file")) % "output filename",
  56. with_prefix("-f", option("align") >> []{} | option("noalign") >> []{} ) % "control alignment"
  57. ),
  58. " ^(^-o~--out^)^%<output file> output filename\n"
  59. "\n"
  60. " -falign|-fnoalign control alignment");
  61. test(__LINE__, fmt, (
  62. (option("-n", "--count") & value("count")) % "number of iterations",
  63. (option("-r", "--ratio") & value("ratio")) % "compression ratio",
  64. (option("-m") & opt_value("lines=5")) % "merge lines (default: 5)"
  65. ),
  66. " ^(^-n~--count^)^%<count> number of iterations\n"
  67. "\n"
  68. " ^(^-r~--ratio^)^%<ratio> compression ratio\n"
  69. "\n"
  70. " -m%<lines=5> merge lines (default: 5)");
  71. test(__LINE__, fmt, (
  72. "input files" % values("file"),
  73. "compress results" % option("-c", "--compress"),
  74. "lines to be ignored" % repeatable( option("-i", "--ignore") & integers("line") )
  75. ),
  76. " ,,,<file>... input files\n"
  77. "\n"
  78. " -c~--compress compress results\n"
  79. "\n"
  80. " ^(^-i~--ignore^)^%<line> lines to be ignored");
  81. test(__LINE__, fmt, (
  82. option("-r", "--recursive") % "recurse into subdirectories",
  83. (required("-i", "--in" ) & value("input dir")) % "sets path to input directory",
  84. (required("-o", "--out") & value("output dir")) % "sets path to output directory"
  85. ),
  86. " -r~--recursive recurse into subdirectories\n"
  87. "\n"
  88. " ^(^-i~--in^)^%<input dir> sets path to input directory\n"
  89. "\n"
  90. " ^(^-o~--out^)^%<output dir> sets path to output directory");
  91. test(__LINE__, fmt, (
  92. value("infile") % "input filename",
  93. value("outfile") % "output filename",
  94. option("-s", "--split") % "split files"
  95. ),
  96. " <infile> input filename\n"
  97. "\n"
  98. " <outfile> output filename\n"
  99. "\n"
  100. " -s~--split split files");
  101. test(__LINE__, fmt, (
  102. option("-a") % "activates a",
  103. option("-b") % "activates b",
  104. option("-c", "--noc") % "deactivates c",
  105. option("--hi")([]{}) % "says hi"
  106. ),
  107. " -a activates a\n"
  108. "\n"
  109. " -b activates b\n"
  110. "\n"
  111. " -c~--noc deactivates c\n"
  112. "\n"
  113. " --hi says hi");
  114. test(__LINE__, fmt, (
  115. command("make"),
  116. value("file") % "name of file to make",
  117. option("-f", "--force") % "overwrite existing file"
  118. ),
  119. " <file> name of file to make\n"
  120. "\n"
  121. " -f~--force overwrite existing file");
  122. test(__LINE__, fmt, (
  123. option("-a") % "activates a",
  124. option("-b") % "activates b",
  125. option("-c", "--noc") % "deactivates c",
  126. option("--hi")([]{}) % "says hi"
  127. ),
  128. " -a activates a\n"
  129. "\n"
  130. " -b activates b\n"
  131. "\n"
  132. " -c~--noc deactivates c\n"
  133. "\n"
  134. " --hi says hi");
  135. test(__LINE__, fmt, (
  136. ( command("ship"), ( ( command("new"), values("name") ) |
  137. ( value("name"),
  138. command("move"), value("x"), value("y"), (option("--speed=") & value("kn")) % "Speed in knots [default: 10]") |
  139. ( command("shoot"), value("x"), value("y") ) ) )
  140. | ( command("mine"),
  141. ( command("set" )
  142. | command("remove") ),
  143. value("x"), value("y"),
  144. ( option("--moored" ) % "Moored (anchored) mine."
  145. | option("--drifting") % "Drifting mine.")
  146. )
  147. | command("-h", "--help") % "Show this screen."
  148. | command("--version")([]{}) % "Show version."
  149. ),
  150. " --speed=%<kn> Speed in knots [default: 10]\n"
  151. "\n"
  152. " --moored Moored (anchored) mine.\n"
  153. "\n"
  154. " --drifting Drifting mine.\n"
  155. "\n"
  156. " -h~--help Show this screen.\n"
  157. "\n"
  158. " --version Show version.");
  159. test(__LINE__, fmt, ( command("help")
  160. | ( command("build"),
  161. ( command("new") | command("add") ),
  162. values("file"),
  163. option("-v", "--verbose") % "print detailed report",
  164. option("-b", "--buffer") & opt_value(
  165. "size=10") % "sets buffer size in KiByte",
  166. ( option("--init") | option("--no-init") ) % "do or don't initialize"
  167. )
  168. | ( command("query"), value("infile"),
  169. required("-o", "--out") & value("outfile"),
  170. option("-f", "--out-format") % "determine output format"
  171. & value("format")
  172. )
  173. ),
  174. " -v~--verbose print detailed report\n"
  175. "\n"
  176. " <size=10> sets buffer size in KiByte\n"
  177. "\n"
  178. " --init|--no-init do or don't initialize\n"
  179. "\n"
  180. " -f~--out-format determine output format");
  181. test(__LINE__, fmt, (
  182. value("file"),
  183. joinable(
  184. option("-r") % "open read-only",
  185. option("-b") % "use backup file",
  186. option("-s") % "use swap file"
  187. ),
  188. joinable(
  189. option(":vim") >> []{}, option(":st3") >> []{},
  190. option(":atom") >> []{}, option(":emacs") >> []{}
  191. ) % "editor(s) to use; multiple possible"
  192. ),
  193. " -r open read-only\n"
  194. "\n"
  195. " -b use backup file\n"
  196. "\n"
  197. " -s use swap file\n"
  198. "\n"
  199. " :vim%:st3%:atom%:emacs editor(s) to use; multiple possible");
  200. test(__LINE__, fmt, (
  201. (option("x") % "sets X", option("y") % "sets Y"),
  202. (option("a") % "activates A", option("b") % "activates B") % "documented group 1:" ,
  203. "documented group 2:" % (option("-g") % "activates G", option("-h") % "activates H"),
  204. "activates E or F" % (option("-e"), option("-f"))
  205. ),
  206. " x sets X\n"
  207. "\n"
  208. " y sets Y\n"
  209. "\n"
  210. "\n"
  211. "\n"
  212. " documented group 1:\n\n"
  213. " a activates A\n"
  214. "\n"
  215. " b activates B\n"
  216. "\n"
  217. "\n"
  218. "\n"
  219. " documented group 2:\n\n"
  220. " -g activates G\n"
  221. "\n"
  222. " -h activates H\n"
  223. "\n"
  224. "\n"
  225. "\n"
  226. " -e%-f activates E or F");
  227. test(__LINE__, fmt, ( ( command("make"), value("wordfile"), required("-dict") & value("dictionary"), option("--progress", "-p")) | (
  228. command("find"), values("infile"), required("-dict") & value("dictionary"),
  229. (option("-o", "--output") & value("outfile")) % "write to file instead of stdout",
  230. ( option("-split" ) | option("-nosplit"))) | command("help"),
  231. option("-v", "--version").call([]{}) % "show version"
  232. ),
  233. " ^(^-o~--output^)^%<outfile> write to file instead of stdout\n"
  234. "\n"
  235. " -v~--version show version");
  236. test(__LINE__, fmt, (
  237. command("help") |
  238. ( command("build"),
  239. "build commands" %
  240. ( command("new") % "make new database"
  241. | command("add") % "append to existing database"
  242. ),
  243. value("file")
  244. ) |
  245. ( command("query"),
  246. "query settings" %
  247. ( required("-i", "--input") & value("infile") % "input file",
  248. option("-p", "--pretty-print") % "human friendly output")
  249. ) |
  250. ( command("info"),
  251. "database info modes" % (
  252. command("space") % "detailed memory occupation analysis" |
  253. (
  254. command("statistics"),
  255. "statistics analysis" % (
  256. command("words") % "word frequency table" |
  257. command("chars") % "character frequency table"
  258. )
  259. )
  260. )
  261. ) |
  262. "remove mode" % (
  263. command("remove"),
  264. "modify" % ( command("any") | command("all") ),
  265. value("regex") % "regular expression filter"
  266. ) |
  267. ( command("modify"),
  268. "modification opererations" % (
  269. option("-c", "--compress") % "compress database in-memory",
  270. option("-u", "--unique") % "keep only unique entries",
  271. option("-m", "--memlimit") % "max. size in RAM" & value("size")
  272. )
  273. )
  274. ),
  275. " build commands\n\n"
  276. " new make new database\n"
  277. "\n"
  278. " add append to existing database\n"
  279. "\n"
  280. "\n"
  281. "\n"
  282. " query settings\n\n"
  283. " <infile> input file\n"
  284. "\n"
  285. " -p~--pretty-print human friendly output\n"
  286. "\n"
  287. "\n"
  288. "\n"
  289. " database info modes\n\n"
  290. " space detailed memory occupation analysis\n"
  291. "\n"
  292. "\n"
  293. "\n"
  294. " statistics analysis\n\n"
  295. " words word frequency table\n"
  296. "\n"
  297. " chars character frequency table\n"
  298. "\n"
  299. "\n"
  300. "\n"
  301. " remove mode\n\n"
  302. " any|all modify\n"
  303. "\n"
  304. " <regex> regular expression filter\n"
  305. "\n"
  306. "\n"
  307. "\n"
  308. " modification opererations\n\n"
  309. " -c~--compress compress database in-memory\n"
  310. "\n"
  311. " -u~--unique keep only unique entries\n"
  312. "\n"
  313. " -m~--memlimit max. size in RAM");
  314. test(__LINE__, fmt, (
  315. command("new"),
  316. value("filename"),
  317. (option("-e", "--encoding") & value("enc")).doc("'utf8' or 'cp1252'")
  318. ),
  319. " ^(^-e~--encoding^)^%<enc> 'utf8' or 'cp1252'");
  320. test(__LINE__, fmt, (
  321. values("file") % "input filenames",
  322. (required("-s") & value("expr")) % "string to look for",
  323. option("any") % "report as soon as any matches" |
  324. option("all") % "report only if all match"
  325. ),
  326. " ,,,<file>... input filenames\n"
  327. "\n"
  328. " -s%<expr> string to look for\n"
  329. "\n"
  330. " any report as soon as any matches\n"
  331. "\n"
  332. " all report only if all match");
  333. auto complexcli = (
  334. "user interface options:" % (
  335. option("-v", "--verbose") % "show detailed output",
  336. option("-i", "--interactive") % "use interactive mode"
  337. ),
  338. "copy mode:" % (
  339. command("copy") | command("move"),
  340. option("--all") % "copy all",
  341. option("--replace") % "replace existing files",
  342. option("-f", "--force") % "don't ask for confirmation"
  343. ) |
  344. "compare mode:" % (
  345. command("compare"),
  346. (command("date") | command("content")),
  347. option("-b", "--binary") % "compare files byte by byte",
  348. option("-q", "--quick") % "use heuristics for faster comparison"
  349. ) |
  350. "merge mode:" % (
  351. command("merge"),
  352. (
  353. command("diff") % "merge using diff" |
  354. command("patch") % "merge using patch" |
  355. ( command("content") % "merge based on content",
  356. "content based merge options:" % (
  357. option("--git-style") % "emulate git's merge behavior",
  358. option("-m", "--marker") & value("marker") % "merge marker symbol"
  359. )
  360. )
  361. ),
  362. required("-o") & value("outdir") % "target directory for merge result",
  363. option("--show-conflicts") % "show merge conflicts during run"
  364. ) |
  365. command("list"),
  366. "mode-independent options:" % (
  367. values("files") % "input files",
  368. option("-r", "--recursive") % "descend into subdirectories",
  369. option("-h", "--help") % "show help"
  370. )
  371. );
  372. test(__LINE__, fmt, complexcli,
  373. " user interface options:\n\n"
  374. " -v~--verbose show detailed output\n"
  375. "\n"
  376. " -i~--interactive use interactive mode\n"
  377. "\n"
  378. "\n"
  379. "\n"
  380. " copy mode:\n\n"
  381. " --all copy all\n"
  382. "\n"
  383. " --replace replace existing files\n"
  384. "\n"
  385. " -f~--force don't ask for confirmation\n"
  386. "\n"
  387. "\n"
  388. "\n"
  389. " compare mode:\n\n"
  390. " -b~--binary compare files byte by byte\n"
  391. "\n"
  392. " -q~--quick use heuristics for faster\n"
  393. " comparison\n"
  394. "\n"
  395. "\n"
  396. "\n"
  397. " merge mode:\n\n"
  398. " diff merge using diff\n"
  399. "\n"
  400. " patch merge using patch\n"
  401. "\n"
  402. " content merge based on content\n"
  403. "\n"
  404. "\n"
  405. "\n"
  406. " content based merge options:\n\n"
  407. " --git-style emulate git's merge behavior\n"
  408. "\n"
  409. " <marker> merge marker symbol\n"
  410. "\n"
  411. "\n"
  412. "\n"
  413. " <outdir> target directory for merge result\n"
  414. "\n"
  415. " --show-conflicts show merge conflicts during run\n"
  416. "\n"
  417. "\n"
  418. "\n"
  419. " mode-independent options:\n\n"
  420. " ,,,<files>... input files\n"
  421. "\n"
  422. " -r~--recursive descend into subdirectories\n"
  423. "\n"
  424. " -h~--help show help");
  425. fmt.first_column(7) .doc_column(20) .last_column(50);
  426. test(__LINE__, fmt, complexcli,
  427. " user interface options:\n\n"
  428. " -v~--verbose\n"
  429. " show detailed output\n\n\n\n"
  430. " -i~--interactive\n"
  431. " use interactive mode\n\n\n\n"
  432. " copy mode:\n\n"
  433. " --all copy all\n\n"
  434. " --replace\n"
  435. " replace existing files\n\n\n\n"
  436. " -f~--force\n"
  437. " don't ask for confirmation\n\n\n\n"
  438. " compare mode:\n\n"
  439. " -b~--binary\n"
  440. " compare files byte by byte\n\n\n\n"
  441. " -q~--quick\n"
  442. " use heuristics for faster\n"
  443. " comparison\n\n\n\n"
  444. " merge mode:\n\n"
  445. " diff merge using diff\n\n"
  446. " patch merge using patch\n\n"
  447. " content merge based on content\n\n\n\n"
  448. " content based merge options:\n\n"
  449. " --git-style\n"
  450. " emulate git's merge behavior\n\n\n\n"
  451. " <marker>\n"
  452. " merge marker symbol\n\n\n\n"
  453. " <outdir> target directory for merge\n"
  454. " result\n\n\n\n"
  455. " --show-conflicts\n"
  456. " show merge conflicts during\n"
  457. " run\n\n\n\n"
  458. " mode-independent options:\n\n"
  459. " ,,,<files>...\n"
  460. " input files\n\n\n\n"
  461. " -r~--recursive\n"
  462. " descend into subdirectories\n\n\n\n"
  463. " -h~--help\n"
  464. " show help");
  465. }
  466. catch(std::exception& e) {
  467. std::cerr << e.what() << std::endl;
  468. return 1;
  469. }
  470. }