package main import ( "fmt" "html/template" "io/ioutil" "net/http" "net/url" "strings" "sync" "github.com/ghodss/yaml" "github.com/gomarkdown/markdown" "github.com/gomarkdown/markdown/parser" "go.uber.org/multierr" ) var ( mu sync.RWMutex ) type Config struct { Modules []*Module } type Module struct { Import string `json:"import"` Repository string `json:"repository"` Readme string `json:"readme"` ReadmeHTML template.HTML `json:"-"` } func (m Module) name() string { u, err := url.Parse("https://" + m.Import) if err != nil { return "" } return u.Path } func (m *Module) LoadReadme() error { if m.Readme == "" { m.Readme = m.Repository + "/raw/master/README.md" } if !strings.HasPrefix(m.Readme, "http") { m.Readme = "https://" + m.Readme } res, err := http.Get(m.Readme) if err != nil { return err } defer res.Body.Close() if res.StatusCode > 300 { return fmt.Errorf("request failed: %s (%d)", res.Status, res.StatusCode) } b, err := ioutil.ReadAll(res.Body) if err != nil { return err } extensions := parser.CommonExtensions | parser.AutoHeadingIDs mdParser := parser.NewWithExtensions(extensions) m.ReadmeHTML = template.HTML(markdown.ToHTML(b, mdParser, nil)) return nil } func NewModules(path string) (Modules, error) { b, err := ioutil.ReadFile(path) if err != nil { return nil, err } c := &Config{} if err := yaml.Unmarshal(b, c); err != nil { return nil, err } return c.Modules, nil } type Modules []*Module func (m *Modules) Find(name string) (*Module, bool) { for _, v := range *m { if strings.HasPrefix(name, v.name()) { return v, true } } return nil, false } func (m *Modules) LoadReadme() error { mu.Lock() defer mu.Unlock() errs := make(chan error, len(*m)) for _, v := range *m { go func(mod *Module) { errs <- mod.LoadReadme() }(v) } var err error for range *m { err = multierr.Append(err, <-errs) } return err }