package main import ( "bufio" "bytes" "fmt" "io/ioutil" "log" "net/textproto" "os" "regexp" "strconv" "strings" "github.com/bmatcuk/doublestar" "github.com/urfave/cli" "github.com/bradrydzewski/togo/template" ) type ( httptestParams struct { Package string Routes []*httptestRoute } httptestRoute struct { Source string Method string Path string Body string Status int Header map[string]string } ) var httptestCommand = cli.Command{ Name: "httptest", Usage: "generate httptest server", Action: httptestAction, Flags: []cli.Flag{ cli.StringFlag{ Name: "package", Value: "testdata", }, cli.StringFlag{ Name: "input", Value: "files/**", }, cli.StringFlag{ Name: "output", Value: "testdata_gen.go", }, cli.StringFlag{ Name: "exclude", }, }, } func httptestAction(c *cli.Context) error { pattern := c.Args().First() if pattern == "" { pattern = c.String("input") } matches, err := doublestar.Glob(pattern) if err != nil { return err } var exclude *regexp.Regexp if s := c.String("exclude"); s != "" { exclude = regexp.MustCompilePOSIX(s) } params := httptestParams{ Package: c.String("package"), } for _, match := range matches { stat, oserr := os.Stat(match) if oserr != nil { return oserr } if stat.IsDir() { continue } if exclude != nil && exclude.MatchString(match) { continue } log.Printf("parsing file %s", match) raw, ioerr := ioutil.ReadFile(match) if ioerr != nil { return ioerr } route, parseErr := parseRoute(raw) if parseErr != nil { return parseErr } route.Source = strings.TrimPrefix(match, "files/") params.Routes = append(params.Routes, route) } wr := os.Stdout if output := c.String("output"); output != "" { wr, err = os.Create(output) if err != nil { return err } defer wr.Close() } return template.Execute(wr, "httptest.tmpl", ¶ms) } func parseRoute(in []byte) (*httptestRoute, error) { out := new(httptestRoute) out.Header = map[string]string{} buf := bufio.NewReader(bytes.NewBuffer(in)) r := textproto.NewReader(buf) // // parses the method and path // line, err := r.ReadLine() if err != nil { return nil, err } parts := strings.Split(line, " ") if len(parts) != 2 { return nil, fmt.Errorf("Invalid request line. Want .") } out.Method = parts[0] out.Path = parts[1] // // parses the mime headers // header, err := r.ReadMIMEHeader() if err != nil { fmt.Println(err) } for k, v := range header { out.Header[k] = strings.Join(v, "; ") } // // extracts the response status code // out.Status, err = strconv.Atoi(header.Get("Status")) if err != nil { return nil, fmt.Errorf("Invalid Status code. %s", err) } delete(out.Header, "Status") // // parse the remainder of the file as the body // body, err := ioutil.ReadAll(buf) if err != nil { return nil, err } out.Body = string(body) return out, nil }