From f67db54d2b877535b5222f37d6e5231fdc8e87a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Llor=C3=A0?= Date: Tue, 1 Mar 2016 10:38:39 -0800 Subject: [PATCH] Refactoring the command line main to allow driver additions. This change contains a refactor of the main to only leave code for driver registration. It also enables the use of flags and removes temporary code used for flag managment. --- tools/vcli/bw/assert/assert.go | 21 +++------- tools/vcli/bw/common/common.go | 70 ++++++++++++++++++++-------------- tools/vcli/bw/io/io.go | 51 +++++++++++++++++++++++++ tools/vcli/bw/main.go | 40 +++++++++++-------- tools/vcli/bw/run/run.go | 23 ++++------- 5 files changed, 130 insertions(+), 75 deletions(-) create mode 100644 tools/vcli/bw/io/io.go diff --git a/tools/vcli/bw/assert/assert.go b/tools/vcli/bw/assert/assert.go index a67e98c2..b5023a45 100644 --- a/tools/vcli/bw/assert/assert.go +++ b/tools/vcli/bw/assert/assert.go @@ -27,41 +27,32 @@ import ( "github.com/google/badwolf/storage" "github.com/google/badwolf/tools/compliance" "github.com/google/badwolf/tools/vcli/bw/command" - "github.com/google/badwolf/tools/vcli/bw/common" + "github.com/google/badwolf/tools/vcli/bw/io" "github.com/google/badwolf/triple/literal" ) // New creates the help command. -func New(store storage.Store, builder literal.Builder) *command.Command { +func New(store storage.Store, builder literal.Builder, chanSize int) *command.Command { cmd := &command.Command{ - UsageLine: "assert [--channel_size=123] folder_path", + UsageLine: "assert folder_path", Short: "asserts all the stories in the indicated folder.", Long: `Asserts all the stories in the folder. Each story is stored in a JSON file containing all the sources and all the assertions to run. `, } cmd.Run = func(ctx context.Context, args []string) int { - return assertCommand(ctx, cmd, args, store, builder) + return assertCommand(ctx, cmd, args, store, builder, chanSize) } return cmd } // assertCommand runs all the BQL statements available in the file. -func assertCommand(ctx context.Context, cmd *command.Command, args []string, store storage.Store, builder literal.Builder) int { +func assertCommand(ctx context.Context, cmd *command.Command, args []string, store storage.Store, builder literal.Builder, chanSize int) int { if len(args) < 3 { fmt.Fprintf(os.Stderr, "Missing required folder path. ") cmd.Usage() return 2 } - chanSize := 0 - if len(args) >= 4 { - c, err := common.ParseChannelSizeFlag(args[2]) - if err != nil { - fmt.Fprintf(os.Stderr, "Fail to parse flag %s with error %v\n", args[2], err) - return 2 - } - chanSize = c - } // Open the folder. folder := strings.TrimSpace(args[len(args)-1]) f, err := os.Open(folder) @@ -83,7 +74,7 @@ func assertCommand(ctx context.Context, cmd *command.Command, args []string, sto continue } fmt.Printf("\tProcessing file %q... ", fi.Name()) - lns, err := common.ReadLines(path.Join(folder, fi.Name())) + lns, err := io.ReadLines(path.Join(folder, fi.Name())) if err != nil { fmt.Fprintf(os.Stderr, "\n\n\tFailed to read file content with error %v\n\n", err) return 2 diff --git a/tools/vcli/bw/common/common.go b/tools/vcli/bw/common/common.go index 04d1190c..85ea1fb8 100644 --- a/tools/vcli/bw/common/common.go +++ b/tools/vcli/bw/common/common.go @@ -17,7 +17,6 @@ package common import ( - "bufio" "fmt" "os" "sort" @@ -26,37 +25,14 @@ import ( "golang.org/x/net/context" + "github.com/google/badwolf/storage" + "github.com/google/badwolf/tools/vcli/bw/assert" "github.com/google/badwolf/tools/vcli/bw/command" + "github.com/google/badwolf/tools/vcli/bw/run" + "github.com/google/badwolf/tools/vcli/bw/version" + "github.com/google/badwolf/triple/literal" ) -// ReadLines from a file into a string array. -func ReadLines(path string) ([]string, error) { - f, err := os.Open(path) - if err != nil { - return nil, err - } - defer f.Close() - - var lines []string - scanner := bufio.NewScanner(f) - line := "" - for scanner.Scan() { - l := strings.TrimSpace(scanner.Text()) - if len(l) == 0 || strings.Index(l, "#") == 0 { - continue - } - line += " " + l - if l[len(l)-1:] == ";" { - lines = append(lines, strings.TrimSpace(line)) - line = "" - } - } - if line != "" { - lines = append(lines, strings.TrimSpace(line)) - } - return lines, scanner.Err() -} - // ParseChannelSizeFlag attempts to parse the "channel_size" flag. func ParseChannelSizeFlag(flag string) (int, error) { ss := strings.Split(flag, "=") @@ -100,6 +76,32 @@ func Help(args []string, cmds []*command.Command) int { return 2 } +// StoreGenerator is a function that generate a new valid storage.Store. +type StoreGenerator func() (storage.Store, error) + +// InitializeDriver attemps to initalize the driver. +func InitializeDriver(driverName string, drivers map[string]StoreGenerator) (storage.Store, error) { + f, ok := drivers[driverName] + if !ok { + var ds []string + for k := range drivers { + ds = append(ds, k) + } + return nil, fmt.Errorf("unkown driver name %q; valid drivers [%q]", driverName, strings.Join(ds, ", ")) + } + return f() +} + +// InitializeCommands intializes the avaialbe commands with the given storage +// instance. +func InitializeCommands(driver storage.Store, chanSize int) []*command.Command { + return []*command.Command{ + assert.New(driver, literal.DefaultBuilder(), chanSize), + run.New(driver, chanSize), + version.New(), + } +} + // Eval of the command line version tool. This allows injecting multiple // drivers. func Eval(ctx context.Context, args []string, cmds []*command.Command) int { @@ -126,3 +128,13 @@ func Eval(ctx context.Context, args []string, cmds []*command.Command) int { } return 1 } + +// Run executes the main of the command line tool. +func Run(driverName string, drivers map[string]StoreGenerator, chanSize int) { + driver, err := InitializeDriver(driverName, drivers) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(2) + } + os.Exit(Eval(context.Background(), os.Args, InitializeCommands(driver, chanSize))) +} diff --git a/tools/vcli/bw/io/io.go b/tools/vcli/bw/io/io.go new file mode 100644 index 00000000..6349b811 --- /dev/null +++ b/tools/vcli/bw/io/io.go @@ -0,0 +1,51 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// 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 +// +// http://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. + +// Package io contains helper functions for io operatoins on the command line +// bw tool. +package io + +import ( + "bufio" + "os" + "strings" +) + +// ReadLines from a file into a string array. +func ReadLines(path string) ([]string, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + + var lines []string + scanner := bufio.NewScanner(f) + line := "" + for scanner.Scan() { + l := strings.TrimSpace(scanner.Text()) + if len(l) == 0 || strings.Index(l, "#") == 0 { + continue + } + line += " " + l + if l[len(l)-1:] == ";" { + lines = append(lines, strings.TrimSpace(line)) + line = "" + } + } + if line != "" { + lines = append(lines, strings.TrimSpace(line)) + } + return lines, scanner.Err() +} diff --git a/tools/vcli/bw/main.go b/tools/vcli/bw/main.go index 8f3f6bf8..17fc33ee 100644 --- a/tools/vcli/bw/main.go +++ b/tools/vcli/bw/main.go @@ -13,31 +13,41 @@ // limitations under the License. // BadWolf command line tools allows you to interact with graphs via BQL. - +// This file is a template for creating your own bw tool with custom backend +// storage drivers. Just *copy* this file to your project, add the required +// flags that you need to initialize your driver and register your diver on +// the registeredDriver map in the registerDrivers function. package main import ( - "os" + "flag" + "github.com/google/badwolf/storage" "github.com/google/badwolf/storage/memory" - "github.com/google/badwolf/tools/vcli/bw/assert" - "github.com/google/badwolf/tools/vcli/bw/command" "github.com/google/badwolf/tools/vcli/bw/common" - "github.com/google/badwolf/tools/vcli/bw/run" - "github.com/google/badwolf/tools/vcli/bw/version" - "github.com/google/badwolf/triple/literal" +) - "golang.org/x/net/context" +var ( + // drivers contains the registed drivers available for this command line tool. + registeredDrivers map[string]common.StoreGenerator + // Available flags. + driver = flag.String("driver", "VOLATILE", "The storage driver to use {VOLATILE}.") + bqlChannelSize = flag.Int("bql_channel_size", 0, "Internal channel size to use on BQL queries.") + // Add your driver flags below. ) -// Registration of the available commands. Please keep sorted. -var cmds = []*command.Command{ - assert.New(memory.NewStore(), literal.DefaultBuilder()), - run.New(memory.NewStore()), - version.New(), +// Registers the available drivers. +func registerDrivers() { + registeredDrivers = map[string]common.StoreGenerator{ + // Memory only storage driver. + "VOLATILE": func() (storage.Store, error) { + return memory.NewStore(), nil + }, + } } func main() { - ctx, args := context.Background(), os.Args - os.Exit(common.Eval(ctx, args, cmds)) + flag.Parse() + registerDrivers() + common.Run(*driver, registeredDrivers, *bqlChannelSize) } diff --git a/tools/vcli/bw/run/run.go b/tools/vcli/bw/run/run.go index 99b20144..cf61dc84 100644 --- a/tools/vcli/bw/run/run.go +++ b/tools/vcli/bw/run/run.go @@ -29,13 +29,13 @@ import ( "github.com/google/badwolf/bql/table" "github.com/google/badwolf/storage" "github.com/google/badwolf/tools/vcli/bw/command" - "github.com/google/badwolf/tools/vcli/bw/common" + "github.com/google/badwolf/tools/vcli/bw/io" ) // New creates the help command. -func New(store storage.Store) *command.Command { +func New(store storage.Store, chanSize int) *command.Command { cmd := &command.Command{ - UsageLine: "run [--channel_size=123] file_path", + UsageLine: "run file_path", Short: "runs BQL statements.", Long: `Runs all the commands listed in the provided file. Lines in the the file starting with # will be ignored. All statements will be run @@ -43,34 +43,25 @@ sequentially. `, } cmd.Run = func(ctx context.Context, args []string) int { - return runCommand(ctx, cmd, args, store) + return runCommand(ctx, cmd, args, store, chanSize) } return cmd } // runCommand runs all the BQL statements available in the file. -func runCommand(ctx context.Context, cmd *command.Command, args []string, store storage.Store) int { +func runCommand(ctx context.Context, cmd *command.Command, args []string, store storage.Store, chanSize int) int { if len(args) < 3 { fmt.Fprintf(os.Stderr, "Missing required file path. ") cmd.Usage() return 2 } - chanSize := 0 - if len(args) >= 4 { - c, err := common.ParseChannelSizeFlag(args[2]) - if err != nil { - fmt.Fprintf(os.Stderr, "Fail to parse flag %s with error %v\n", args[2], err) - return 2 - } - chanSize = c - } file := strings.TrimSpace(args[len(args)-1]) lines, err := getStatementsFromFile(file) if err != nil { fmt.Fprintf(os.Stderr, "Failed to read file %s\n\n\t%v\n\n", file, err) return 2 } - fmt.Printf("Processing file %s\n\n", args[2]) + fmt.Printf("Processing file %s\n\n", args[len(args)-1]) for idx, stm := range lines { fmt.Printf("Processing statement (%d/%d):\n%s\n\n", idx+1, len(lines), stm) tbl, err := runBQL(ctx, stm, store, chanSize) @@ -110,7 +101,7 @@ func runBQL(ctx context.Context, bql string, s storage.Store, chanSize int) (*ta // getStatementsFromFile returns the statements found in the provided file. func getStatementsFromFile(path string) ([]string, error) { - stms, err := common.ReadLines(path) + stms, err := io.ReadLines(path) if err != nil { return nil, err }