goics

package
v0.0.0-...-89fe5e3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 2, 2022 License: MIT, MIT Imports: 6 Imported by: 0

README

A go toolkit for decoding, encoding icalendar ics ical files

Alpha status

Build Status

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

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.

Documentation

Overview

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)

Index

Constants

View Source
const (
	CRLF   = "\r\n"
	CRLFSP = "\r\n "
)

Line endings

Variables

View Source
var (
	ErrCalendarNotFound = errors.New("vCalendar not found")
	ErrParseEndCalendar = errors.New("wrong format END:VCALENDAR not Found")
)

Errors

View Source
var LineSize = 75

LineSize of the ics format

Functions

func FormatDateField

func FormatDateField(key string, val time.Time) (string, string)

FormatDateField returns a formated date: "DTEND;VALUE=DATE:20140406"

func FormatDateTime

func FormatDateTime(key string, val time.Time) (string, string)

FormatDateTime as "DTSTART:19980119T070000Z"

func FormatDateTimeField

func FormatDateTimeField(key string, val time.Time) (string, string)

FormatDateTimeField in the form "X-MYDATETIME;VALUE=DATE-TIME:20120901T130000"

func NewDecoder

func NewDecoder(r io.Reader) *decoder

NewDecoder creates an instance of de decoder

func WriteStringField

func WriteStringField(key string, val string) string

WriteStringField UID:asdfasdfаs@dfasdf.com

Types

type Calendar

type Calendar struct {
	Data   map[string]*IcsNode // map of every property found on ics file
	Events []*Event            // slice of events founds in file
}

Calendar holds the base struct for a Component VCALENDAR

type Component

type Component struct {
	Tipo       string
	Elements   []Componenter
	Properties map[string][]string
}

Component is the base type for holding a ICal datatree before serilizing it

func NewComponent

func NewComponent() *Component

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 (*Component) AddComponent

func (c *Component) AddComponent(cc Componenter)

AddComponent to the base component, just for building the component tree

func (*Component) AddProperty

func (c *Component) AddProperty(key string, val string)

AddProperty adds a property to the component.

func (*Component) SetType

func (c *Component) SetType(t string)

SetType of the component, as VCALENDAR VEVENT...

func (*Component) Write

func (c *Component) Write(w *ICalEncode)

Writes the component to the Writer

type Componenter

type Componenter interface {
	Write(w *ICalEncode)
	AddComponent(c Componenter)
	SetType(t string)
	AddProperty(string, string)
}

Componenter defines what should be a component that can be rendered with others components inside and some properties CALENDAR >> VEVENT ALARM VTODO

type Event

type Event struct {
	Data   map[string]*IcsNode
	Alarms []*map[string]*IcsNode
	// Stores multiple keys for the same property... ( a list )
	List map[string][]*IcsNode
}

Event holds the base struct for a Event Component during decoding

type ICalConsumer

type ICalConsumer interface {
	ConsumeICal(d *Calendar, err error) error
}

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 ICalEmiter

type ICalEmiter interface {
	EmitICal() Componenter
}

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 ICalEncode

type ICalEncode struct {
	// contains filtered or unexported fields
}

ICalEncode is the real writer, that wraps every line, in 75 chars length... Also gets the component from the emmiter and starts the iteration.

func NewICalEncode

func NewICalEncode(w io.Writer) *ICalEncode

NewICalEncode generates a new encoder, and needs a writer

func (*ICalEncode) Encode

func (enc *ICalEncode) Encode(c ICalEmiter)

Encode the Component into the ical format

func (*ICalEncode) WriteLine

func (enc *ICalEncode) WriteLine(s string)

WriteLine in ics format max length = LineSize continuation lines start with a space.

type IcsNode

type IcsNode struct {
	Key    string
	Val    string
	Params map[string]string
}

IcsNode is a basic token.., with, key, val, and extra params to define the type of val.

func DecodeLine

func DecodeLine(line string) *IcsNode

DecodeLine extracts key, val and extra params from a line

func (*IcsNode) DateDecode

func (n *IcsNode) DateDecode() (time.Time, error)

DateDecode Decodes a date in the distincts formats

func (*IcsNode) GetOneParam

func (n *IcsNode) GetOneParam() (string, string)

GetOneParam resturns the first param found usefull when you know that there is a only one param token

func (*IcsNode) ParamsLen

func (n *IcsNode) ParamsLen() int

ParamsLen returns how many params has a token

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL