main.go

  1package main
  2
  3import (
  4	_ "embed"
  5	"fmt"
  6	"os"
  7	"strings"
  8
  9	"github.com/adrg/frontmatter"
 10	"github.com/go-git/go-git/v5"
 11	flag "github.com/spf13/pflag"
 12	"gopkg.in/yaml.v3"
 13)
 14
 15var (
 16	flagHelp          *bool   = flag.BoolP("help", "h", false, "Show the help message")
 17	flagInput         *string = flag.StringP("input", "i", "", "Path to input Markdown")
 18	flagOutput        *string = flag.StringP("output", "o", "", "Path to output PNG")
 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
 24//go:embed font.otf
 25
 26func main() {
 27	flag.Parse()
 28
 29	if *flagHelp {
 30		help()
 31		os.Exit(0)
 32	}
 33
 34	validateFlags()
 35
 36	postTitle, postSubtitle, postDate, postContent := getPostInfo(*flagInput)
 37	postReadTime := getReadTime(postContent)
 38	siteTitle := getSiteTitle()
 39	dateEdited := getGitDate(*flagInput)
 40
 41	// TODO: Render information to image
 42	fmt.Println(postTitle, postSubtitle, postDate, postReadTime, siteTitle, dateEdited)
 43}
 44
 45// Print help message
 46func help() {
 47	fmt.Println("\nUsage: p2c [options]")
 48	fmt.Println("\nOptions:")
 49	flag.PrintDefaults()
 50	fmt.Print(`
 51example: p2c -i input.md -o output.png
 52
 53p2c is meant for use with Hugo.
 54
 55It looks at...
 56- The Markdown file's frontmatter fields for
 57  - title
 58  - subtitle
 59  - date
 60- The site's config.{toml/yaml/yml} fields for
 61  - title (site title)
 62- The git history to determine the date the post was last edited.
 63
 64`)
 65}
 66
 67// Validate flags
 68func validateFlags() {
 69	if *flagInput == "" {
 70		fmt.Println("Error: No input file specified")
 71		os.Exit(1)
 72	}
 73	if *flagOutput == "" {
 74		fmt.Println("Error: No output file specified")
 75		os.Exit(1)
 76	}
 77}
 78
 79// Get the post's title, subtitle, and date
 80func getPostInfo(input string) (string, string, string, string) {
 81	if _, err := os.Stat(input); os.IsNotExist(err) {
 82		fmt.Println("Error: Input file does not exist")
 83		fmt.Println(err)
 84		os.Exit(1)
 85	}
 86
 87	fileContents, err := os.ReadFile(input)
 88	if err != nil {
 89		fmt.Println("Error: Could not read input file")
 90		fmt.Println(err)
 91		os.Exit(1)
 92	}
 93
 94	var fm struct {
 95		Title    string `yaml:"title"`
 96		Subtitle string `yaml:"subtitle"`
 97		Date     string `yaml:"date"`
 98	}
 99
100	content, err := frontmatter.Parse(strings.NewReader(string(fileContents)), &fm)
101	if err != nil {
102		fmt.Println("Error: Could not parse frontmatter")
103		fmt.Println(err)
104		os.Exit(1)
105	}
106
107	fm.Date = strings.Split(fm.Date, "T")[0]
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}