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	return fm.Title, fm.Subtitle, fm.Date, string(content)
108}
109
110// Get the read time of the post
111func getReadTime(content string) int {
112	wordCount := len(strings.Fields(content))
113	return wordCount / 200
114}
115
116// Get the site's title
117func getSiteTitle() string {
118	validConf := ""
119	confs := []string{"config.toml", "config.yaml", "config.yml"}
120	for _, conf := range confs {
121		if _, err := os.Stat(conf); os.IsNotExist(err) {
122			continue
123		}
124		validConf = conf
125	}
126	if validConf == "" {
127		fmt.Println("Error: No valid config file found")
128		os.Exit(1)
129	}
130
131	var t struct {
132		Title string `yaml:"title"`
133	}
134
135	f, err := os.Open(validConf)
136	if err != nil {
137		fmt.Println("Error: Could not open config file")
138		fmt.Println(err)
139		os.Exit(1)
140	}
141	defer f.Close()
142
143	decoder := yaml.NewDecoder(f)
144	err = decoder.Decode(&t)
145	if err != nil {
146		fmt.Println("Error: Could not parse config file")
147		fmt.Println(err)
148		os.Exit(1)
149	}
150
151	return t.Title
152}
153
154// Get the date the post was last edited
155func getGitDate(input string) string {
156	if _, err := os.Stat(input); os.IsNotExist(err) {
157		fmt.Println("Error: Input file does not exist")
158		os.Exit(1)
159	}
160
161	repo, err := git.PlainOpenWithOptions(".", &git.PlainOpenOptions{DetectDotGit: true})
162	if err != nil {
163		fmt.Println("Error: Could not open git repository")
164		fmt.Println(err)
165		os.Exit(1)
166	}
167
168	commitIter, err := repo.Log(&git.LogOptions{FileName: &input})
169	if err != nil {
170		fmt.Println("Error: Could not get git history")
171		fmt.Println(err)
172		os.Exit(1)
173	}
174
175	commit, err := commitIter.Next()
176	if err != nil {
177		fmt.Println("Error: Could not get git history")
178		fmt.Println(err)
179		os.Exit(1)
180	}
181
182	return commit.Committer.When.Format("2006-01-02")
183}