1package wfimport 2 3import ( 4 "io/ioutil" 5 "net/http" 6 "os" 7 "path/filepath" 8 "regexp" 9 "strings" 10 11 "github.com/hashicorp/go-multierror" 12 "github.com/writeas/go-writeas/v2" 13) 14 15// FromDirectoryMatch reads all text and markdown files in path that match the 16// pattern returning the parsed posts and an error if any. 17// 18// The pattern should be a valid regex, for more details see 19// https://golang.org/s/re2syntax or run `go doc regexp/syntax` 20func FromDirectoryMatch(path, pattern string) ([]*writeas.PostParams, error) { 21 return fromDirectory(path, pattern) 22} 23 24// FromDirectory reads all text and markdown files in path and returns the 25// parsed posts and an error if any. 26func FromDirectory(path string) ([]*writeas.PostParams, error) { 27 return fromDirectory(path, "") 28} 29 30// fromDirectory takes an 'optional' pattern, if an empty string is passed 31// all valid txt and md files will be included under path. 32// Otherwise pattern should be a valid regex per MatchFromDirectory 33func fromDirectory(path, pattern string) ([]*writeas.PostParams, error) { 34 if pattern == "" { 35 pattern = "." 36 } 37 rx, err := regexp.Compile(pattern) 38 if err != nil { 39 return nil, err 40 } 41 list, err := ioutil.ReadDir(path) 42 if err != nil { 43 return nil, err 44 } 45 if len(list) == 0 { 46 return nil, ErrEmptyDir 47 } 48 49 var postErrors error 50 posts := []*writeas.PostParams{} 51 for _, f := range list { 52 if !f.IsDir() { 53 filename := f.Name() 54 if rx.MatchString(filename) { 55 post, err := FromFile(filepath.Join(path, filename)) 56 if err != nil { 57 postErrors = multierror.Append(postErrors, err) 58 continue 59 } 60 61 posts = append(posts, post) 62 } 63 } 64 } 65 return posts, postErrors 66} 67 68// FromFile reads in a file from path and returns the parsed post and an error 69// if any. The title will be extracted from the first markdown level 1 header. 70func FromFile(path string) (*writeas.PostParams, error) { 71 b, err := ioutil.ReadFile(path) 72 if err != nil { 73 return nil, err 74 } 75 76 info, err := os.Stat(path) 77 if err != nil { 78 return nil, err 79 } 80 81 p, err := fromBytes(b) 82 if err != nil { 83 return nil, err 84 } 85 created := info.ModTime() 86 p.Created = &created 87 88 return p, nil 89} 90 91func fromBytes(b []byte) (*writeas.PostParams, error) { 92 if len(b) == 0 { 93 return nil, ErrEmptyFile 94 } 95 96 if contentType := http.DetectContentType(b); !strings.HasPrefix(contentType, "text/") { 97 return nil, ErrInvalidContentType 98 } 99 100 title, body := extractTitle(string(b)) 101 post := writeas.PostParams{ 102 Title: title, 103 Content: body, 104 } 105 106 return &post, nil 107} 108 109// TODO: copied from writeas/web-core/posts due to errors with package imports 110// maybe also find a way to grab the first line as a title in plain text files 111func extractTitle(content string) (title string, body string) { 112 if hashIndex := strings.Index(content, "# "); hashIndex == 0 { 113 eol := strings.IndexRune(content, '\n') 114 // First line should start with # and end with \n 115 if eol != -1 { 116 body = strings.TrimLeft(content[eol:], " \t\n\r") 117 title = content[len("# "):eol] 118 return 119 } 120 } 121 body = content 122 return 123} 124