|
|
@@ -18,24 +18,45 @@ In either case, options with an arity greater than one are not allowed.
|
|
|
Abbreviated options support key-value concatenation, such as how you can do `-I/path/to/my/include/dir` in gcc/clang.
|
|
|
|
|
|
#### Snippets
|
|
|
+Let's take a look at a partial example of how we might design the interfact for git using this tool.
|
|
|
|
|
|
-A partial git command arguments:
|
|
|
+For conveniece sake, we'll start with a helper object.
|
|
|
```c++
|
|
|
-struct Push : program::Arguments<Push> {
|
|
|
+struct GitBranch {
|
|
|
+ GitBranch(Git const *context); // Must be implemented later
|
|
|
+
|
|
|
+ std::string name() const;
|
|
|
+ std::string remote() const;
|
|
|
+ // ...more inspectors
|
|
|
+};
|
|
|
+
|
|
|
+template <typename CRTP> struct GitAction : program::Arguments<CRTP> {
|
|
|
+protected:
|
|
|
using Arguments::Arguments;
|
|
|
+ GitBranch branch() const { return GitBranch(parent<Git>()); }
|
|
|
+};
|
|
|
+```
|
|
|
+
|
|
|
+Now, we start do define some actions. For the sake of this example, we'll take a look only at push and pull.
|
|
|
+The commands git-push and git-pull have the interesting characteristic that if the current branch is a remote-tracking branch, then we can automatically pick out the the remote and push/pull it without specifying which remote OR which branch is selected.
|
|
|
+```c++
|
|
|
+struct Push : GitAction<Push> {
|
|
|
+ using GitAction::GitAction;
|
|
|
+
|
|
|
bool atomic = flag("atomic");
|
|
|
bool dry_run = flag("dry-run", 'n') = false;
|
|
|
bool no_verify = flag("no-verify", "skip push hooks") = false;
|
|
|
bool quiet = flag("quiet", 'q') = false;
|
|
|
bool verbose = flag("verbose", 'v') = false;
|
|
|
// ...more options
|
|
|
- // TODO: Fetch Upstream Implicitly
|
|
|
- std::string repository = argument(0, "repository") = "";
|
|
|
- std::vector<std::string> refspec = rest("refspec");
|
|
|
+
|
|
|
+ std::string repository = argument(0, "repository") = PROGRAM_DEFER(branch().remote());
|
|
|
+ std::vector<std::string> refspec = rest("refspec") = PROGRAM_DEFER(branch().name());
|
|
|
};
|
|
|
|
|
|
-struct Pull : program::Arguments<Pull> {
|
|
|
- using Arguments::Arguments;
|
|
|
+struct Pull : GitAction<Pull> {
|
|
|
+ using GitAction::GitAction;
|
|
|
+
|
|
|
bool quiet = flag("quiet", 'q') = false;
|
|
|
bool verbose = flag("verbose", 'v') = false;
|
|
|
bool commit = flag("commit", "immediately commit the merged result") = true;
|
|
|
@@ -43,30 +64,41 @@ struct Pull : program::Arguments<Pull> {
|
|
|
bool fastforward = flag("ff", "resolve the merge with a fast-forward if possible") = true;
|
|
|
bool only_ff = flag("ff-only", "fail if fast-forwarding will not succeed");
|
|
|
// ...more options
|
|
|
- // TODO: Fetch Upstream Implicitly
|
|
|
- std::string repository = argument(0, "repository") = "";
|
|
|
- std::vector<std::string> refspec = rest("refspec");
|
|
|
-};
|
|
|
|
|
|
-struct Commit : program::Arguments<Commit> {
|
|
|
- using Arguments::Arguments;
|
|
|
+ std::string repository = argument(0, "repository") = PROGRAM_DEFER(branch().remote());
|
|
|
+ std::vector<std::string> refspec = rest("refspec") = PROGRAM_DEFER(branch().name());
|
|
|
};
|
|
|
+```
|
|
|
|
|
|
+And finally we can define the actual Git command. The macro PROGRAM\_ARGS\_INVOKE creates a method invoke(), which iterates through all of the actions, and checks to see if they were set. If an action is set, then we will automagically recurse into its own invoke function, creating a chain of actions that will be passed into a TYPED\_MAIN function.
|
|
|
+```c++
|
|
|
struct Git : program::Arguments<Git> {
|
|
|
public:
|
|
|
- using Arguments::Arguments;
|
|
|
+ // Automatically fails if no action was input
|
|
|
+ PROGRAM_ARGS_INVOKE(commit, push, pull);
|
|
|
+
|
|
|
std::string pwd = option('C');
|
|
|
std::string git_dir = option("git-dir") = PROGRAM_DEFER(locate_git_dir(pwd));
|
|
|
std::map<std::string, std::string> config_env = option("config-env");
|
|
|
// ...more options
|
|
|
-private:
|
|
|
- friend int main(int, char const * const*);
|
|
|
- Commit commit = action("commit");
|
|
|
+
|
|
|
+private: // PROGRAM_ARGS_INVOKE is the only one who needs to access these
|
|
|
Push push = action("push");
|
|
|
Pull pull = action("pull");
|
|
|
};
|
|
|
+```
|
|
|
+
|
|
|
+For each series of "actions" that we support - we instantiate a typed\_main function. A combination of the PROGRAM\_ARGS\_MAIN macro and the PROGRAM\_ARGS\_INVOKE macro will automatically dispatch the correct action selected to the correct TYPED\_MAIN body.
|
|
|
+```c++
|
|
|
+TYPED_MAIN(Git const &git, Push const &push) {
|
|
|
+ ...
|
|
|
+}
|
|
|
+
|
|
|
+TYPED_MAIN(Git const &git, Pull const &pull) {
|
|
|
+ ...
|
|
|
+}
|
|
|
|
|
|
-TYPED_MAIN(Git)
|
|
|
+PROGRAM_ARGS_MAIN(Git)
|
|
|
```
|
|
|
|
|
|
Singular option storage cannot be repeated
|