/
main.go
146 lines (136 loc) · 3.84 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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/", "directory in which the files will be generated, note that the directory layout is kept")
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, `<!-- Code generated by snippet-extractor %s; DO NOT EDIT MANUALLY -->`, 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
}