Add the depstool binary

Currently, it only supports a "validate" command that validates that the tool can parse the WORKSPACE file.

PiperOrigin-RevId: 481650085
diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel
index 620ac8a..aa887dd 100644
--- a/WORKSPACE.bazel
+++ b/WORKSPACE.bazel
@@ -46,7 +46,7 @@
 
 http_archive(
     name = "com_google_googleurl",
-    sha256 = "a1bc96169d34dcc1406ffb750deef3bc8718bd1f9069a2878838e1bd905de989",
+    sha256 = "a1bc96169d34dcc1406ffb750deef3bc8718bd1f9069a2878838e1bd905de989",  # Last updated 2022-04-04
     urls = ["https://storage.googleapis.com/quiche-envoy-integration/googleurl_9cdb1f4d1a365ebdbcbf179dadf7f8aa5ee802e7.tar.gz"],
 )
 
@@ -71,9 +71,9 @@
 # https://github.com/google/re2/tree/abseil
 http_archive(
     name = "com_googlesource_code_re2",
-    sha256 = "906d0df8ff48f8d3a00a808827f009a840190f404559f649cb8e4d7143255ef9",
+    sha256 = "906d0df8ff48f8d3a00a808827f009a840190f404559f649cb8e4d7143255ef9",  # Last updated 2022-04-08
     strip_prefix = "re2-a276a8c738735a0fe45a6ee590fe2df69bcf4502",
-    urls = ["https://github.com/google/re2/archive/a276a8c738735a0fe45a6ee590fe2df69bcf4502.zip"],  # 2022-04-08
+    urls = ["https://github.com/google/re2/archive/a276a8c738735a0fe45a6ee590fe2df69bcf4502.zip"],
 )
 
 # -------- Load and call dependencies of underlying libraries --------
diff --git a/depstool/depstool.go b/depstool/depstool.go
new file mode 100644
index 0000000..cbc27bc
--- /dev/null
+++ b/depstool/depstool.go
@@ -0,0 +1,70 @@
+// depstool is a command-line tool for manipulating QUICHE WORKSPACE.bazel file.
+package main
+
+import (
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"os"
+
+	"github.com/bazelbuild/buildtools/build"
+	"quiche.googlesource.com/quiche/depstool/deps"
+)
+
+func validate(path string, contents []byte) {
+	file, err := build.ParseWorkspace(path, contents)
+	if err != nil {
+		log.Fatalf("Failed to parse the WORKSPACE.bazel file: %v", err)
+	}
+
+	success := true
+	for _, stmt := range file.Stmt {
+		rule, ok := deps.HTTPArchiveRule(stmt)
+		if !ok {
+			// Skip unrelated rules
+			continue
+		}
+		if _, err := deps.ParseHTTPArchiveRule(rule); err != nil {
+			log.Printf("Failed to parse http_archive in %s on the line %d, issue: %v", path, rule.Pos.Line, err)
+			success = false
+		}
+	}
+	if !success {
+		os.Exit(1)
+	}
+	log.Printf("All http_archive rules have been validated successfully")
+	os.Exit(0)
+}
+
+func usage() {
+	fmt.Fprintf(flag.CommandLine.Output(), `
+usage: depstool [WORKSPACE file] [subcommand]
+
+Available subcommands:
+    validate   Validates that the WORKSPACE file is parsable
+`)
+	flag.PrintDefaults()
+}
+
+func main() {
+	flag.Usage = usage
+	flag.Parse()
+	path := flag.Arg(0)
+	if path == "" {
+		usage()
+		os.Exit(1)
+	}
+	contents, err := ioutil.ReadFile(path)
+	if err != nil {
+		log.Fatalf("Failed to read WORKSPACE.bazel file: %v", err)
+	}
+
+	subcommand := flag.Arg(1)
+	switch subcommand {
+	case "validate":
+		validate(path, contents)
+	default:
+		log.Fatalf("Unknown command: %s", subcommand)
+	}
+}