/
decode.go
141 lines (130 loc) · 2.74 KB
/
decode.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
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package ics provides support for reading Apple's iCalendar file format.
package ics
import (
"bufio"
"bytes"
"errors"
"io"
"sort"
"strings"
"time"
)
type Calendar struct {
Event []*Event
}
type Event struct {
UID string
Start, End time.Time
Summary, Location, Description string
}
func Decode(rd io.Reader) (c *Calendar, err error) {
r := bufio.NewReader(rd)
for {
key, value, err := decodeLine(r)
if err != nil {
return nil, err
}
if key == "BEGIN" {
if c == nil {
if value != "VCALENDAR" {
return nil, errors.New("didn't find BEGIN:VCALENDAR")
}
c = new(Calendar)
}
if value == "VEVENT" {
e, err := decodeEvent(r)
if err != nil {
return nil, err
}
c.Event = append(c.Event, e)
}
}
if key == "END" && value == "VCALENDAR" {
break
}
}
sort.Sort(eventList(c.Event))
return c, nil
}
func decodeEvent(r *bufio.Reader) (*Event, error) {
e := new(Event)
var key, value string
var err error
for {
if err != nil {
return nil, err
}
key, value, err = decodeLine(r)
switch key {
case "END":
if value != "VEVENT" {
return nil, errors.New("unexpected END value")
}
return e, nil
case "UID":
e.UID = value
case "DTSTART":
e.Start, err = decodeTime(value)
case "DTEND":
e.End, err = decodeTime(value)
case "SUMMARY":
e.Summary = value
case "LOCATION":
e.Location = value
case "DESCRIPTION":
e.Description = value
}
}
panic("unreachable")
}
func decodeTime(value string) (time.Time, error) {
const layout = "20060102T150405Z"
return time.Parse(layout, value)
}
func decodeLine(r *bufio.Reader) (key, value string, err error) {
var buf bytes.Buffer
for {
// get full line
b, isPrefix, err := r.ReadLine()
if err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return "", "", err
}
if isPrefix {
return "", "", errors.New("unexpected long line")
}
if len(b) == 0 {
return "", "", errors.New("unexpected blank line")
}
if b[0] == ' ' {
b = b[1:]
}
buf.Write(b)
b, err = r.Peek(1)
if err != nil || b[0] != ' ' {
break
}
}
p := strings.SplitN(buf.String(), ":", 2)
if len(p) != 2 {
return "", "", errors.New("bad line, couldn't find key:value")
}
return p[0], p[1], nil
}
type eventList []*Event
func (l eventList) Less(i, j int) bool {
if l[i].Start.IsZero() {
return true
}
if l[j].Start.IsZero() {
return false
}
return l[i].Start.Before(l[j].Start)
}
func (l eventList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l eventList) Len() int { return len(l) }