Skip to content

Commit c34f37d

Browse files
Extract the structs into their own files
Signed-off-by: Chris Cummer <[email protected]>
1 parent 112857b commit c34f37d

11 files changed

+1050
-994
lines changed

main.go

+423
Large diffs are not rendered by default.

src/colours.go

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package src
2+
3+
import "fmt"
4+
5+
var (
6+
// Blue writes blue text
7+
Blue = Colour("\033[1;36m%s\033[0m")
8+
9+
// Green writes green text
10+
Green = Colour("\033[1;32m%s\033[0m")
11+
12+
// Red writes red text
13+
Red = Colour("\033[1;31m%s\033[0m")
14+
)
15+
16+
// Colour returns a function that defines a printable colour string
17+
func Colour(colorString string) func(...interface{}) string {
18+
sprint := func(args ...interface{}) string {
19+
return fmt.Sprintf(colorString,
20+
fmt.Sprint(args...))
21+
}
22+
return sprint
23+
}

src/configuration.go

+185
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
package src
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"io/ioutil"
7+
"os"
8+
"path/filepath"
9+
10+
"github.com/olebedev/config"
11+
)
12+
13+
const (
14+
defaultConfig = `---
15+
commitMessage: "build, save, push"
16+
committerEmail: [email protected]
17+
committerName: "TIL Autobot"
18+
editor: ""
19+
targetDirectory:
20+
a: "~/Documents/tilblog"
21+
`
22+
23+
tilConfigDir = "~/.config/til/"
24+
tilConfigFile = "config.yml"
25+
)
26+
27+
const (
28+
errConfigDirCreate = "could not create the configuration directory"
29+
errConfigExpandPath = "could not expand the config directory"
30+
errConfigFileAssert = "could not assert the configuration file exists"
31+
errConfigFileCreate = "could not create the configuration file"
32+
errConfigFileWrite = "could not write the configuration file"
33+
errConfigPathEmpty = "config path cannot be empty"
34+
)
35+
36+
// GlobalConfig holds and makes available all the user-configurable
37+
// settings that are stored in the config file.
38+
// (I know! Friends don't let friends use globals, but since I have
39+
// no friends working on this, there's no one around to stop me)
40+
var GlobalConfig *config.Config
41+
42+
// Config handles all things to do with configuration
43+
type Config struct{}
44+
45+
// Load reads the configuration file
46+
func (c *Config) Load() {
47+
makeConfigDir()
48+
makeConfigFile()
49+
50+
GlobalConfig = readConfigFile()
51+
}
52+
53+
// getConfigDir returns the string path to the directory that should
54+
// contain the configuration file.
55+
// It tries to be XDG-compatible
56+
func getConfigDir() (string, error) {
57+
cDir := os.Getenv("XDG_CONFIG_HOME")
58+
if cDir == "" {
59+
cDir = tilConfigDir
60+
}
61+
62+
// If the user hasn't changed the default path then we expect it to start
63+
// with a tilde (the user's home), and we need to turn that into an
64+
// absolute path. If it does not start with a '~' then we assume the
65+
// user has set their $XDG_CONFIG_HOME to something specific, and we
66+
// do not mess with it (because doing so makes the archlinux people
67+
// very cranky)
68+
if cDir[0] != '~' {
69+
return cDir, nil
70+
}
71+
72+
dir, err := os.UserHomeDir()
73+
if err != nil {
74+
return "", errors.New(errConfigExpandPath)
75+
}
76+
77+
cDir = filepath.Join(dir, cDir[1:])
78+
79+
if cDir == "" {
80+
return "", errors.New(errConfigPathEmpty)
81+
}
82+
83+
return cDir, nil
84+
}
85+
86+
// GetConfigFilePath returns the string path to the configuration file
87+
func GetConfigFilePath() (string, error) {
88+
cDir, err := getConfigDir()
89+
if err != nil {
90+
return "", err
91+
}
92+
93+
if cDir == "" {
94+
return "", errors.New(errConfigPathEmpty)
95+
}
96+
97+
return fmt.Sprintf("%s/%s", cDir, tilConfigFile), nil
98+
}
99+
100+
func makeConfigDir() {
101+
cDir, err := getConfigDir()
102+
if err != nil {
103+
Defeat(err)
104+
}
105+
106+
if cDir == "" {
107+
Defeat(errors.New(errConfigPathEmpty))
108+
}
109+
110+
if _, err := os.Stat(cDir); os.IsNotExist(err) {
111+
err := os.MkdirAll(cDir, os.ModePerm)
112+
if err != nil {
113+
Defeat(errors.New(errConfigDirCreate))
114+
}
115+
116+
Progress(fmt.Sprintf("created %s", cDir))
117+
}
118+
}
119+
120+
func makeConfigFile() {
121+
cPath, err := GetConfigFilePath()
122+
if err != nil {
123+
Defeat(err)
124+
}
125+
126+
if cPath == "" {
127+
Defeat(errors.New(errConfigPathEmpty))
128+
}
129+
130+
_, err = os.Stat(cPath)
131+
132+
if err != nil {
133+
// Something went wrong trying to find the config file.
134+
// Let's see if we can figure out what happened
135+
if os.IsNotExist(err) {
136+
// Ah, the config file does not exist, which is probably fine
137+
_, err = os.Create(cPath)
138+
if err != nil {
139+
// That was not fine
140+
Defeat(errors.New(errConfigFileCreate))
141+
}
142+
143+
} else {
144+
// But wait, it's some kind of other error. What kind?
145+
// I dunno, but it's probably bad so die
146+
Defeat(err)
147+
}
148+
}
149+
150+
// Let's double-check that the file's there now
151+
fileInfo, err := os.Stat(cPath)
152+
if err != nil {
153+
Defeat(errors.New(errConfigFileAssert))
154+
}
155+
156+
// Write the default config, but only if the file is empty.
157+
// Don't want to stop on any non-default values the user has written in there
158+
if fileInfo.Size() == 0 {
159+
if ioutil.WriteFile(cPath, []byte(defaultConfig), 0600) != nil {
160+
Defeat(errors.New(errConfigFileWrite))
161+
}
162+
163+
Progress(fmt.Sprintf("created %s", cPath))
164+
}
165+
}
166+
167+
// readConfigFile reads the contents of the config file and jams them
168+
// into the global config variable
169+
func readConfigFile() *config.Config {
170+
cPath, err := GetConfigFilePath()
171+
if err != nil {
172+
Defeat(err)
173+
}
174+
175+
if cPath == "" {
176+
Defeat(errors.New(errConfigPathEmpty))
177+
}
178+
179+
cfg, err := config.ParseYamlFile(cPath)
180+
if err != nil {
181+
Defeat(err)
182+
}
183+
184+
return cfg
185+
}

