package main import ( "errors" "fmt" "io/fs" "os" "path/filepath" "strings" "github.com/bluesky-social/indigo/lex" "github.com/urfave/cli/v2" ) func findSchemas(dir string, out []string) ([]string, error) { err := filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } if strings.HasSuffix(path, ".json") { out = append(out, path) } return nil }) if err != nil { return out, err } return out, nil } // for direct .json lexicon files or directories containing lexicon .json files, get one flat list of all paths to .json files func expandArgs(args []string) ([]string, error) { var out []string for _, a := range args { st, err := os.Stat(a) if err != nil { return nil, err } if st.IsDir() { out, err = findSchemas(a, out) if err != nil { return nil, err } } else if strings.HasSuffix(a, ".json") { out = append(out, a) } } return out, nil } func main() { app := cli.NewApp() app.Flags = []cli.Flag{ &cli.StringFlag{ Name: "outdir", }, &cli.BoolFlag{ Name: "gen-server", }, &cli.BoolFlag{ Name: "gen-handlers", }, &cli.StringSliceFlag{ Name: "types-import", }, &cli.StringSliceFlag{ Name: "external-lexicons", }, &cli.StringFlag{ Name: "package", Value: "schemagen", }, &cli.StringFlag{ Name: "build", Value: "", }, &cli.StringFlag{ Name: "build-file", Value: "", }, } app.Action = func(cctx *cli.Context) error { paths, err := expandArgs(cctx.Args().Slice()) if err != nil { return err } var schemas []*lex.Schema for _, arg := range paths { if strings.HasSuffix(arg, "com/atproto/temp/importRepo.json") { fmt.Printf("skipping schema: %s\n", arg) continue } s, err := lex.ReadSchema(arg) if err != nil { return fmt.Errorf("failed to read file %q: %w", arg, err) } schemas = append(schemas, s) } externalPaths, err := expandArgs(cctx.StringSlice("external-lexicons")) if err != nil { return err } var externalSchemas []*lex.Schema for _, arg := range externalPaths { s, err := lex.ReadSchema(arg) if err != nil { return fmt.Errorf("failed to read file %q: %w", arg, err) } externalSchemas = append(externalSchemas, s) } buildLiteral := cctx.String("build") buildPath := cctx.String("build-file") var packages []lex.Package if buildLiteral != "" { if buildPath != "" { return errors.New("must not set both --build and --build-file") } packages, err = lex.ParsePackages([]byte(buildLiteral)) if err != nil { return fmt.Errorf("--build error, %w", err) } if len(packages) == 0 { return errors.New("--build must specify at least one Package{}") } } else if buildPath != "" { blob, err := os.ReadFile(buildPath) if err != nil { return fmt.Errorf("--build-file error, %w", err) } packages, err = lex.ParsePackages(blob) if err != nil { return fmt.Errorf("--build-file error, %w", err) } if len(packages) == 0 { return errors.New("--build-file must specify at least one Package{}") } } else { return errors.New("need exactly one of --build or --build-file") } if cctx.Bool("gen-server") { pkgname := cctx.String("package") outdir := cctx.String("outdir") if outdir == "" { return fmt.Errorf("must specify output directory (--outdir)") } defmap := lex.BuildExtDefMap(append(schemas, externalSchemas...), packages) _ = defmap paths := cctx.StringSlice("types-import") importmap := make(map[string]string) for _, p := range paths { parts := strings.Split(p, ":") importmap[parts[0]] = parts[1] } handlers := cctx.Bool("gen-handlers") if err := lex.CreateHandlerStub(pkgname, importmap, outdir, schemas, handlers); err != nil { return err } } else { return lex.Run(schemas, externalSchemas, packages) } return nil } app.RunAndExitOnError() }