Pass rustfmt to fidlgen_rust

This CL adds support for rustfmt, so that FIDL->Rust output is
formatted. It uses the rustfmt binary from fuchsia/prebuilts, and the
confing file fuchsia/rustfmt.toml.

This also removes logic in main.go to determine default values for flags
because it is outdated (e.g. assumes fidlbolt is in the fuchsia tree)
and these defaults are already set in the Makefile.

Change-Id: I7942de31f14b39c58ffcc5b4bb97e5d0472ac878
diff --git a/Dockerfile b/Dockerfile
index 8a80046..9a2186b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -28,4 +28,5 @@
 CMD ["/server", \
     "-static", "/static", \
     "-bin", "/fuchsia/bin", \
+    "-etc", "/fuchsia/etc", \
     "-fidl", "/fuchsia/sdk/fidl:/fuchsia/zircon/system/fidl"]
diff --git a/Makefile b/Makefile
index 5b5ac0b..4346bb4 100644
--- a/Makefile
+++ b/Makefile
@@ -5,13 +5,26 @@
 # Whether to run the server against copied fuchsia files.
 COPY ?= 0
 
+# Detect host platform.
+UNAME_S := $(shell uname -s)
+ifeq ($(UNAME_S),Linux)
+HOST_PLATFORM := linux-x64
+else ifeq ($(UNAME_S),Darwin)
+HOST_PLATFORM := mac-x64
+else
+$(error Unsupported platform $(UNAME_S))
+endif
+
 # Paths to FIDL files and binaries (either copies or in-tree paths).
 ifeq ($(COPY),1)
 BIN := ./fuchsia/bin
+ETC := ./fuchsia/etc
 FIDL := ./fuchsia/sdk/fidl:fuchsia/zircon/system/fidl
 else
 BUILD_DIR := $(FUCHSIA_DIR)/$(file < $(FUCHSIA_DIR)/.fx-build-dir)
-BIN := $(BUILD_DIR)/host_x64:$(BUILD_DIR).zircon/tools
+RUST_BIN := $(FUCHSIA_DIR)/prebuilt/third_party/rust/$(HOST_PLATFORM)/bin
+BIN := $(BUILD_DIR)/host_x64:$(BUILD_DIR).zircon/tools:$(RUST_BIN)
+ETC := $(FUCHSIA_DIR)
 FIDL := $(FUCHSIA_DIR)/sdk/fidl:$(FUCHSIA_DIR)/zircon/system/fidl
 endif
 
@@ -54,7 +67,7 @@
 run: $(if $(wildcard frontend/dist),,frontend)
 run: $(if $(wildcard $(SERVER)),,backend)
 ifndef FUCHSIA_DIR
-	$(error FUCHSIA_DIR is not defined. Either pass in FUCHSIA_DIR="path" or define the variable.)
+	$(error FUCHSIA_DIR is not defined. Either pass in FUCHSIA_DIR="path" or define the variable)
 endif
 ifeq ($(COPY),1)
 	./copy_fuchsia_files.sh
@@ -62,6 +75,7 @@
 	./$(SERVER) \
 		-static=frontend/dist \
 		-bin=$(BIN) \
+		-etc=$(ETC) \
 		-fidl=$(FIDL) \
 		-port=$(PORT) \
 		-verbose=$(VERBOSE)
diff --git a/backend/main.go b/backend/main.go
index 647d550..c4947c6 100644
--- a/backend/main.go
+++ b/backend/main.go
@@ -8,12 +8,10 @@
 	"encoding/json"
 	"flag"
 	"fmt"
-	"io/ioutil"
 	"log"
 	"net/http"
 	"os"
 	"path/filepath"
-	"strings"
 	"time"
 )
 
@@ -22,6 +20,7 @@
 	verboseFlag = flag.Bool("verbose", false, "enable verbose logging")
 	staticFlag  = flag.String("static", "", "static files directory")
 	binFlag     = flag.String("bin", "", "binary search paths, "+pathHelp)
+	etcFlag     = flag.String("etc", "", "config file search paths, "+pathHelp)
 	fidlFlag    = flag.String("fidl", "", "FIDL library search paths, "+pathHelp)
 
 	pathHelp = fmt.Sprintf(`separated by "%s"`, string(filepath.ListSeparator))
@@ -45,33 +44,24 @@
 	flag.Usage = printUsage
 	flag.Parse()
 
+	// Remove timestamps from logs for startup error messages.
+	log.SetFlags(0)
+
 	static := *staticFlag
 	bin := filepath.SplitList(*binFlag)
+	etc := filepath.SplitList(*etcFlag)
 	fidl := filepath.SplitList(*fidlFlag)
-	if static == "" || len(bin) == 0 || len(fidl) == 0 {
-		fuchsia := getFuchsiaDir()
-		if static == "" {
-			static = filepath.Join(filepath.Dir(fuchsia), "fidlbolt", "frontend", "dist")
-		}
-		if len(bin) == 0 {
-			build := getBuildDir(fuchsia)
-			bin = []string{
-				filepath.Join(build, "host_x64"),
-				filepath.Join(build+".zircon", "tools"),
-			}
-		}
-		if len(fidl) == 0 {
-			fidl = []string{
-				filepath.Join(fuchsia, "sdk", "fidl"),
-				filepath.Join(fuchsia, "zircon", "system", "fidl"),
-			}
-		}
+	if static == "" || len(bin) == 0 || len(etc) == 0 || len(fidl) == 0 {
+		log.Print("must provide -static, -bin, -etc, and -fidl")
+		printUsage()
+		os.Exit(1)
 	}
 	checkDirs(static)
 	checkDirs(bin...)
+	checkDirs(etc...)
 	checkDirs(fidl...)
 
-	server, err := NewServer(bin, fidl)
+	server, err := NewServer(bin, etc, fidl)
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -100,6 +90,7 @@
 		WriteTimeout: timeout + 5*time.Second,
 	}
 	log.Printf("Listening on %s", s.Addr)
