1 #include "Flag.h"
2 #include "StringPiece.h"
3 
4 #include <functional>
5 #include <iomanip>
6 #include <iostream>
7 #include <string>
8 #include <vector>
9 
10 namespace aapt {
11 namespace flag {
12 
13 struct Flag {
14     std::string name;
15     std::string description;
16     std::function<bool(const StringPiece&, std::string*)> action;
17     bool required;
18     bool* flagResult;
19     bool flagValueWhenSet;
20     bool parsed;
21 };
22 
23 static std::vector<Flag> sFlags;
24 static std::vector<std::string> sArgs;
25 
wrap(const std::function<void (const StringPiece &)> & action)26 static std::function<bool(const StringPiece&, std::string*)> wrap(
27         const std::function<void(const StringPiece&)>& action) {
28     return [action](const StringPiece& arg, std::string*) -> bool {
29         action(arg);
30         return true;
31     };
32 }
33 
optionalFlag(const StringPiece & name,const StringPiece & description,std::function<void (const StringPiece &)> action)34 void optionalFlag(const StringPiece& name, const StringPiece& description,
35                   std::function<void(const StringPiece&)> action) {
36     sFlags.push_back(Flag{
37             name.toString(), description.toString(), wrap(action),
38             false, nullptr, false, false });
39 }
40 
requiredFlag(const StringPiece & name,const StringPiece & description,std::function<void (const StringPiece &)> action)41 void requiredFlag(const StringPiece& name, const StringPiece& description,
42                   std::function<void(const StringPiece&)> action) {
43     sFlags.push_back(Flag{ name.toString(), description.toString(), wrap(action),
44             true, nullptr, false, false });
45 }
46 
requiredFlag(const StringPiece & name,const StringPiece & description,std::function<bool (const StringPiece &,std::string *)> action)47 void requiredFlag(const StringPiece& name, const StringPiece& description,
48                   std::function<bool(const StringPiece&, std::string*)> action) {
49     sFlags.push_back(Flag{ name.toString(), description.toString(), action,
50             true, nullptr, false, false });
51 }
52 
optionalSwitch(const StringPiece & name,const StringPiece & description,bool resultWhenSet,bool * result)53 void optionalSwitch(const StringPiece& name, const StringPiece& description, bool resultWhenSet,
54                     bool* result) {
55     sFlags.push_back(Flag{
56             name.toString(), description.toString(), {},
57             false, result, resultWhenSet, false });
58 }
59 
usageAndDie(const StringPiece & command)60 void usageAndDie(const StringPiece& command) {
61     std::cerr << command << " [options]";
62     for (const Flag& flag : sFlags) {
63         if (flag.required) {
64             std::cerr << " " << flag.name << " arg";
65         }
66     }
67     std::cerr << " files..." << std::endl << std::endl << "Options:" << std::endl;
68 
69     for (const Flag& flag : sFlags) {
70         std::string command = flag.name;
71         if (!flag.flagResult) {
72             command += " arg ";
73         }
74         std::cerr << "  " << std::setw(30) << std::left << command
75                   << flag.description << std::endl;
76     }
77     exit(1);
78 }
79 
parse(int argc,char ** argv,const StringPiece & command)80 void parse(int argc, char** argv, const StringPiece& command) {
81     std::string errorStr;
82     for (int i = 0; i < argc; i++) {
83         const StringPiece arg(argv[i]);
84         if (*arg.data() != '-') {
85             sArgs.push_back(arg.toString());
86             continue;
87         }
88 
89         bool match = false;
90         for (Flag& flag : sFlags) {
91             if (arg == flag.name) {
92                 match = true;
93                 flag.parsed = true;
94                 if (flag.flagResult) {
95                     *flag.flagResult = flag.flagValueWhenSet;
96                 } else {
97                     i++;
98                     if (i >= argc) {
99                         std::cerr << flag.name << " missing argument." << std::endl
100                                   << std::endl;
101                         usageAndDie(command);
102                     }
103 
104                     if (!flag.action(argv[i], &errorStr)) {
105                         std::cerr << errorStr << "." << std::endl << std::endl;
106                         usageAndDie(command);
107                     }
108                 }
109                 break;
110             }
111         }
112 
113         if (!match) {
114             std::cerr << "unknown option '" << arg << "'." << std::endl << std::endl;
115             usageAndDie(command);
116         }
117     }
118 
119     for (const Flag& flag : sFlags) {
120         if (flag.required && !flag.parsed) {
121             std::cerr << "missing required flag " << flag.name << std::endl << std::endl;
122             usageAndDie(command);
123         }
124     }
125 }
126 
getArgs()127 const std::vector<std::string>& getArgs() {
128     return sArgs;
129 }
130 
131 } // namespace flag
132 } // namespace aapt
133