Return errors from subcommands

This pulls in `anyhow` and updates subcommand implementations to return
results which propagate up to main.

Change-Id: Idd360e03c1adf8960c09a2690bf5e0753ac3e464
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/qg/+/119870
Commit-Queue: Alexei Frolov <frolv@google.com>
Reviewed-by: Erik Gilling <konkers@google.com>
diff --git a/Cargo.lock b/Cargo.lock
index 79929bf..51ba387 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1259,6 +1259,7 @@
 name = "qg-cli"
 version = "0.1.0"
 dependencies = [
+ "anyhow",
  "clap 4.0.26",
  "qg",
  "tokio",
diff --git a/qg-cli/Cargo.toml b/qg-cli/Cargo.toml
index 46f8f2d..315008c 100644
--- a/qg-cli/Cargo.toml
+++ b/qg-cli/Cargo.toml
@@ -8,6 +8,7 @@
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
+anyhow = "1.0.66"
 clap = { version = "4.0.25", features = ["derive"] }
 qg = { path = "../qg" }
 tokio = { version = "1.21.2", features = ["full"] }
diff --git a/qg-cli/src/main.rs b/qg-cli/src/main.rs
index 060cbcf..b3b8d55 100644
--- a/qg-cli/src/main.rs
+++ b/qg-cli/src/main.rs
@@ -13,17 +13,19 @@
 // the License.
 
 mod hello;
+mod new;
 mod subcommands;
 
 #[cfg(feature = "python")]
 mod py_demo;
 
+use anyhow::Result;
 use clap::{FromArgMatches, Subcommand};
 
 use subcommands::Subcommands;
 
 #[tokio::main]
-async fn main() {
+async fn main() -> Result<()> {
     let app = clap::Command::new("qg");
 
     let app = Subcommands::augment_subcommands(app);
@@ -34,6 +36,7 @@
         Ok(subcommands) => subcommands.run(),
         Err(e) if e.kind() == clap::error::ErrorKind::MissingSubcommand => {
             run_main_command(&matches);
+            Ok(())
         }
         Err(e) => e.exit(),
     }
diff --git a/qg-cli/src/new.rs b/qg-cli/src/new.rs
new file mode 100644
index 0000000..d645b60
--- /dev/null
+++ b/qg-cli/src/new.rs
@@ -0,0 +1,22 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+use anyhow::{anyhow, Result};
+
+#[derive(clap::Parser, Debug)]
+pub struct Command {}
+
+pub fn run(_args: &Command) -> Result<()> {
+    Err(anyhow!("Not implemented"))
+}
diff --git a/qg-cli/src/subcommands.rs b/qg-cli/src/subcommands.rs
index b33cd98..aa3b870 100644
--- a/qg-cli/src/subcommands.rs
+++ b/qg-cli/src/subcommands.rs
@@ -12,7 +12,9 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-use crate::hello;
+use anyhow::Result;
+
+use crate::{hello, new};
 
 #[cfg(feature = "python")]
 use crate::py_demo;
@@ -24,21 +26,28 @@
 #[derive(clap::Parser, Debug)]
 pub enum Subcommands {
     Hello(hello::Command),
+    New(new::Command),
+
     #[cfg(feature = "python")]
     PyDemo(py_demo::Command),
 }
 
 impl Subcommands {
     /// Invokes a subcommand with its parsed arguments.
-    pub fn run(&self) {
+    pub fn run(&self) -> Result<()> {
         match self {
-            Self::Hello(cmd) => {
-                hello::run(cmd);
+            Self::Hello(args) => {
+                hello::run(args);
+            }
+            Self::New(args) => {
+                new::run(args)?;
             }
             #[cfg(feature = "python")]
             Self::PyDemo(cmd) => {
                 py_demo::run(cmd);
             }
-        }
+        };
+
+        Ok(())
     }
 }