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