+	log.SetFlags(log.LstdFlags)
 	log.Fatal(s.ListenAndServe())
 }
 
@@ -144,22 +135,6 @@
 	w.Write(body)
 }
 
-func getFuchsiaDir() string {
-	dir, ok := os.LookupEnv("FUCHSIA_DIR")
-	if !ok {
-		log.Fatal("Must set FUCHSIA_DIR or provide all flags")
-	}
-	return dir
-}
-
-func getBuildDir(fuchsia string) string {
-	b, err := ioutil.ReadFile(filepath.Join(fuchsia, ".fx-build-dir"))
-	if err != nil {
-		log.Fatal(err)
-	}
-	return filepath.Join(fuchsia, strings.TrimSpace(string(b)))
-}
-
 func checkDirs(dirs ...string) {
 	for _, dir := range dirs {
 		if _, err := os.Stat(dir); os.IsNotExist(err) {
diff --git a/backend/server.go b/backend/server.go
index e2487cd..99571a5 100644
--- a/backend/server.go
+++ b/backend/server.go
@@ -22,15 +22,17 @@
 type Server struct {
 	// External programs used by fidlbolt.
 	fidlFormat, fidlLint, fidlc, fidlgenLlcpp, fidlgenHlcpp, fidlgenRust,
-	fidlgenGo, fidlgenDart program
+	fidlgenGo, fidlgenDart, rustfmt program
+	// External files used by fidlbolt.
+	rustfmtToml string
 	// Cache of FIDL library information.
 	libraryAnalyzer *libraryAnalyzer
 }
 
-// NewServer returns a new fidlbolt server. It takes two lists of paths: bin,
-// for locating binaries, and fidl, for locating FIDL libraries. Returns an
-// error if a required program could not be located in bin.
-func NewServer(bin, fidl []string) (*Server, error) {
+// NewServer returns a new fidlbolt server. It takes three lists of paths: bin,
+// for locating binaries, etc, for locating config files, and fidl, for locating
+// FIDL libraries. Returns an error if a required file could not be located.
+func NewServer(bin, etc, fidl []string) (*Server, error) {
 	var s Server
 	var err error
 	s.fidlFormat, err = findProgram("fidl-format", bin)
@@ -65,6 +67,14 @@
 	if err != nil {
 		return nil, err
 	}
+	s.rustfmt, err = findProgram("rustfmt", bin)
+	if err != nil {
+		return nil, err
+	}
+	s.rustfmtToml, err = findFile("rustfmt.toml", etc)
+	if err != nil {
+		return nil, err
+	}
 	s.libraryAnalyzer = newLibraryAnalyzer(fidl)
 	return &s, nil
 }
@@ -402,6 +412,8 @@
 		if res := s.fidlgenRust.run(ctx,
 			"-json", jsonIR,
 			"-output-filename", temp.join("impl.rs"),
+			"-rustfmt", s.rustfmt.path,
+			"-rustfmt-config", s.rustfmtToml,
 		); !res.Ok {
 			return res, nil
 		}
@@ -482,13 +494,20 @@
 // findProgram searches the directories in bin for the named program. It returns
 // an error if the program cannot be found.
 func findProgram(name string, bin []string) (program, error) {
-	for _, dir := range bin {
+	path, err := findFile(name, bin)
+	return program{path}, err
+}
+
+// findFile searches the directories in dirs for the file with the given name.
+// It returns the full path, or an error if the file cannot be found.
+func findFile(name string, dirs []string) (string, error) {
+	for _, dir := range dirs {
 		path := filepath.Join(dir, name)
 		if _, err := os.Stat(path); err == nil {
-			return program{path}, nil
+			return path, nil
 		}
 	}
-	return program{}, fmt.Errorf("cannot find program: %q", name)
+	return "", fmt.Errorf("cannot find %s (searched in %s)", name, strings.Join(dirs, ", "))
 }
 
 // run runs a program with arguments and produces a response. It stops the
diff --git a/copy_fuchsia_files.sh b/copy_fuchsia_files.sh
index f3e2ebc..3839114 100755
--- a/copy_fuchsia_files.sh
+++ b/copy_fuchsia_files.sh
@@ -11,6 +11,9 @@
     die "FUCHSIA_DIR not set"
 fi
 
+# Sets PREBUILT_RUST_DIR.
+source "$FUCHSIA_DIR/tools/devshell/lib/prebuilt.sh"
+
 build_dir_file="$FUCHSIA_DIR/.fx-build-dir"
 if ! [[ -f "$build_dir_file" ]]; then
     die "$build_dir_file not found"
@@ -28,11 +31,15 @@
 
 cd "$(dirname "$0")"
 rm -rf fuchsia/
-mkdir -p fuchsia/{bin,sdk/fidl,zircon/system/fidl}
+mkdir -p fuchsia/{bin,etc,sdk/fidl,zircon/system/fidl}
 cp \
     "$build_dir/host_x64/"{fidlc,fidlgen_{llcpp,hlcpp,rust,go,dart}} \
     "$zircon_build_dir/tools/fidl-"{format,lint} \
+    "$PREBUILT_RUST_DIR/bin/rustfmt" \
     fuchsia/bin
+cp \
+    "$FUCHSIA_DIR/rustfmt.toml" \
+    fuchsia/etc
 find "$FUCHSIA_DIR/sdk/fidl" -mindepth 1 -maxdepth 1 -type d \
     | xargs -I% cp -r % fuchsia/sdk/fidl
 find "$FUCHSIA_DIR/zircon/system/fidl" -mindepth 1 -maxdepth 1 -type d \