From a7abf75794eb29bb4ea031cc186b562c44b737cb Mon Sep 17 00:00:00 2001 From: Adrien Delorme Date: Thu, 16 Jul 2020 15:14:04 +0200 Subject: [PATCH] Add snippet-extract cmd that allows to extract parts of examples for docs --- cmd/snippet-extractor/main.go | 146 ++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 cmd/snippet-extractor/main.go diff --git a/cmd/snippet-extractor/main.go b/cmd/snippet-extractor/main.go new file mode 100644 index 000000000..331ea4404 --- /dev/null +++ b/cmd/snippet-extractor/main.go @@ -0,0 +1,146 @@ +// snippet-extract --begin=#_BEGIN_WRAP_TAG_ --end=#_END_WRAP_TAG_ -output_dir=./docs/ [./file|./directory/]... +// Extracts markdown snippets from relative files into output_dir, keeping the +// directory layout. +// It is not mandatory to terminate a snippet, the extractor will simply add +// line until EOF. +// Lines matching begin or end tags will not be put in the resulting file. +// When a directory is passed, all files from directory will be parsed. +package main + +import ( + "bufio" + "bytes" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "strings" +) + +type options struct { + filenames []string + begin, end string + outputDir string + extension string +} + +func (o *options) AddFlagSets(fs *flag.FlagSet) { + fs.StringVar(&o.begin, "begin", "#_BEGIN_WRAP_TAG_", "flag to mark beginning of a snippet") + fs.StringVar(&o.end, "end", "#_END_WRAP_TAG_", "flag to mark ending of a snippet") + fs.StringVar(&o.outputDir, "output_dir", "./docs/", "flag to mark ending of a snippet") + fs.StringVar(&o.extension, "extension", ".mdx", "extension for generated files") +} + +func main() { + fs := flag.NewFlagSet("snippet-extract", flag.ContinueOnError) + opts := options{} + opts.AddFlagSets(fs) + if err := fs.Parse(os.Args[1:]); err != nil { + fs.Usage() + os.Exit(1) + } + wd, _ := os.Getwd() + fmt.Printf("working from %s\n", wd) + opts.filenames = extactFilenames(fs.Args()) + for _, filename := range opts.filenames { + ext := filepath.Ext(filename) + snippets := extractSnippets(opts.begin, opts.end, filename) + for _, snippet := range snippets { + outputFile := filepath.Join(opts.outputDir, filepath.Base(filename), snippet.Identifier+opts.extension) + folder := filepath.Dir(outputFile) + err := os.MkdirAll(folder, os.ModePerm) + if err != nil { + log.Printf("cannot mkdir %s: %s", folder, err) + } + f := bytes.NewBuffer(nil) + fmt.Fprintf(f, ``, strings.Join(os.Args[1:], " ")) + fmt.Fprintf(f, "\n\n```%s\n%s```\n", ext, snippet.Text) + err = ioutil.WriteFile(outputFile, f.Bytes(), 0600) + if err != nil { + log.Printf("cannot write %s in %s: %s", filepath.Base(outputFile), folder, err) + } + } + } +} + +type snippet struct { + Identifier string + Text string + Closed bool +} + +func extractSnippets(beginPattern, endPattern, filename string) []snippet { + file, err := os.Open(filename) + if err != nil { + log.Printf("could not open file: %s", err) + os.Exit(1) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + + snippets := []snippet{} + for scanner.Scan() { + line := scanner.Text() + if identifier := matches(line, beginPattern); identifier != "" { + snippets = append(snippets, snippet{ + Identifier: identifier, + }) + continue + } + if identifier := matches(line, endPattern); identifier != "" { + for i := range snippets { + snippet := &snippets[i] + if snippet.Identifier == identifier { + snippet.Closed = true + } + } + continue + } + for i := range snippets { + snippet := &snippets[i] + if snippet.Closed { + continue + } + snippet.Text = snippet.Text + line + "\n" + } + } + return snippets +} + +func matches(s, prefix string) string { + trimmed := strings.TrimSpace(s) + lenDiff := len(s) - len(trimmed) + if strings.HasPrefix(trimmed, prefix) { + return s[len(prefix)+lenDiff:] + } + return "" +} + +// if an entry is a directory all files from directory will be listed. +func extactFilenames(in []string) []string { + out := []string{} + for _, path := range in { + fi, err := os.Stat(path) + if err != nil { + log.Fatalf("%s: %s", path, err) + } + if !fi.IsDir() { + out = append(out, path) + continue + } + files, err := ioutil.ReadDir(path) + if err != nil { + log.Fatalf("could not read directory %s: %s", path, err) + } + for _, file := range files { + if file.IsDir() { + continue + } + out = append(out, file.Name()) + } + } + return in +}