1package main
2
3import (
4 "fmt"
5 "os"
6 "strings"
7
8 "github.com/adrg/frontmatter"
9 "github.com/go-git/go-git/v5"
10 flag "github.com/spf13/pflag"
11 "gopkg.in/yaml.v3"
12)
13
14var (
15 flagHelp *bool = flag.BoolP("help", "h", false, "Show the help message")
16 flagInput *string = flag.StringP("input", "i", "", "Path to input Markdown")
17 flagOutput *string = flag.StringP("output", "o", "", "Path to output PNG")
18 flagFont *string = flag.StringP("font", "f", "", "Name of font used in the cover image")
19 flagMetaSize *int = flag.IntP("metasize", "m", 12, "Size of font for meta information")
20 flagPostTitleSize *int = flag.IntP("posttitlesize", "p", 14, "Size of font for post title")
21 flagSiteTitleSize *int = flag.IntP("sitetitlesize", "s", 10, "Size of font for site title")
22)
23
24func main() {
25 flag.Parse()
26
27 if *flagHelp {
28 help()
29 os.Exit(0)
30 }
31
32 validateFlags()
33
34 postTitle, postSubtitle, postDate, postContent := getPostInfo(*flagInput)
35 postReadTime := getReadTime(postContent)
36 siteTitle := getSiteTitle()
37 dateEdited := getGitDate(*flagInput)
38
39 // TODO: Render information to image
40 fmt.Println(postTitle, postSubtitle, postDate, postReadTime, siteTitle, dateEdited)
41}
42
43// Print help message
44func help() {
45 fmt.Println("\nUsage: p2c [options]")
46 fmt.Println("\nOptions:")
47 flag.PrintDefaults()
48 fmt.Print(`
49example: p2c -i input.md -o output.png -f "Liberation Sans"
50
51p2c is meant for use with Hugo.
52
53It looks at...
54- The Markdown file's frontmatter fields for
55 - title
56 - subtitle
57 - date
58- The site's config.{toml/yaml/yml} fields for
59 - title (site title)
60- The git history to determine the date the post was last edited.
61
62`)
63}
64
65// Validate flags
66func validateFlags() {
67 if *flagInput == "" {
68 fmt.Println("Error: No input file specified")
69 os.Exit(1)
70 }
71 if *flagOutput == "" {
72 fmt.Println("Error: No output file specified")
73 os.Exit(1)
74 }
75 if *flagFont == "" {
76 fmt.Println("Error: No font specified")
77 os.Exit(1)
78 }
79}
80
81// Get the post's title, subtitle, and date
82func getPostInfo(input string) (string, string, string, string) {
83 if _, err := os.Stat(input); os.IsNotExist(err) {
84 fmt.Println("Error: Input file does not exist")
85 fmt.Println(err)
86 os.Exit(1)
87 }
88
89 fileContents, err := os.ReadFile(input)
90 if err != nil {
91 fmt.Println("Error: Could not read input file")
92 fmt.Println(err)
93 os.Exit(1)
94 }
95
96 var fm struct {
97 Title string `yaml:"title"`
98 Subtitle string `yaml:"subtitle"`
99 Date string `yaml:"date"`
100 }
101
102 content, err := frontmatter.Parse(strings.NewReader(string(fileContents)), &fm)
103 if err != nil {
104 fmt.Println("Error: Could not parse frontmatter")
105 fmt.Println(err)
106 os.Exit(1)
107 }
108
109 return fm.Title, fm.Subtitle, fm.Date, string(content)
110}
111
112// Get the read time of the post
113func getReadTime(content string) int {
114 wordCount := len(strings.Fields(content))
115 return wordCount / 200
116}
117
118// Get the site's title
119func getSiteTitle() string {
120 validConf := ""
121 confs := []string{"config.toml", "config.yaml", "config.yml"}
122 for _, conf := range confs {
123 if _, err := os.Stat(conf); os.IsNotExist(err) {
124 continue
125 }
126 validConf = conf
127 }
128 if validConf == "" {
129 fmt.Println("Error: No valid config file found")
130 os.Exit(1)
131 }
132
133 var t struct {
134 Title string `yaml:"title"`
135 }
136
137 f, err := os.Open(validConf)
138 if err != nil {
139 fmt.Println("Error: Could not open config file")
140 fmt.Println(err)
141 os.Exit(1)
142 }
143 defer f.Close()
144
145 decoder := yaml.NewDecoder(f)
146 err = decoder.Decode(&t)
147 if err != nil {
148 fmt.Println("Error: Could not parse config file")
149 fmt.Println(err)
150 os.Exit(1)
151 }
152
153 return t.Title
154}
155
156// Get the date the post was last edited
157func getGitDate(input string) string {
158 if _, err := os.Stat(input); os.IsNotExist(err) {
159 fmt.Println("Error: Input file does not exist")
160 os.Exit(1)
161 }
162
163 repo, err := git.PlainOpenWithOptions(".", &git.PlainOpenOptions{DetectDotGit: true})
164 if err != nil {
165 fmt.Println("Error: Could not open git repository")
166 fmt.Println(err)
167 os.Exit(1)
168 }
169
170 commitIter, err := repo.Log(&git.LogOptions{FileName: &input})
171 if err != nil {
172 fmt.Println("Error: Could not get git history")
173 fmt.Println(err)
174 os.Exit(1)
175 }
176
177 commit, err := commitIter.Next()
178 if err != nil {
179 fmt.Println("Error: Could not get git history")
180 fmt.Println(err)
181 os.Exit(1)
182 }
183
184 return commit.Committer.When.Format("2006-01-02")
185}