annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/include/kj/main.h @ 69:33d812a61356

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 17:55:14 -0400
parents
children
rev   line source
jpayne@69 1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
jpayne@69 2 // Licensed under the MIT License:
jpayne@69 3 //
jpayne@69 4 // Permission is hereby granted, free of charge, to any person obtaining a copy
jpayne@69 5 // of this software and associated documentation files (the "Software"), to deal
jpayne@69 6 // in the Software without restriction, including without limitation the rights
jpayne@69 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
jpayne@69 8 // copies of the Software, and to permit persons to whom the Software is
jpayne@69 9 // furnished to do so, subject to the following conditions:
jpayne@69 10 //
jpayne@69 11 // The above copyright notice and this permission notice shall be included in
jpayne@69 12 // all copies or substantial portions of the Software.
jpayne@69 13 //
jpayne@69 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
jpayne@69 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
jpayne@69 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
jpayne@69 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
jpayne@69 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
jpayne@69 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
jpayne@69 20 // THE SOFTWARE.
jpayne@69 21
jpayne@69 22 #pragma once
jpayne@69 23
jpayne@69 24 #include "array.h"
jpayne@69 25 #include "string.h"
jpayne@69 26 #include "vector.h"
jpayne@69 27 #include "function.h"
jpayne@69 28
jpayne@69 29 KJ_BEGIN_HEADER
jpayne@69 30
jpayne@69 31 namespace kj {
jpayne@69 32
jpayne@69 33 class ProcessContext {
jpayne@69 34 // Context for command-line programs.
jpayne@69 35
jpayne@69 36 public:
jpayne@69 37 virtual StringPtr getProgramName() = 0;
jpayne@69 38 // Get argv[0] as passed to main().
jpayne@69 39
jpayne@69 40 KJ_NORETURN(virtual void exit()) = 0;
jpayne@69 41 // Indicates program completion. The program is considered successful unless `error()` was
jpayne@69 42 // called. Typically this exits with _Exit(), meaning that the stack is not unwound, buffers
jpayne@69 43 // are not flushed, etc. -- it is the responsibility of the caller to flush any buffers that
jpayne@69 44 // matter. However, an alternate context implementation e.g. for unit testing purposes could
jpayne@69 45 // choose to throw an exception instead.
jpayne@69 46 //
jpayne@69 47 // At first this approach may sound crazy. Isn't it much better to shut down cleanly? What if
jpayne@69 48 // you lose data? However, it turns out that if you look at each common class of program, _Exit()
jpayne@69 49 // is almost always preferable. Let's break it down:
jpayne@69 50 //
jpayne@69 51 // * Commands: A typical program you might run from the command line is single-threaded and
jpayne@69 52 // exits quickly and deterministically. Commands often use buffered I/O and need to flush
jpayne@69 53 // those buffers before exit. However, most of the work performed by destructors is not
jpayne@69 54 // flushing buffers, but rather freeing up memory, placing objects into freelists, and closing
jpayne@69 55 // file descriptors. All of this is irrelevant if the process is about to exit anyway, and
jpayne@69 56 // for a command that runs quickly, time wasted freeing heap space may make a real difference
jpayne@69 57 // in the overall runtime of a script. Meanwhile, it is usually easy to determine exactly what
jpayne@69 58 // resources need to be flushed before exit, and easy to tell if they are not being flushed
jpayne@69 59 // (because the command fails to produce the expected output). Therefore, it is reasonably
jpayne@69 60 // easy for commands to explicitly ensure all output is flushed before exiting, and it is
jpayne@69 61 // probably a good idea for them to do so anyway, because write failures should be detected
jpayne@69 62 // and handled. For commands, a good strategy is to allocate any objects that require clean
jpayne@69 63 // destruction on the stack, and allow them to go out of scope before the command exits.
jpayne@69 64 // Meanwhile, any resources which do not need to be cleaned up should be allocated as members
jpayne@69 65 // of the command's main class, whose destructor normally will not be called.
jpayne@69 66 //
jpayne@69 67 // * Interactive apps: Programs that interact with the user (whether they be graphical apps
jpayne@69 68 // with windows or console-based apps like emacs) generally exit only when the user asks them
jpayne@69 69 // to. Such applications may store large data structures in memory which need to be synced
jpayne@69 70 // to disk, such as documents or user preferences. However, relying on stack unwind or global
jpayne@69 71 // destructors as the mechanism for ensuring such syncing occurs is probably wrong. First of
jpayne@69 72 // all, it's 2013, and applications ought to be actively syncing changes to non-volatile
jpayne@69 73 // storage the moment those changes are made. Applications can crash at any time and a crash
jpayne@69 74 // should never lose data that is more than half a second old. Meanwhile, if a user actually
jpayne@69 75 // does try to close an application while unsaved changes exist, the application UI should
jpayne@69 76 // prompt the user to decide what to do. Such a UI mechanism is obviously too high level to
jpayne@69 77 // be implemented via destructors, so KJ's use of _Exit() shouldn't make a difference here.
jpayne@69 78 //
jpayne@69 79 // * Servers: A good server is fault-tolerant, prepared for the possibility that at any time
jpayne@69 80 // it could crash, the OS could decide to kill it off, or the machine it is running on could
jpayne@69 81 // just die. So, using _Exit() should be no problem. In fact, servers generally never even
jpayne@69 82 // call exit anyway; they are killed externally.
jpayne@69 83 //
jpayne@69 84 // * Batch jobs: A long-running batch job is something between a command and a server. It
jpayne@69 85 // probably knows exactly what needs to be flushed before exiting, and it probably should be
jpayne@69 86 // fault-tolerant.
jpayne@69 87 //
jpayne@69 88 // Meanwhile, regardless of program type, if you are adhering to KJ style, then the use of
jpayne@69 89 // _Exit() shouldn't be a problem anyway:
jpayne@69 90 //
jpayne@69 91 // * KJ style forbids global mutable state (singletons) in general and global constructors and
jpayne@69 92 // destructors in particular. Therefore, everything that could possibly need cleanup either
jpayne@69 93 // lives on the stack or is transitively owned by something living on the stack.
jpayne@69 94 //
jpayne@69 95 // * Calling exit() simply means "Don't clean up anything older than this stack frame.". If you
jpayne@69 96 // have resources that require cleanup before exit, make sure they are owned by stack frames
jpayne@69 97 // beyond the one that eventually calls exit(). To be as safe as possible, don't place any
jpayne@69 98 // state in your program's main class, and don't call exit() yourself. Then, runMainAndExit()
jpayne@69 99 // will do it, and the only thing on the stack at that time will be your main class, which
jpayne@69 100 // has no state anyway.
jpayne@69 101 //
jpayne@69 102 // TODO(someday): Perhaps we should use the new std::quick_exit(), so that at_quick_exit() is
jpayne@69 103 // available for those who really think they need it. Unfortunately, it is not yet available
jpayne@69 104 // on many platforms.
jpayne@69 105
jpayne@69 106 virtual void warning(StringPtr message) = 0;
jpayne@69 107 // Print the given message to standard error. A newline is printed after the message if it
jpayne@69 108 // doesn't already have one.
jpayne@69 109
jpayne@69 110 virtual void error(StringPtr message) = 0;
jpayne@69 111 // Like `warning()`, but also sets a flag indicating that the process has failed, and that when
jpayne@69 112 // it eventually exits it should indicate an error status.
jpayne@69 113
jpayne@69 114 KJ_NORETURN(virtual void exitError(StringPtr message)) = 0;
jpayne@69 115 // Equivalent to `error(message)` followed by `exit()`.
jpayne@69 116
jpayne@69 117 KJ_NORETURN(virtual void exitInfo(StringPtr message)) = 0;
jpayne@69 118 // Displays the given non-error message to the user and then calls `exit()`. This is used to
jpayne@69 119 // implement things like --help.
jpayne@69 120
jpayne@69 121 virtual void increaseLoggingVerbosity() = 0;
jpayne@69 122 // Increase the level of detail produced by the debug logging system. `MainBuilder` invokes
jpayne@69 123 // this if the caller uses the -v flag.
jpayne@69 124
jpayne@69 125 // TODO(someday): Add interfaces representing standard OS resources like the filesystem, so that
jpayne@69 126 // these things can be mocked out.
jpayne@69 127 };
jpayne@69 128
jpayne@69 129 class TopLevelProcessContext final: public ProcessContext {
jpayne@69 130 // A ProcessContext implementation appropriate for use at the actual entry point of a process
jpayne@69 131 // (as opposed to when you are trying to call a program's main function from within some other
jpayne@69 132 // program). This implementation writes errors to stderr, and its `exit()` method actually
jpayne@69 133 // calls the C `quick_exit()` function.
jpayne@69 134
jpayne@69 135 public:
jpayne@69 136 explicit TopLevelProcessContext(StringPtr programName);
jpayne@69 137
jpayne@69 138 struct CleanShutdownException { int exitCode; };
jpayne@69 139 // If the environment variable KJ_CLEAN_SHUTDOWN is set, then exit() will actually throw this
jpayne@69 140 // exception rather than exiting. `kj::runMain()` catches this exception and returns normally.
jpayne@69 141 // This is useful primarily for testing purposes, to assist tools like memory leak checkers that
jpayne@69 142 // are easily confused by quick_exit().
jpayne@69 143
jpayne@69 144 StringPtr getProgramName() override;
jpayne@69 145 KJ_NORETURN(void exit() override);
jpayne@69 146 void warning(StringPtr message) override;
jpayne@69 147 void error(StringPtr message) override;
jpayne@69 148 KJ_NORETURN(void exitError(StringPtr message) override);
jpayne@69 149 KJ_NORETURN(void exitInfo(StringPtr message) override);
jpayne@69 150 void increaseLoggingVerbosity() override;
jpayne@69 151
jpayne@69 152 private:
jpayne@69 153 StringPtr programName;
jpayne@69 154 bool cleanShutdown;
jpayne@69 155 bool hadErrors = false;
jpayne@69 156 };
jpayne@69 157
jpayne@69 158 typedef Function<void(StringPtr programName, ArrayPtr<const StringPtr> params)> MainFunc;
jpayne@69 159
jpayne@69 160 int runMainAndExit(ProcessContext& context, MainFunc&& func, int argc, char* argv[]);
jpayne@69 161 // Runs the given main function and then exits using the given context. If an exception is thrown,
jpayne@69 162 // this will catch it, report it via the context and exit with an error code.
jpayne@69 163 //
jpayne@69 164 // Normally this function does not return, because returning would probably lead to wasting time
jpayne@69 165 // on cleanup when the process is just going to exit anyway. However, to facilitate memory leak
jpayne@69 166 // checkers and other tools that require a clean shutdown to do their job, if the environment
jpayne@69 167 // variable KJ_CLEAN_SHUTDOWN is set, the function will in fact return an exit code, which should
jpayne@69 168 // then be returned from main().
jpayne@69 169 //
jpayne@69 170 // Most users will use the KJ_MAIN() macro rather than call this function directly.
jpayne@69 171
jpayne@69 172 #define KJ_MAIN(MainClass) \
jpayne@69 173 int main(int argc, char* argv[]) { \
jpayne@69 174 ::kj::TopLevelProcessContext context(argv[0]); \
jpayne@69 175 MainClass mainObject(context); \
jpayne@69 176 return ::kj::runMainAndExit(context, mainObject.getMain(), argc, argv); \
jpayne@69 177 }
jpayne@69 178 // Convenience macro for declaring a main function based on the given class. The class must have
jpayne@69 179 // a constructor that accepts a ProcessContext& and a method getMain() which returns
jpayne@69 180 // kj::MainFunc (probably building it using a MainBuilder).
jpayne@69 181
jpayne@69 182 class MainBuilder {
jpayne@69 183 // Builds a main() function with nice argument parsing. As options and arguments are parsed,
jpayne@69 184 // corresponding callbacks are called, so that you never have to write a massive switch()
jpayne@69 185 // statement to interpret arguments. Additionally, this approach encourages you to write
jpayne@69 186 // main classes that have a reasonable API that can be used as an alternative to their
jpayne@69 187 // command-line interface.
jpayne@69 188 //
jpayne@69 189 // All StringPtrs passed to MainBuilder must remain valid until option parsing completes. The
jpayne@69 190 // assumption is that these strings will all be literals, making this an easy requirement. If
jpayne@69 191 // not, consider allocating them in an Arena.
jpayne@69 192 //
jpayne@69 193 // Some flags are automatically recognized by the main functions built by this class:
jpayne@69 194 // --help: Prints help text and exits. The help text is constructed based on the
jpayne@69 195 // information you provide to the builder as you define each flag.
jpayne@69 196 // --verbose: Increase logging verbosity.
jpayne@69 197 // --version: Print version information and exit.
jpayne@69 198 //
jpayne@69 199 // Example usage:
jpayne@69 200 //
jpayne@69 201 // class FooMain {
jpayne@69 202 // public:
jpayne@69 203 // FooMain(kj::ProcessContext& context): context(context) {}
jpayne@69 204 //
jpayne@69 205 // bool setAll() { all = true; return true; }
jpayne@69 206 // // Enable the --all flag.
jpayne@69 207 //
jpayne@69 208 // kj::MainBuilder::Validity setOutput(kj::StringPtr name) {
jpayne@69 209 // // Set the output file.
jpayne@69 210 //
jpayne@69 211 // if (name.endsWith(".foo")) {
jpayne@69 212 // outputFile = name;
jpayne@69 213 // return true;
jpayne@69 214 // } else {
jpayne@69 215 // return "Output file must have extension .foo.";
jpayne@69 216 // }
jpayne@69 217 // }
jpayne@69 218 //
jpayne@69 219 // kj::MainBuilder::Validity processInput(kj::StringPtr name) {
jpayne@69 220 // // Process an input file.
jpayne@69 221 //
jpayne@69 222 // if (!exists(name)) {
jpayne@69 223 // return kj::str(name, ": file not found");
jpayne@69 224 // }
jpayne@69 225 // // ... process the input file ...
jpayne@69 226 // return true;
jpayne@69 227 // }
jpayne@69 228 //
jpayne@69 229 // kj::MainFunc getMain() {
jpayne@69 230 // return MainBuilder(context, "Foo Builder v1.5", "Reads <source>s and builds a Foo.")
jpayne@69 231 // .addOption({'a', "all"}, KJ_BIND_METHOD(*this, setAll),
jpayne@69 232 // "Frob all the widgets. Otherwise, only some widgets are frobbed.")
jpayne@69 233 // .addOptionWithArg({'o', "output"}, KJ_BIND_METHOD(*this, setOutput),
jpayne@69 234 // "<filename>", "Output to <filename>. Must be a .foo file.")
jpayne@69 235 // .expectOneOrMoreArgs("<source>", KJ_BIND_METHOD(*this, processInput))
jpayne@69 236 // .build();
jpayne@69 237 // }
jpayne@69 238 //
jpayne@69 239 // private:
jpayne@69 240 // bool all = false;
jpayne@69 241 // kj::StringPtr outputFile;
jpayne@69 242 // kj::ProcessContext& context;
jpayne@69 243 // };
jpayne@69 244
jpayne@69 245 public:
jpayne@69 246 MainBuilder(ProcessContext& context, StringPtr version,
jpayne@69 247 StringPtr briefDescription, StringPtr extendedDescription = nullptr);
jpayne@69 248 ~MainBuilder() noexcept(false);
jpayne@69 249
jpayne@69 250 class OptionName {
jpayne@69 251 public:
jpayne@69 252 OptionName() = default;
jpayne@69 253 inline OptionName(char shortName): isLong(false), shortName(shortName) {}
jpayne@69 254 inline OptionName(const char* longName): isLong(true), longName(longName) {}
jpayne@69 255
jpayne@69 256 private:
jpayne@69 257 bool isLong;
jpayne@69 258 union {
jpayne@69 259 char shortName;
jpayne@69 260 const char* longName;
jpayne@69 261 };
jpayne@69 262 friend class MainBuilder;
jpayne@69 263 };
jpayne@69 264
jpayne@69 265 class Validity {
jpayne@69 266 public:
jpayne@69 267 inline Validity(bool valid) {
jpayne@69 268 if (!valid) errorMessage = heapString("invalid argument");
jpayne@69 269 }
jpayne@69 270 inline Validity(const char* errorMessage)
jpayne@69 271 : errorMessage(heapString(errorMessage)) {}
jpayne@69 272 inline Validity(String&& errorMessage)
jpayne@69 273 : errorMessage(kj::mv(errorMessage)) {}
jpayne@69 274
jpayne@69 275 inline const Maybe<String>& getError() const { return errorMessage; }
jpayne@69 276 inline Maybe<String> releaseError() { return kj::mv(errorMessage); }
jpayne@69 277
jpayne@69 278 private:
jpayne@69 279 Maybe<String> errorMessage;
jpayne@69 280 friend class MainBuilder;
jpayne@69 281 };
jpayne@69 282
jpayne@69 283 MainBuilder& addOption(std::initializer_list<OptionName> names, Function<Validity()> callback,
jpayne@69 284 StringPtr helpText);
jpayne@69 285 // Defines a new option (flag). `names` is a list of characters and strings that can be used to
jpayne@69 286 // specify the option on the command line. Single-character names are used with "-" while string
jpayne@69 287 // names are used with "--". `helpText` is a natural-language description of the flag.
jpayne@69 288 //
jpayne@69 289 // `callback` is called when the option is seen. Its return value indicates whether the option
jpayne@69 290 // was accepted. If not, further option processing stops, and error is written, and the process
jpayne@69 291 // exits.
jpayne@69 292 //
jpayne@69 293 // Example:
jpayne@69 294 //
jpayne@69 295 // builder.addOption({'a', "all"}, KJ_BIND_METHOD(*this, showAll), "Show all files.");
jpayne@69 296 //
jpayne@69 297 // This option could be specified in the following ways:
jpayne@69 298 //
jpayne@69 299 // -a
jpayne@69 300 // --all
jpayne@69 301 //
jpayne@69 302 // Note that single-character option names can be combined into a single argument. For example,
jpayne@69 303 // `-abcd` is equivalent to `-a -b -c -d`.
jpayne@69 304 //
jpayne@69 305 // The help text for this option would look like:
jpayne@69 306 //
jpayne@69 307 // -a, --all
jpayne@69 308 // Show all files.
jpayne@69 309 //
jpayne@69 310 // Note that help text is automatically word-wrapped.
jpayne@69 311
jpayne@69 312 MainBuilder& addOptionWithArg(std::initializer_list<OptionName> names,
jpayne@69 313 Function<Validity(StringPtr)> callback,
jpayne@69 314 StringPtr argumentTitle, StringPtr helpText);
jpayne@69 315 // Like `addOption()`, but adds an option which accepts an argument. `argumentTitle` is used in
jpayne@69 316 // the help text. The argument text is passed to the callback.
jpayne@69 317 //
jpayne@69 318 // Example:
jpayne@69 319 //
jpayne@69 320 // builder.addOptionWithArg({'o', "output"}, KJ_BIND_METHOD(*this, setOutput),
jpayne@69 321 // "<filename>", "Output to <filename>.");
jpayne@69 322 //
jpayne@69 323 // This option could be specified with an argument of "foo" in the following ways:
jpayne@69 324 //
jpayne@69 325 // -ofoo
jpayne@69 326 // -o foo
jpayne@69 327 // --output=foo
jpayne@69 328 // --output foo
jpayne@69 329 //
jpayne@69 330 // Note that single-character option names can be combined, but only the last option can have an
jpayne@69 331 // argument, since the characters after the option letter are interpreted as the argument. E.g.
jpayne@69 332 // `-abofoo` would be equivalent to `-a -b -o foo`.
jpayne@69 333 //
jpayne@69 334 // The help text for this option would look like:
jpayne@69 335 //
jpayne@69 336 // -o FILENAME, --output=FILENAME
jpayne@69 337 // Output to FILENAME.
jpayne@69 338
jpayne@69 339 MainBuilder& addSubCommand(StringPtr name, Function<MainFunc()> getSubParser,
jpayne@69 340 StringPtr briefHelpText);
jpayne@69 341 // If exactly the given name is seen as an argument, invoke getSubParser() and then pass all
jpayne@69 342 // remaining arguments to the parser it returns. This is useful for implementing commands which
jpayne@69 343 // have lots of sub-commands, like "git" (which has sub-commands "checkout", "branch", "pull",
jpayne@69 344 // etc.).
jpayne@69 345 //
jpayne@69 346 // `getSubParser` is only called if the command is seen. This avoids building main functions
jpayne@69 347 // for commands that aren't used.
jpayne@69 348 //
jpayne@69 349 // `briefHelpText` should be brief enough to show immediately after the command name on a single
jpayne@69 350 // line. It will not be wrapped. Users can use the built-in "help" command to get extended
jpayne@69 351 // help on a particular command.
jpayne@69 352
jpayne@69 353 MainBuilder& expectArg(StringPtr title, Function<Validity(StringPtr)> callback);
jpayne@69 354 MainBuilder& expectOptionalArg(StringPtr title, Function<Validity(StringPtr)> callback);
jpayne@69 355 MainBuilder& expectZeroOrMoreArgs(StringPtr title, Function<Validity(StringPtr)> callback);
jpayne@69 356 MainBuilder& expectOneOrMoreArgs(StringPtr title, Function<Validity(StringPtr)> callback);
jpayne@69 357 // Set callbacks to handle arguments. `expectArg()` and `expectOptionalArg()` specify positional
jpayne@69 358 // arguments with special handling, while `expect{Zero,One}OrMoreArgs()` specifies a handler for
jpayne@69 359 // an argument list (the handler is called once for each argument in the list). `title`
jpayne@69 360 // specifies how the argument should be represented in the usage text.
jpayne@69 361 //
jpayne@69 362 // All options callbacks are called before argument callbacks, regardless of their ordering on
jpayne@69 363 // the command line. This matches GNU getopt's behavior of permuting non-flag arguments to the
jpayne@69 364 // end of the argument list. Also matching getopt, the special option "--" indicates that the
jpayne@69 365 // rest of the command line is all arguments, not options, even if they start with '-'.
jpayne@69 366 //
jpayne@69 367 // The interpretation of positional arguments is fairly flexible. The non-optional arguments can
jpayne@69 368 // be expected at the beginning, end, or in the middle. If more arguments are specified than
jpayne@69 369 // the number of non-optional args, they are assigned to the optional argument handlers in the
jpayne@69 370 // order of registration.
jpayne@69 371 //
jpayne@69 372 // For example, say you called:
jpayne@69 373 // builder.expectArg("<foo>", ...);
jpayne@69 374 // builder.expectOptionalArg("<bar>", ...);
jpayne@69 375 // builder.expectArg("<baz>", ...);
jpayne@69 376 // builder.expectZeroOrMoreArgs("<qux>", ...);
jpayne@69 377 // builder.expectArg("<corge>", ...);
jpayne@69 378 //
jpayne@69 379 // This command requires at least three arguments: foo, baz, and corge. If four arguments are
jpayne@69 380 // given, the second is assigned to bar. If five or more arguments are specified, then the
jpayne@69 381 // arguments between the third and last are assigned to qux. Note that it never makes sense
jpayne@69 382 // to call `expect*OrMoreArgs()` more than once since only the first call would ever be used.
jpayne@69 383 //
jpayne@69 384 // In practice, you probably shouldn't create such complicated commands as in the above example.
jpayne@69 385 // But, this flexibility seems necessary to support commands where the first argument is special
jpayne@69 386 // as well as commands (like `cp`) where the last argument is special.
jpayne@69 387
jpayne@69 388 MainBuilder& callAfterParsing(Function<Validity()> callback);
jpayne@69 389 // Call the given function after all arguments have been parsed.
jpayne@69 390
jpayne@69 391 MainFunc build();
jpayne@69 392 // Build the "main" function, which simply parses the arguments. Once this returns, the
jpayne@69 393 // `MainBuilder` is no longer valid.
jpayne@69 394
jpayne@69 395 private:
jpayne@69 396 struct Impl;
jpayne@69 397 Own<Impl> impl;
jpayne@69 398
jpayne@69 399 class MainImpl;
jpayne@69 400 };
jpayne@69 401
jpayne@69 402 } // namespace kj
jpayne@69 403
jpayne@69 404 KJ_END_HEADER