feat: add hhu calendar
This commit is contained in:
commit
3e4c6c77cb
35 changed files with 4116 additions and 0 deletions
1
src/vendor/github.com/jordic/goics/.gitignore
generated
vendored
Normal file
1
src/vendor/github.com/jordic/goics/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
.idea
|
||||
17
src/vendor/github.com/jordic/goics/.travis.yml
generated
vendored
Normal file
17
src/vendor/github.com/jordic/goics/.travis.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
language: go
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- go: "tip"
|
||||
- go: "1.x"
|
||||
- go: "1.16"
|
||||
- go: "1.15"
|
||||
- go: "1.14"
|
||||
- go: "1.13"
|
||||
- go: "1.12"
|
||||
- go: "1.11"
|
||||
- go: "1.10"
|
||||
- go: "1.9"
|
||||
- go: "1.8"
|
||||
allow_failures:
|
||||
- go: tip
|
||||
22
src/vendor/github.com/jordic/goics/LICENSE
generated
vendored
Normal file
22
src/vendor/github.com/jordic/goics/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Jordi Collell
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
44
src/vendor/github.com/jordic/goics/README.md
generated
vendored
Normal file
44
src/vendor/github.com/jordic/goics/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
|
||||
### A go toolkit for decoding, encoding icalendar ics ical files
|
||||
|
||||
Alpha status
|
||||
|
||||
[](https://travis-ci.org/jordic/goics)
|
||||
|
||||
After trying to decode some .ics files and review available go packages, I decided to start writing this pacakge.
|
||||
|
||||
First attempt was from a fixed structure, similar to that needed. Later, I started to investigate the format and discovered that it is a pain, and has many variants, depending on who implements it. For this reason I evolved it to a tookit for decode and encode the format.
|
||||
|
||||
**Check examples dir for user cases:**
|
||||
|
||||
[Demo app encoding an ical, using a mysql DB source](examples/mysqlsource)
|
||||
|
||||
|
||||
Features implemented:
|
||||
|
||||
- Parsing and writing of vevent, not completly..
|
||||
- No recursive events,
|
||||
- And no alarms inside events
|
||||
|
||||
|
||||
Follows:
|
||||
http://tools.ietf.org/html/rfc5545
|
||||
|
||||
|
||||
TODO
|
||||
--
|
||||
|
||||
Integrate testing from:
|
||||
https://github.com/libical/libical
|
||||
|
||||
|
||||
CHANGELOG:
|
||||
--
|
||||
|
||||
- v00. First api traial
|
||||
- v01. Api evolves to a ical toolkit
|
||||
- v02. Added example of integration with a mysql db.
|
||||
|
||||
Thanks to:
|
||||
Joe Shaw Reviewing first revision.
|
||||
|
||||
178
src/vendor/github.com/jordic/goics/decoder.go
generated
vendored
Normal file
178
src/vendor/github.com/jordic/goics/decoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
package goics
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
keySep = ":"
|
||||
vBegin = "BEGIN"
|
||||
vCalendar = "VCALENDAR"
|
||||
vEnd = "END"
|
||||
vEvent = "VEVENT"
|
||||
|
||||
maxLineRead = 65
|
||||
)
|
||||
|
||||
// Errors
|
||||
var (
|
||||
ErrCalendarNotFound = errors.New("vCalendar not found")
|
||||
ErrParseEndCalendar = errors.New("wrong format END:VCALENDAR not Found")
|
||||
)
|
||||
|
||||
type decoder struct {
|
||||
scanner *bufio.Scanner
|
||||
err error
|
||||
Calendar *Calendar
|
||||
currentEvent *Event
|
||||
nextFn stateFn
|
||||
prevFn stateFn
|
||||
current string
|
||||
buffered string
|
||||
line int
|
||||
}
|
||||
|
||||
type stateFn func(*decoder)
|
||||
|
||||
// NewDecoder creates an instance of de decoder
|
||||
func NewDecoder(r io.Reader) *decoder {
|
||||
d := &decoder{
|
||||
scanner: bufio.NewScanner(r),
|
||||
nextFn: decodeInit,
|
||||
line: 0,
|
||||
buffered: "",
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *decoder) Decode(c ICalConsumer) error {
|
||||
d.next()
|
||||
if d.Calendar == nil {
|
||||
d.err = ErrCalendarNotFound
|
||||
d.Calendar = &Calendar{}
|
||||
}
|
||||
// If theres no error but, nextFn is not reset
|
||||
// last element not closed
|
||||
if d.nextFn != nil && d.err == nil {
|
||||
d.err = ErrParseEndCalendar
|
||||
}
|
||||
if d.err != nil {
|
||||
return d.err
|
||||
}
|
||||
|
||||
d.err = c.ConsumeICal(d.Calendar, d.err)
|
||||
return d.err
|
||||
}
|
||||
|
||||
// Lines processed. If Decoder reports an error.
|
||||
// Error
|
||||
func (d *decoder) Lines() int {
|
||||
return d.line
|
||||
}
|
||||
|
||||
// Current Line content
|
||||
func (d *decoder) CurrentLine() string {
|
||||
return d.current
|
||||
}
|
||||
|
||||
// Advances a new line in the decoder
|
||||
// And calls the next stateFunc
|
||||
// checks if next line is continuation line
|
||||
func (d *decoder) next() {
|
||||
// If there's not buffered line
|
||||
if d.buffered == "" {
|
||||
res := d.scanner.Scan()
|
||||
if true != res {
|
||||
d.err = d.scanner.Err()
|
||||
return
|
||||
}
|
||||
d.line++
|
||||
d.current = d.scanner.Text()
|
||||
} else {
|
||||
d.current = d.buffered
|
||||
d.buffered = ""
|
||||
}
|
||||
|
||||
for d.scanner.Scan() {
|
||||
d.line++
|
||||
line := d.scanner.Text()
|
||||
if strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t") {
|
||||
d.current = d.current + line[1:]
|
||||
} else {
|
||||
// If is not a continuation line, buffer it, for the
|
||||
// next call.
|
||||
d.buffered = line
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
d.err = d.scanner.Err()
|
||||
|
||||
if d.nextFn != nil {
|
||||
d.nextFn(d)
|
||||
}
|
||||
}
|
||||
|
||||
func decodeInit(d *decoder) {
|
||||
node := DecodeLine(d.current)
|
||||
if node.Key == vBegin && node.Val == vCalendar {
|
||||
d.Calendar = &Calendar{
|
||||
Data: make(map[string]*IcsNode),
|
||||
}
|
||||
d.prevFn = decodeInit
|
||||
d.nextFn = decodeInsideCal
|
||||
d.next()
|
||||
return
|
||||
}
|
||||
d.next()
|
||||
}
|
||||
|
||||
func decodeInsideCal(d *decoder) {
|
||||
node := DecodeLine(d.current)
|
||||
switch {
|
||||
case node.Key == vBegin && node.Val == vEvent:
|
||||
d.currentEvent = &Event{
|
||||
Data: make(map[string]*IcsNode),
|
||||
List: make(map[string][]*IcsNode),
|
||||
}
|
||||
d.nextFn = decodeInsideEvent
|
||||
d.prevFn = decodeInsideCal
|
||||
case node.Key == vEnd && node.Val == vCalendar:
|
||||
d.nextFn = nil
|
||||
default:
|
||||
d.Calendar.Data[node.Key] = node
|
||||
}
|
||||
d.next()
|
||||
}
|
||||
|
||||
func decodeInsideEvent(d *decoder) {
|
||||
|
||||
node := DecodeLine(d.current)
|
||||
if node.Key == vEnd && node.Val == vEvent {
|
||||
// Come back to parent node
|
||||
d.nextFn = d.prevFn
|
||||
d.Calendar.Events = append(d.Calendar.Events, d.currentEvent)
|
||||
d.next()
|
||||
return
|
||||
}
|
||||
//@todo handle Valarm
|
||||
//@todo handle error if we found a startevent without closing pass one
|
||||
// #2 handle multiple equal keys. ej. Attendee
|
||||
// List keys already set
|
||||
if _, ok := d.currentEvent.List[node.Key]; ok {
|
||||
d.currentEvent.List[node.Key] = append(d.currentEvent.List[node.Key], node)
|
||||
} else {
|
||||
// Multiple key detected...
|
||||
if val, ok := d.currentEvent.Data[node.Key]; ok {
|
||||
d.currentEvent.List[node.Key] = []*IcsNode{val, node}
|
||||
delete(d.currentEvent.Data, node.Key)
|
||||
} else {
|
||||
d.currentEvent.Data[node.Key] = node
|
||||
}
|
||||
}
|
||||
d.next()
|
||||
|
||||
}
|
||||
120
src/vendor/github.com/jordic/goics/decoder_line.go
generated
vendored
Normal file
120
src/vendor/github.com/jordic/goics/decoder_line.go
generated
vendored
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
package goics
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
vParamSep = ";"
|
||||
)
|
||||
|
||||
// IcsNode is a basic token.., with, key, val, and extra params
|
||||
// to define the type of val.
|
||||
type IcsNode struct {
|
||||
Key string
|
||||
Val string
|
||||
Params map[string]string
|
||||
}
|
||||
|
||||
// ParamsLen returns how many params has a token
|
||||
func (n *IcsNode) ParamsLen() int {
|
||||
if n.Params == nil {
|
||||
return 0
|
||||
}
|
||||
return len(n.Params)
|
||||
}
|
||||
|
||||
// GetOneParam resturns the first param found
|
||||
// usefull when you know that there is a only
|
||||
// one param token
|
||||
func (n *IcsNode) GetOneParam() (string, string) {
|
||||
if n.ParamsLen() == 0 {
|
||||
return "", ""
|
||||
}
|
||||
var key, val string
|
||||
for k, v := range n.Params {
|
||||
key, val = k, v
|
||||
break
|
||||
}
|
||||
return key, val
|
||||
}
|
||||
|
||||
// DecodeLine extracts key, val and extra params from a line
|
||||
func DecodeLine(line string) *IcsNode {
|
||||
if strings.Contains(line, keySep) == false {
|
||||
return &IcsNode{}
|
||||
}
|
||||
key, val := getKeyVal(line)
|
||||
//@todo test if val containes , multipleparams
|
||||
if strings.Contains(key, vParamSep) == false {
|
||||
return &IcsNode{
|
||||
Key: key,
|
||||
Val: val,
|
||||
}
|
||||
}
|
||||
// Extract key
|
||||
firstParam := strings.Index(key, vParamSep)
|
||||
realkey := key[0:firstParam]
|
||||
n := &IcsNode{
|
||||
Key: realkey,
|
||||
Val: val,
|
||||
}
|
||||
// Extract params
|
||||
params := key[firstParam+1:]
|
||||
n.Params = decodeParams(params)
|
||||
return n
|
||||
}
|
||||
|
||||
// decode extra params linked in key val in the form
|
||||
// key;param1=val1:val
|
||||
func decodeParams(arr string) map[string]string {
|
||||
|
||||
p := make(map[string]string)
|
||||
var isQuoted = false
|
||||
var isParam = true
|
||||
var curParam string
|
||||
var curVal string
|
||||
for _, c := range arr {
|
||||
switch {
|
||||
// if string is quoted, wait till next quote
|
||||
// and capture content
|
||||
case c == '"':
|
||||
if isQuoted == false {
|
||||
isQuoted = true
|
||||
} else {
|
||||
p[curParam] = curVal
|
||||
isQuoted = false
|
||||
}
|
||||
case c == '=' && isQuoted == false:
|
||||
isParam = false
|
||||
case c == ';' && isQuoted == false:
|
||||
isParam = true
|
||||
p[curParam] = curVal
|
||||
curParam = ""
|
||||
curVal = ""
|
||||
default:
|
||||
if isParam {
|
||||
curParam = curParam + string(c)
|
||||
} else {
|
||||
curVal = curVal + string(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
p[curParam] = curVal
|
||||
return p
|
||||
|
||||
}
|
||||
|
||||
// Returns a key, val... for a line..
|
||||
func getKeyVal(s string) (key, value string) {
|
||||
p := strings.SplitN(s, keySep, 2)
|
||||
return p[0], icsReplacer.Replace(p[1])
|
||||
}
|
||||
|
||||
var icsReplacer = strings.NewReplacer(
|
||||
`\,`, ",",
|
||||
`\;`, ";",
|
||||
`\\`, `\`,
|
||||
`\n`, "\n",
|
||||
`\N`, "\n",
|
||||
)
|
||||
49
src/vendor/github.com/jordic/goics/decoder_types.go
generated
vendored
Normal file
49
src/vendor/github.com/jordic/goics/decoder_types.go
generated
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package goics
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// DateDecode Decodes a date in the distincts formats
|
||||
func (n *IcsNode) DateDecode() (time.Time, error) {
|
||||
|
||||
// DTEND;VALUE=DATE:20140406
|
||||
if val, ok := n.Params["VALUE"]; ok {
|
||||
switch {
|
||||
case val == "DATE":
|
||||
t, err := time.Parse("20060102", n.Val)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
return t, nil
|
||||
case val == "DATE-TIME":
|
||||
t, err := time.Parse("20060102T150405", n.Val)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
// DTSTART;TZID=Europe/Paris:20140116T120000
|
||||
if val, ok := n.Params["TZID"]; ok {
|
||||
loc, err := time.LoadLocation(val)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
t, err := time.ParseInLocation("20060102T150405", n.Val, loc)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
//DTSTART:19980119T070000Z utf datetime
|
||||
if len(n.Val) == 16 {
|
||||
t, err := time.Parse("20060102T150405Z", n.Val)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
return time.Time{}, nil
|
||||
}
|
||||
98
src/vendor/github.com/jordic/goics/doc.go
generated
vendored
Normal file
98
src/vendor/github.com/jordic/goics/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
Package goics is a toolkit for encoding and decoding ics/Ical/icalendar files.
|
||||
|
||||
This is a work in progress project, that will try to incorporate as many exceptions and variants of the format.
|
||||
|
||||
This is a toolkit because user has to define the way it needs the data. The idea is builded with something similar to the consumer/provider pattern.
|
||||
|
||||
We want to decode a stream of vevents from a .ics file into a custom type Events
|
||||
|
||||
type Event struct {
|
||||
Start, End time.Time
|
||||
Id, Summary string
|
||||
}
|
||||
|
||||
type Events []Event
|
||||
|
||||
func (e *Events) ConsumeICal(c *goics.Calendar, err error) error {
|
||||
for _, el := range c.Events {
|
||||
node := el.Data
|
||||
dtstart, err := node["DTSTART"].DateDecode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dtend, err := node["DTEND"].DateDecode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d := Event{
|
||||
Start: dtstart,
|
||||
End: dtend,
|
||||
Id: node["UID"].Val,
|
||||
Summary: node["SUMMARY"].Val,
|
||||
}
|
||||
*e = append(*e, d)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Our custom type, will need to implement ICalConsumer interface, where,
|
||||
the type will pick up data from the format.
|
||||
The decoding process will be somthing like this:
|
||||
|
||||
|
||||
d := goics.NewDecoder(strings.NewReader(testConsumer))
|
||||
consumer := Events{}
|
||||
err := d.Decode(&consumer)
|
||||
|
||||
|
||||
I have choosed this model, because, this format is a pain and also I don't like a lot the reflect package.
|
||||
|
||||
For encoding objects to iCal format, something similar has to be done:
|
||||
|
||||
The object emitting elements for the encoder, will have to implement the ICalEmiter, returning a Component structure to be encoded.
|
||||
This also had been done, because every package could require to encode vals and keys their way. Just for encoding time, I found more than
|
||||
three types of lines.
|
||||
|
||||
type EventTest struct {
|
||||
component goics.Componenter
|
||||
}
|
||||
|
||||
func (evt *EventTest) EmitICal() goics.Componenter {
|
||||
return evt.component
|
||||
}
|
||||
|
||||
|
||||
The Componenter, is an interface that every Component that can be encoded to ical implements.
|
||||
|
||||
c := goics.NewComponent()
|
||||
c.SetType("VCALENDAR")
|
||||
c.AddProperty("CALSCAL", "GREGORIAN")
|
||||
c.AddProperty("PRODID", "-//tmpo.io/src/goics")
|
||||
|
||||
m := goics.NewComponent()
|
||||
m.SetType("VEVENT")
|
||||
m.AddProperty("UID", "testing")
|
||||
c.AddComponent(m)
|
||||
|
||||
Properties, had to be stored as strings, the conversion from origin type to string format, must be done,
|
||||
on the emmiter. There are some helpers for date conversion and on the future I will add more, for encoding
|
||||
params on the string, and also for handling lists and recurrent events.
|
||||
|
||||
A simple example not functional used for testing:
|
||||
|
||||
c := goics.NewComponent()
|
||||
c.SetType("VCALENDAR")
|
||||
c.AddProperty("CALSCAL", "GREGORIAN")
|
||||
|
||||
ins := &EventTest{
|
||||
component: c,
|
||||
}
|
||||
|
||||
w := &bytes.Buffer{}
|
||||
enc := goics.NewICalEncode(w)
|
||||
enc.Encode(ins)
|
||||
|
||||
|
||||
*/
|
||||
package goics
|
||||
151
src/vendor/github.com/jordic/goics/encoder.go
generated
vendored
Normal file
151
src/vendor/github.com/jordic/goics/encoder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
package goics
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Line endings
|
||||
const (
|
||||
CRLF = "\r\n"
|
||||
CRLFSP = "\r\n "
|
||||
)
|
||||
|
||||
// NewComponent returns a new Component and setups
|
||||
// and setups Properties map for the component
|
||||
// and also allows more Components inside it.
|
||||
// VCALENDAR is a Component that has VEVENTS,
|
||||
// VEVENTS can hold VALARMS
|
||||
func NewComponent() *Component {
|
||||
return &Component{
|
||||
Elements: make([]Componenter, 0),
|
||||
Properties: make(map[string][]string),
|
||||
}
|
||||
}
|
||||
|
||||
// Component is the base type for holding a
|
||||
// ICal datatree before serilizing it
|
||||
type Component struct {
|
||||
Tipo string
|
||||
Elements []Componenter
|
||||
Properties map[string][]string
|
||||
}
|
||||
|
||||
// Writes the component to the Writer
|
||||
func (c *Component) Write(w *ICalEncode) {
|
||||
w.WriteLine("BEGIN:" + c.Tipo + CRLF)
|
||||
|
||||
// Iterate over component properties
|
||||
var keys []string
|
||||
for k := range c.Properties {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, key := range keys {
|
||||
vals := c.Properties[key]
|
||||
for _, val := range vals {
|
||||
w.WriteLine(WriteStringField(key, val))
|
||||
}
|
||||
}
|
||||
|
||||
for _, xc := range c.Elements {
|
||||
xc.Write(w)
|
||||
}
|
||||
|
||||
w.WriteLine("END:" + c.Tipo + CRLF)
|
||||
}
|
||||
|
||||
// SetType of the component, as
|
||||
// VCALENDAR VEVENT...
|
||||
func (c *Component) SetType(t string) {
|
||||
c.Tipo = t
|
||||
}
|
||||
|
||||
// AddComponent to the base component, just for building
|
||||
// the component tree
|
||||
func (c *Component) AddComponent(cc Componenter) {
|
||||
c.Elements = append(c.Elements, cc)
|
||||
}
|
||||
|
||||
// AddProperty adds a property to the component.
|
||||
func (c *Component) AddProperty(key string, val string) {
|
||||
c.Properties[key] = append(c.Properties[key], val)
|
||||
}
|
||||
|
||||
// ICalEncode is the real writer, that wraps every line,
|
||||
// in 75 chars length... Also gets the component from the emmiter
|
||||
// and starts the iteration.
|
||||
type ICalEncode struct {
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
// NewICalEncode generates a new encoder, and needs a writer
|
||||
func NewICalEncode(w io.Writer) *ICalEncode {
|
||||
return &ICalEncode{
|
||||
w: w,
|
||||
}
|
||||
}
|
||||
|
||||
// Encode the Component into the ical format
|
||||
func (enc *ICalEncode) Encode(c ICalEmiter) {
|
||||
component := c.EmitICal()
|
||||
component.Write(enc)
|
||||
}
|
||||
|
||||
// LineSize of the ics format
|
||||
var LineSize = 75
|
||||
|
||||
// WriteLine in ics format max length = LineSize
|
||||
// continuation lines start with a space.
|
||||
func (enc *ICalEncode) WriteLine(s string) {
|
||||
if len(s) <= LineSize {
|
||||
io.WriteString(enc.w, s)
|
||||
return
|
||||
}
|
||||
|
||||
// The first line does not begin with a space.
|
||||
firstLine := truncateString(s, LineSize-len(CRLF))
|
||||
io.WriteString(enc.w, firstLine+CRLF)
|
||||
|
||||
// Reserve three bytes for space + CRLF.
|
||||
lines := splitLength(s[len(firstLine):], LineSize-len(CRLFSP))
|
||||
for i, line := range lines {
|
||||
if i < len(lines)-1 {
|
||||
io.WriteString(enc.w, " "+line+CRLF)
|
||||
} else {
|
||||
// This is the last line, don't append CRLF.
|
||||
io.WriteString(enc.w, " "+line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FormatDateField returns a formated date: "DTEND;VALUE=DATE:20140406"
|
||||
func FormatDateField(key string, val time.Time) (string, string) {
|
||||
return key + ";VALUE=DATE", val.Format("20060102")
|
||||
}
|
||||
|
||||
// FormatDateTimeField in the form "X-MYDATETIME;VALUE=DATE-TIME:20120901T130000"
|
||||
func FormatDateTimeField(key string, val time.Time) (string, string) {
|
||||
return key + ";VALUE=DATE-TIME", val.Format("20060102T150405")
|
||||
}
|
||||
|
||||
// FormatDateTime as "DTSTART:19980119T070000Z"
|
||||
func FormatDateTime(key string, val time.Time) (string, string) {
|
||||
return key, val.UTC().Format("20060102T150405Z")
|
||||
}
|
||||
|
||||
// WriteStringField UID:asdfasdfаs@dfasdf.com
|
||||
func WriteStringField(key string, val string) string {
|
||||
return strings.ToUpper(key) + ":" + quoteString(val) + CRLF
|
||||
}
|
||||
|
||||
func quoteString(s string) string {
|
||||
s = strings.Replace(s, "\\", "\\\\", -1)
|
||||
s = strings.Replace(s, ";", "\\;", -1)
|
||||
s = strings.Replace(s, ",", "\\,", -1)
|
||||
s = strings.Replace(s, "\n", "\\n", -1)
|
||||
|
||||
return s
|
||||
}
|
||||
46
src/vendor/github.com/jordic/goics/goics.go
generated
vendored
Normal file
46
src/vendor/github.com/jordic/goics/goics.go
generated
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2015 jordi collell j@tmpo.io. All rights reserved
|
||||
// Package goics implements an ical encoder and decoder.
|
||||
// First release will include decode and encoder of Event types
|
||||
|
||||
package goics
|
||||
|
||||
// ICalConsumer is the realy important part of the decoder lib
|
||||
// The decoder is organized around the Provider/Consumer pattern.
|
||||
// the decoder acts as a consummer producing IcsNode's and
|
||||
// Every data type that wants to receive data, must implement
|
||||
// the consumer pattern.
|
||||
type ICalConsumer interface {
|
||||
ConsumeICal(d *Calendar, err error) error
|
||||
}
|
||||
|
||||
// ICalEmiter must be implemented in order to allow objects to be serialized
|
||||
// It should return a *goics.Calendar and optional a map of fields and
|
||||
// their serializers, if no serializer is defined, it will serialize as
|
||||
// string..
|
||||
type ICalEmiter interface {
|
||||
EmitICal() Componenter
|
||||
}
|
||||
|
||||
// Componenter defines what should be a component that can be rendered with
|
||||
// others components inside and some properties
|
||||
// CALENDAR >> VEVENT ALARM VTODO
|
||||
type Componenter interface {
|
||||
Write(w *ICalEncode)
|
||||
AddComponent(c Componenter)
|
||||
SetType(t string)
|
||||
AddProperty(string, string)
|
||||
}
|
||||
|
||||
// Calendar holds the base struct for a Component VCALENDAR
|
||||
type Calendar struct {
|
||||
Data map[string]*IcsNode // map of every property found on ics file
|
||||
Events []*Event // slice of events founds in file
|
||||
}
|
||||
|
||||
// Event holds the base struct for a Event Component during decoding
|
||||
type Event struct {
|
||||
Data map[string]*IcsNode
|
||||
Alarms []*map[string]*IcsNode
|
||||
// Stores multiple keys for the same property... ( a list )
|
||||
List map[string][]*IcsNode
|
||||
}
|
||||
40
src/vendor/github.com/jordic/goics/string.go
generated
vendored
Normal file
40
src/vendor/github.com/jordic/goics/string.go
generated
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
package goics
|
||||
|
||||
// splitLength returns a slice of strings, each string is at most length bytes long.
|
||||
// Does not break UTF-8 codepoints.
|
||||
func splitLength(s string, length int) []string {
|
||||
var ret []string
|
||||
|
||||
for len(s) > 0 {
|
||||
tmp := truncateString(s, length)
|
||||
if len(tmp) == 0 {
|
||||
// too short length, or invalid UTF-8 string
|
||||
break
|
||||
}
|
||||
ret = append(ret, tmp)
|
||||
s = s[len(tmp):]
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// truncateString truncates s to a maximum of length bytes without breaking UTF-8 codepoints.
|
||||
func truncateString(s string, length int) string {
|
||||
if len(s) <= length {
|
||||
return s
|
||||
}
|
||||
|
||||
// UTF-8 continuation bytes start with 10xx xxxx:
|
||||
// 0xc0 = 1100 0000
|
||||
// 0x80 = 1000 0000
|
||||
cutoff := length
|
||||
for s[cutoff]&0xc0 == 0x80 {
|
||||
cutoff--
|
||||
if cutoff < 0 {
|
||||
cutoff = 0
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return s[0:cutoff]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue