main.go

  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}