src/logging.go

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package src
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"os"
7+
)
8+
9+
// LL is a go routine-safe implementation of Logger
10+
// (More globals! This is getting crazy)
11+
var LL *log.Logger
12+
13+
// Defeat writes out an error message
14+
func Defeat(err error) {
15+
LL.Fatal(fmt.Sprintf("%s %s", Red("✘"), err.Error()))
16+
}
17+
18+
// Info writes out an informative message
19+
func Info(msg string) {
20+
LL.Print(fmt.Sprintf("%s %s", Green("->"), msg))
21+
}
22+
23+
// Progress writes out a progress status message
24+
func Progress(msg string) {
25+
LL.Print(fmt.Sprintf("\t%s %s\n", Blue("->"), msg))
26+
}
27+
28+
// Victory writes out a victorious final message and then expires dramatically
29+
func Victory(msg string) {
30+
LL.Print(fmt.Sprintf("%s %s", Green("✓"), msg))
31+
os.Exit(0)
32+
}

src/page.go

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package src
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"path/filepath"
7+
"strings"
8+
"time"
9+
)
10+
11+
const (
12+
// A custom datetime format that plays nicely with GitHub Pages filename restrictions
13+
ghFriendlyDateFormat = "2006-01-02T15-04-05"
14+
15+
// FileExtension defines the extension to write on the generated file
16+
FileExtension = "md"
17+
)
18+
19+
// Page represents a TIL page
20+
type Page struct {
21+
Content string `fm:"content" yaml:"-"`
22+
Date string `yaml:"date"`
23+
FilePath string `yaml:"filepath"`
24+
TagsStr string `yaml:"tags"`
25+
Title string `yaml:"title"`
26+
}
27+
28+
// NewPage creates and returns an instance of page
29+
func NewPage(title string, targetDir string) *Page {
30+
date := time.Now()
31+
32+
page := &Page{
33+
Date: date.Format(time.RFC3339),
34+
FilePath: fmt.Sprintf(
35+
"%s/%s-%s.%s",
36+
targetDir,
37+
date.Format(ghFriendlyDateFormat),
38+
strings.ReplaceAll(strings.ToLower(title), " ", "-"),
39+
FileExtension,
40+
),
41+
Title: title,
42+
}
43+
44+
page.Save()
45+
46+
return page
47+
}
48+
49+
// CreatedAt returns a time instance representing when the page was created
50+
func (page *Page) CreatedAt() time.Time {
51+
date, err := time.Parse(time.RFC3339, page.Date)
52+
if err != nil {
53+
return time.Time{}
54+
}
55+
56+
return date
57+
}
58+
59+
// CreatedMonth returns the month the page was created
60+
func (page *Page) CreatedMonth() time.Month {
61+
if page.CreatedAt().IsZero() {
62+
return 0
63+
}
64+
65+
return page.CreatedAt().Month()
66+
}
67+
68+
// FrontMatter returns the front-matter of the page
69+
func (page *Page) FrontMatter() string {
70+
return fmt.Sprintf(
71+
"---\ndate: %s\ntitle: %s\ntags: %s\n---\n\n",
72+
page.Date,
73+
page.Title,
74+
page.TagsStr,
75+
)
76+
}
77+
78+
// IsContentPage returns true if the page is a valid entry page, false if it is not
79+
func (page *Page) IsContentPage() bool {
80+
return page.Title != ""
81+
}
82+
83+
// Link returns a link string suitable for embedding in a Markdown page
84+
func (page *Page) Link() string {
85+
return fmt.Sprintf(
86+
"<code>%s</code> [%s](%s)",
87+
page.PrettyDate(),
88+
page.Title,
89+
filepath.Base(page.FilePath),
90+
)
91+
}
92+
93+
// PrettyDate returns a human-friendly representation of the CreatedAt date
94+
func (page *Page) PrettyDate() string {
95+
return page.CreatedAt().Format("Jan 02, 2006")
96+
}
97+
98+
// Save writes the content of the page to file
99+
func (page *Page) Save() {
100+
pageSrc := page.FrontMatter()
101+
pageSrc += fmt.Sprintf("# %s\n\n", page.Title)
102+
103+
err := ioutil.WriteFile(page.FilePath, []byte(pageSrc), 0644)
104+
if err != nil {
105+
Defeat(err)
106+
}
107+
}
108+
109+
// Tags returns a slice of tags assigned to this page
110+
func (page *Page) Tags() []*Tag {
111+
tags := []*Tag{}
112+
113+
names := strings.Split(page.TagsStr, ",")
114+
for _, name := range names {
115+
tags = append(tags, NewTag(name, page))
116+
}
117+
118+
return tags
119+
}

src/page_elements.go

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package src
2+
3+
import (
4+
"fmt"
5+
"time"
6+
)
7+
8+
func Footer() string {
9+
return fmt.Sprintf(
10+
"<sup><sub>generated %s by <a href='https://github.com/senorprogrammer/til'>til</a></sub></sup>\n",
11+
time.Now().Format("2 Jan 2006 15:04:05"),
12+
)
13+
}

0 commit comments

Comments
 (0)