-
Notifications
You must be signed in to change notification settings - Fork 0
/
ics.go
153 lines (127 loc) · 3.18 KB
/
ics.go
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright (c) 2020 Anders Bergh.
// License: MIT.
// Package ics provides a simple iCalendar encoder.
package ics
import (
"fmt"
"io"
"log"
"sort"
"strings"
"time"
"github.com/anders/strutils"
)
// Event is a single calendar entry. Supported types are strings.Stringer,
// string and time.Time.
type Event map[string]interface{}
// Calendar holds a list of Events.
type Calendar struct {
Properties map[string]interface{} // VCALENDAR fields
Events []Event // a list of Events
}
// NewCalendar returns a new instance of Calendar with some properties preset.
func NewCalendar() *Calendar {
return &Calendar{
Properties: map[string]interface{}{
"VERSION": "2.0",
"PRODID": "-//github.com/anders/ics",
"CALSCAL": "GREGORIAN",
},
}
}
// Get returns a property.
func (c *Calendar) Get(key string) interface{} {
return c.Properties[key]
}
// Set sets a VCALENDAR property.
func (c *Calendar) Set(key string, value interface{}) {
c.Properties[key] = value
}
// Add adds an object to the calendar.
func (c *Calendar) Add(obj interface{}) error {
switch v := obj.(type) {
case Event:
c.Events = append(c.Events, v)
return nil
default:
return fmt.Errorf("Add(): unsupported type %T", obj)
}
}
// Encode writes a complete calendar to the specified writer.
func (cal Calendar) Encode(w io.Writer) error {
if _, err := io.WriteString(w, "BEGIN:VCALENDAR\r\n"); err != nil {
return err
}
keys := []string{}
for key := range cal.Properties {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
value := cal.Properties[key]
if err := encodeKV(w, key, value); err != nil {
return err
}
}
// TODO: handle other types of iCalendar entries.
for _, ev := range cal.Events {
if err := ev.Encode(w); err != nil {
return err
}
}
if _, err := io.WriteString(w, "END:VCALENDAR\r\n"); err != nil {
return err
}
return nil
}
func encodeKV(w io.Writer, key string, value interface{}) error {
var str string
switch v := value.(type) {
case string:
str = v
case time.Time:
str = v.In(time.UTC).Format("20060102T150405Z")
case fmt.Stringer:
str = v.String()
default:
log.Printf("encodeKV(): key %s: unsupported type %T", key, v)
}
str = strings.Replace(str, "\\", "\\\\", -1)
str = strings.Replace(str, ";", "\\;", -1)
str = strings.Replace(str, ",", "\\,", -1)
str = strings.Replace(str, "\n", "\\n", -1)
key = strings.ToUpper(key)
lines := strutils.SplitLength(key+":"+str, 72)
for i, line := range lines {
if i > 0 {
line = " " + line
}
_, err := io.WriteString(w, line+"\r\n")
if err != nil {
return err
}
}
return nil
}
// Encode writes a single event to the specified Writer.
// Supported values are strings and time.Time, as well as any time that conforms
// to the Stringer interface.
func (ev Event) Encode(w io.Writer) error {
if _, err := io.WriteString(w, "BEGIN:VEVENT\r\n"); err != nil {
return err
}
var keys []string
for key := range ev {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
if err := encodeKV(w, key, ev[key]); err != nil {
return err
}
}
if _, err := io.WriteString(w, "END:VEVENT\r\n"); err != nil {
return err
}
return nil
}