1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
package main
import (
_ "embed"
"encoding/gob"
"html/template"
"io"
"log"
"net/http"
"os"
"os/signal"
"runtime/debug"
)
// File for persistent storage
var DataFilename = "ideas.data"
// Version number used for debugging compatibility with the .data file
var Version string
// layout for how the date should be output in html
var DateLayout string = "02/01/2006 on 15:04"
// template for listing out the ideas, should be bundled inside the executable
//
//go:embed ideas.html
var ideas_html string
var data Store
// ToDo's
// - [ ] Add a post
// - [ ] Remove a post
// - [ ] work with funcmaps in templates
// - [ ] Put a reaction on a post
// - [x] Store ideas to a file (encoder/gob)
// - [x] Change the date format printing
// Represents an idea
// CreatedAt is a formatted date string
// out of 5 rating of the idea
// formatted time string with DateLayout
type Idea struct {
Title string
Description string
Author string
CreatedAt string
}
// Data needed through the programm, this data is written to DataFilename for persistent storage
// Is also the format for the Data file
type Store struct {
Version string
Ideas []Idea
}
func GetVersion() string {
buildinfo, ok := debug.ReadBuildInfo()
if !ok {
log.Fatalln("Could not read buildinfo to know package version.")
}
return buildinfo.Main.Version
}
func main() {
Version = GetVersion()
// Create a .data file in the cache directory and decode eventual ideas from it
{
p := os.Getenv("XDG_CACHE_HOME")
// TODO: remove for testing
p = ""
if p == "" {
p = "."
}
DataFilename = p + "/" + DataFilename
f, err := os.OpenFile(DataFilename, os.O_RDONLY|os.O_CREATE, 0666)
if err != nil {
log.Fatalln("Error while opening data file:", err)
}
dec := gob.NewDecoder(f)
if err := dec.Decode(&data); err != io.EOF && err != nil {
log.Fatalln("Error while decoding ideas:", err)
}
log.Println(data)
if data.Version != Version {
log.Fatalf("Version mismatch for datafile@%s != package@%s\n", data.Version, Version)
}
log.Printf("Imported @%s: %d ideas\n", data.Version, len(data.Ideas))
if err := f.Close(); err != nil {
log.Fatalln("Error while closing file:", err)
}
}
// Handle SIGINT
// On program exit it should write all its data to the datafile
// TODO: Instead of recreating the file the new ideas should be appended, a way to achieve this would be that every time a new idea is added it is appended to the file, this way there is no need to ensure this.
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
f, err := os.Create(DataFilename)
if err != nil {
log.Fatalln(err)
}
defer f.Close()
enc := gob.NewEncoder(f)
if err := enc.Encode(data); err != nil {
log.Fatalln(err)
}
log.Println("data saved.")
os.Exit(0)
}()
tmpl, err := template.New("ideas_html").Parse(ideas_html)
if err != nil {
log.Fatalln(err)
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
tmpl.Execute(w, data)
})
log.Println("Listening on http://localhost:8080")
err = http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatalln(err)
}
}
|