concal

package module
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Nov 6, 2026 License: MIT Imports: 13 Imported by: 0

README

concal -- Concurrent iCalendar parser wrapper for Go.

ci status codeql status GitHub license GitHub release Go reference

concal is a Go package for reading iCalendar (.ics) data from local files or HTTP URLs and parsing its events. It wraps apognu/gocal and also provides an API for parsing multiple sources concurrently.

Requirements

  • Go 1.26 or later

Installation

go get github.com/goark/concal

Parse One Source

ParseOne reads a local file or an HTTP URL and returns the events in the configured time period.

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/goark/concal"
)

func main() {
	parser := concal.NewParser(
		concal.WithStartTime(time.Date(2026, time.January, 1, 0, 0, 0, 0, time.UTC)),
		concal.WithEndTime(time.Date(2026, time.December, 31, 23, 59, 59, 0, time.UTC)),
	)

	events, err := parser.ParseOne(context.Background(), "./calendar.ics")
	if err != nil {
		panic(err)
	}
	for _, event := range events {
		fmt.Println(event.Summary)
	}
}

Pass an http:// or https:// URL instead of a file path to read a remote calendar.

Parse Multiple Sources Concurrently

Conc.Submit starts parsing each source in its own goroutine. Consume events while Wait is running: Wait waits for parsing and event sends to finish, then closes the event channel. Calling Wait before receiving events can block because the event channel is unbuffered.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/goark/concal"
)

func main() {
	parser := concal.NewParser()
	conc := concal.NewConcurrent(context.Background(), parser)

	for _, source := range []string{
		"./calendar-a.ics",
		"https://example.com/calendar.ics",
	} {
		if err := conc.Submit(source); err != nil {
			log.Printf("submit %q: %v", source, err)
		}
	}

	go conc.Wait() // start waiting in a separate goroutine
	for event := range conc.Events() {
		fmt.Println(event.Summary)
	}

	if err := conc.Close(); err != nil {
		log.Printf("parse calendars: %v", err)
	}
}

Close cancels outstanding requests and waits for parsing to stop. After normal event consumption, use it to retrieve errors accumulated while parsing. Sources cannot be submitted after Wait or Close has started.

Parser Options

  • WithStartTime(t) sets the beginning of the event range.
  • WithEndTime(t) sets the end of the event range.
  • WithLocationAllDayEvent(location) sets the time location for all-day events.
  • WithLocationMap(name, location) adds a named time location for parsing events.
  • WithHTTP11Client(true) forces HTTP/1.1 for URL requests.

If neither range endpoint is set, the default range is 90 days from the current time. If only one endpoint is set, the other endpoint is inferred as a 90-day range. If both are set in reverse order, they are swapped.

ParseOne returns errors for the individual source. Parser.GetError returns errors accumulated by that parser. Conc.Close returns errors accumulated while processing submitted sources.

Modules Requirement Graph

dependency.png

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalidSource    = errors.New("invalid source")
	ErrConcurrentClosed = errors.New("concurrent parser is closed")
	ErrInvalidLocation  = errors.New("invalid location")
)

Functions

func WithEndTime

func WithEndTime(end time.Time) parseOpts

WithEndTime sets the end time for the parsing period.

func WithHTTP11Client

func WithHTTP11Client(forceHTTP11 bool) parseOpts

WithHTTP11Client sets the HTTP client to use HTTP/1.1.

func WithLocationAllDayEvent added in v0.1.2

func WithLocationAllDayEvent(location *time.Location) parseOpts

WithLocationAllDayEvent sets the time location for all-day events.

func WithLocationMap added in v0.1.2

func WithLocationMap(name string, location *time.Location) parseOpts

WithLocationMap sets the time location for the parser.

func WithStartTime

func WithStartTime(start time.Time) parseOpts

WithStartTime sets the start time for the parsing period.

Types

type Conc

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

Conc parses multiple calendar sources concurrently.

func NewConcurrent

func NewConcurrent(ctx context.Context, parser *Parser) *Conc

NewConcurrent creates a concurrent parser using parser for each source.

func (*Conc) Close

func (c *Conc) Close() error

Close cancels ongoing parsing, waits for workers, and returns accumulated errors.

func (*Conc) EventSlice

func (c *Conc) EventSlice() []gocal.Event

EventSlice returns a slice of gocal.Event from the Conc object. If the Conc object is nil, it returns nil.

func (*Conc) Events

func (c *Conc) Events() <-chan gocal.Event

Events returns the channel of parsed events.

func (*Conc) Submit

func (c *Conc) Submit(src string) error

Submit starts parsing src. Sources cannot be submitted after Wait or Close.

func (*Conc) Wait

func (c *Conc) Wait()

Wait prevents further submissions, waits for all events to be sent, and closes Events. Events must be consumed concurrently with Wait to avoid blocking event senders.

type Parser

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

Parser is a struct that holds the context, error list, HTTP client settings, and time period for parsing calendar events.

func NewParser

func NewParser(opts ...parseOpts) *Parser

NewParser creates a new Parser instance with the given context and options.

func (*Parser) GetError

func (p *Parser) GetError() error

GetError returns the aggregated error from the parser, if any.

func (*Parser) ParseOne

func (p *Parser) ParseOne(ctx context.Context, s string) (events []gocal.Event, retErr error)

ParseOne parses the calendar events from the given source string within the specified period. It returns the parsed events and any error encountered.

Example
package main

import (
	"context"
	"fmt"
	"sort"
	"time"

	"github.com/goark/concal"
)

func main() {
	p := concal.NewParser(
		concal.WithStartTime(time.Date(2026, time.November, 1, 0, 0, 0, 0, time.UTC)),
		concal.WithEndTime(time.Date(2026, time.November, 30, 23, 59, 59, 999999999, time.UTC)),
	)
	evts, err := p.ParseOne(context.Background(), "testdata/holiday.ics")
	if err != nil {
		fmt.Printf("%+v\n", err)
		return
	}
	sort.SliceStable(evts, func(i, j int) bool {
		return evts[i].Start.Before(*evts[j].Start)
	})

	for _, e := range evts {
		fmt.Printf("%s on %s\n", e.Summary, e.Start.Format("2006-01-02"))
	}
}
Output:
文化の日 on 2026-11-03
勤労感謝の日 on 2026-11-23

type Source

type Source interface {
	Type() SourceType
	Reader() io.Reader
	Close() error
}

Source represents a generic source for calendar data, which can be a file or a URL.

type SourceType

type SourceType int

SourceType represents the type of the source (file, URL, or unknown).

const (
	// Source types for the parser
	TypeUnknown SourceType = iota // Unknown source type
	TypeFile                      // File source type
	TypeURL                       // URL source type
)

Jump to

Keyboard shortcuts

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