All Classes Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
formatter.hpp
1 // The MIT License (MIT)
2 
3 // Copyright (c) 2012-2014 Danny Y., Rapptz
4 
5 // Permission is hereby granted, free of charge, to any person obtaining a copy of
6 // this software and associated documentation files (the "Software"), to deal in
7 // the Software without restriction, including without limitation the rights to
8 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 // the Software, and to permit persons to whom the Software is furnished to do so,
10 // subject to the following conditions:
11 
12 // The above copyright notice and this permission notice shall be included in all
13 // copies or substantial portions of the Software.
14 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 
22 #ifndef GEARS_OPTPARSE_FORMATTER_HPP
23 #define GEARS_OPTPARSE_FORMATTER_HPP
24 
25 #include "subcommand.hpp"
26 
27 namespace gears {
28 namespace optparse {
37 struct formatter {
38  size_t column;
39 
43  formatter(size_t column = 80): column(column) {}
44 
48  virtual ~formatter() = default;
49 
61  virtual std::string wrap(const std::string& str, size_t indent = 0) {
62  if(str.empty()) {
63  return str;
64  }
65 
66  size_t c = column - 2;
67  std::string result;
68  std::string indentation(indent, ' ');
69 
70  if(str.size() + indent > c) {
71  std::istringstream in(str);
72  std::string word;
73  if(in >> word) {
74  result.append(word);
75  size_t spaces = c - word.size() - indent;
76  while(in >> word) {
77  if(spaces < word.size() + 1) {
78  result.append(1, '\n').append(indent, ' ').append(word);
79  spaces = c - word.size() - indent;
80  }
81  else {
82  result.append(1, ' ').append(word);
83  spaces -= word.size() + 1;
84  }
85  }
86  }
87  result.push_back('\n');
88  }
89  else {
90  result.append(str).push_back('\n');
91  }
92 
93  return result;
94  }
95 
106  virtual std::string usage(const std::string& name, const std::string& command, const std::string& str) {
107  std::string result = "usage: " + name;
108  if(!command.empty()) {
109  result.push_back(' ');
110  result += command;
111  }
112  result.push_back(' ');
113  result += str;
114  return wrap(result);
115  }
116 
128  virtual std::string description(const std::string& str) {
129  auto&& desc = wrap(str);
130  if(!str.empty()) {
131  desc.push_back('\n');
132  }
133  return desc;
134  }
135 
146  virtual std::string epilogue(const std::string& str) {
147  return wrap(str);
148  }
149 
160  virtual std::string subcommands(const std::vector<subcommand>& subs) {
161  if(subs.empty()) {
162  return "";
163  }
164 
165 
166  std::string out = "subcommands\n";
167 
168  auto&& max = std::max_element(subs.begin(), subs.end(), [](const subcommand& lhs, const subcommand& rhs) {
169  return lhs.name < rhs.name;
170  });
171 
172  size_t indent = 20u;
173  if(max != subs.end()) {
174  indent += max->name.size();
175  }
176 
177  for(auto&& i : subs) {
178  out.append(4, ' ').append(i.name);
179  size_t current = indent - 4 - i.name.size();
180  out.append(current, ' ');
181  if(!i.help.empty()) {
182  out.append(wrap(i.help, current));
183  }
184  else {
185  out.push_back('\n');
186  }
187  }
188 
189  return out;
190  }
191 
214  virtual std::string options(const option_set& opts) {
215  if(opts.empty()) {
216  return "";
217  }
218 
219  std::string result = "options:\n";
220 
221  auto&& max_name = std::max_element(opts.begin(), opts.end(), [](const option& lhs, const option& rhs) {
222  return lhs.name < rhs.name;
223  });
224 
225  auto&& max_meta = std::max_element(opts.begin(), opts.end(), [](const option& lhs, const option& rhs) {
226  return lhs.metavar() < rhs.metavar();
227  });
228 
229  size_t max_indent = 14u;
230 
231  if(max_name != opts.end()) {
232  max_indent += max_name->name.size();
233  }
234 
235  if(max_meta != opts.end()) {
236  max_indent += 5u + max_meta->metavar().size();
237  }
238 
239  for(auto&& opt : opts) {
240  if((opt.flags & trait::hidden) == trait::hidden) {
241  continue;
242  }
243 
244  size_t indent = max_indent;
245  const bool has_long_option = !opt.name.empty();
246  const bool has_short_option = opt.alias != '\0';
247  const std::string metavar = opt.metavar();
248  const bool has_metavar = !metavar.empty();
249 
250  if(has_long_option && has_short_option) {
251  result.append(4, ' ').append(1, '-').append(1, opt.alias).append(", --").append(opt.name);
252  indent -= 10u + opt.name.size();
253  }
254  else if(has_long_option) {
255  result.append(4, ' ').append(" --").append(opt.name);
256  indent -= 10u + opt.name.size();
257  }
258  else if(has_short_option) {
259  result.append(4, ' ').append(1, '-').append(1, opt.alias);
260  indent -= 6u;
261  }
262 
263  if(has_metavar) {
264  if(has_long_option) {
265  result.append("[=<").append(metavar).append(">]");
266  indent -= 5u + metavar.size();
267  }
268  else {
269  result.append(1, ' ').append(1, '<').append(metavar).append(1, '>');
270  indent -= 3u + metavar.size();
271  }
272  }
273 
274  result.append(indent, ' ');
275 
276  if(opt.help.empty()) {
277  result.push_back('\n');
278  continue;
279  }
280 
281  result.append(wrap(opt.help, indent));
282  }
283 
284  return result;
285  }
286 };
287 } // optparse
288 } // gears
289 
290 #endif // GEARS_OPTPARSE_FORMATTER_HPP