Documentation
¶
Overview ¶
Package calsvc is the shared service layer between the user-facing frontends (the CLI and the MCP server) and the calendar/event domain packages.
It owns everything between "user-shaped strings" and domain calls: session bootstrap, key unlocking, calendar resolution and caching, datetime/occurrence parsing, recurrence-option validation, and the orchestration of list/create/update/delete operations. Frontends are reduced to argument binding and rendering.
Bootstrap responses (key material and the calendar list - never event content) are cached on disk with liberal TTLs; stale entries are self-healing because key material fails cryptographically when wrong (see cacheapi.go).
Index ¶
- Variables
- func FormatOccurrenceStart(ts int64, allDay bool, loc *time.Location) string
- func ResolveColorCreate(spec string) (string, error)
- func ResolveCreateReminders(mode ReminderMode, specs []string) (list []caltypes.Notification, set bool, err error)
- func ResolveUpdateReminders(mode ReminderMode, specs []string) (*event.RemindersUpdate, error)
- type ColorUpdate
- type CreateCalendarInput
- type CreateEventInput
- type CreatedEvent
- type DeleteCalendarInput
- type DeleteEventInput
- type EventList
- type GetEventInput
- type GotCalendar
- type GotEvent
- type ListEventsInput
- type ListedItem
- type Recurrence
- type ReminderMode
- type Service
- func (s *Service) Calendars(ctx context.Context) ([]calendar.Info, error)
- func (s *Service) Close()
- func (s *Service) CreateCalendar(ctx context.Context, in CreateCalendarInput) (*GotCalendar, error)
- func (s *Service) CreateEvent(ctx context.Context, in CreateEventInput) (*CreatedEvent, error)
- func (s *Service) DefaultCalendarID(ctx context.Context) (string, error)
- func (s *Service) DeleteCalendar(ctx context.Context, in DeleteCalendarInput) error
- func (s *Service) DeleteEvent(ctx context.Context, in DeleteEventInput) (*event.DeleteResult, error)
- func (s *Service) EffectiveTimezone(override string) string
- func (s *Service) GetCalendar(ctx context.Context, selector string) (*GotCalendar, error)
- func (s *Service) GetEvent(ctx context.Context, in GetEventInput) (*GotEvent, error)
- func (s *Service) ListEvents(ctx context.Context, in ListEventsInput) (*EventList, error)
- func (s *Service) ResolveCalendarInfo(ctx context.Context, selector string) (calendar.Info, error)
- func (s *Service) UpdateCalendar(ctx context.Context, in UpdateCalendarInput) (*GotCalendar, error)
- func (s *Service) UpdateEvent(ctx context.Context, in UpdateEventInput) (*event.UpdateOutcome, error)
- type UpdateCalendarInput
- type UpdateEventInput
Constants ¶
This section is empty.
Variables ¶
var ErrICSUndecryptable = errors.New("event could not be decrypted into iCalendar")
ErrICSUndecryptable is the error frontends return when an ICS export was requested but no card could be decrypted (GotEvent.ICS is empty).
Functions ¶
func FormatOccurrenceStart ¶
FormatOccurrenceStart renders an occurrence's original start in the form the occurrence arguments accept back: the date (all-day, midnight UTC) or wall time in loc.
func ResolveColorCreate ¶
ResolveColorCreate resolves a create-side color spec to a palette hex. "" or "default" return "" (a create with no color inherits the calendar's).
func ResolveCreateReminders ¶
func ResolveCreateReminders(mode ReminderMode, specs []string) (list []caltypes.Notification, set bool, err error)
ResolveCreateReminders turns a mode + specs into the create-side (list, set) pair: inherit -> (nil, false); none -> (nil, true); custom -> (list, true). ReminderKeep is treated as inherit (a create with no reminders inherits).
func ResolveUpdateReminders ¶
func ResolveUpdateReminders(mode ReminderMode, specs []string) (*event.RemindersUpdate, error)
ResolveUpdateReminders turns a mode + specs into the update tri-state (nil = keep). The Inherit:true (revert -> null) vs List:nil (none -> []) distinction is preserved deliberately; see event/write.go.
Types ¶
type ColorUpdate ¶
ColorUpdate is the calsvc-level color change intent. Inherit reverts to the calendar's color (resolved at apply time); else Hex is the palette color to set.
func ResolveColorUpdate ¶
func ResolveColorUpdate(spec string) (*ColorUpdate, error)
ResolveColorUpdate resolves an update-side color spec to the change intent. "default" reverts to the calendar color; else the spec is a name/hex. The caller decides whether a color was supplied at all (nil = keep).
type CreateCalendarInput ¶
CreateCalendarInput describes a new owned calendar. Name is required; Color is a Proton color name or hex (empty = a random palette color, as the web client does). MakeDefault also sets it as the account's default calendar.
type CreateEventInput ¶
type CreateEventInput struct {
Summary string
Description string
Location string
Start string // "YYYY-MM-DD HH:MM"; with AllDay: "YYYY-MM-DD"
End string // timed: "" defaults to start + calendar default duration; all-day: inclusive end date, "" = Start
AllDay bool
Recurrence Recurrence
Calendar string
TZ string
// Reminders + RemindersSet select the tri-state (false = inherit default,
// true+empty = none, true+non-empty = custom); pre-parsed by the frontend.
Reminders []caltypes.Notification
RemindersSet bool
// Color is the per-event color override ("#RRGGBB"); "" = inherit.
Color string
}
CreateEventInput describes a new event with user-shaped strings.
type CreatedEvent ¶
type CreatedEvent struct {
ID string
UID string
Summary string
Description string
Location string
Start time.Time
End time.Time
AllDay bool
RRule string
Reminders []caltypes.Notification
Color string
}
CreatedEvent reports a create outcome. ID/UID empty when the server didn't echo the row; Start/End are echoed times if available, else requested. Reminders/Color reflect what was REQUESTED (the create response doesn't reliably echo row fields).
type DeleteCalendarInput ¶
DeleteCalendarInput describes a calendar deletion. Password (the login password) is required only for owned (normal) calendars, which need the elevated "locked" scope; it is ignored for managed (holidays) calendars.
type DeleteEventInput ¶
type DeleteEventInput struct {
EventID string
Occurrence string // "" = whole event/series; else the ORIGINAL start of one occurrence
Calendar string
TZ string
}
DeleteEventInput addresses a deletion with user-shaped strings.
type EventList ¶
type EventList struct {
Calendars []calendar.Info // the resolved calendars this window covers
Location *time.Location // display zone the read was resolved in
From time.Time
FromGiven bool // false when the window starts "now"
Days int
Items []ListedItem // sorted by occurrence start, then row ID
}
EventList is a resolved, expanded, decrypted listing window. It may span multiple calendars; each item carries its own calendar context.
func (*EventList) SingleCalendar ¶
SingleCalendar reports whether the window covers exactly one calendar (the common case); frontends use it to decide whether to label each event with its calendar.
type GetEventInput ¶
type GetEventInput struct {
EventID string
Calendar string // calendar ID or name; "" = default, else first
TZ string
WithICS bool // also reconstruct the raw iCalendar (BuildICS)
// NoSeries (with WithICS on a recurring event) limits the export to the single
// addressed VEVENT instead of the whole series; no-op (warns) for non-recurring.
NoSeries bool
}
GetEventInput addresses a single event for detailed retrieval.
type GotCalendar ¶
GotCalendar is a single resolved calendar with its default settings and a flag for whether it is the configured default.
type GotEvent ¶
type GotEvent struct {
Calendar calendar.Info
Settings calendar.Settings // calendar default reminders (for inheritance)
Location *time.Location // display zone the read was resolved in
Event *event.Event
Raw *caltypes.RawEvent
ICS string // populated only when GetEventInput.WithICS
}
GotEvent is a single decrypted event with its raw row, the resolved calendar, the display zone, and (when requested) the reconstructed ICS.
type ListEventsInput ¶
type ListEventsInput struct {
Calendars []string // calendar IDs or names; empty = default, else first
AllCalendars bool // list every calendar (ignores Calendars)
TZ string // IANA zone override; "" = configured / system
From string // window start "YYYY-MM-DD [HH:MM]"; "" = now
Days int // days to look ahead; <= 0 = 7
}
ListEventsInput addresses a listing window with user-shaped strings. The window spans one or more calendars: an empty Calendars (with AllCalendars false) lists the default-or-first calendar; AllCalendars lists every calendar.
type ListedItem ¶
ListedItem is one expanded occurrence tagged with the calendar it came from. event.Listed is embedded so callers reach Event/Occurrence directly; Calendar and Settings give the per-item calendar context needed when a listing spans multiple calendars (each calendar has its own color and default reminders).
type Recurrence ¶
type Recurrence struct {
Repeat string // "", "daily", "weekly", "monthly", "yearly"
Every int // interval; 0 or 1 = default
Count int // 0 = unset
Until string // "" = unset; YYYY-MM-DD
RawRRule string // raw RRULE value; replaces the structured options
}
Recurrence holds the structured recurrence options shared by the create and update operations.
func (Recurrence) Empty ¶
func (r Recurrence) Empty() bool
Empty reports whether no recurrence options were given.
type ReminderMode ¶
type ReminderMode int
ReminderMode is the surface-independent reminder change intent, shared by the CLI (from boolean flags) and the MCP server (from a string). The CLI never uses ReminderKeep on create (a new event with no flags inherits, same as ReminderInherit); update uses all four.
const ( ReminderKeep ReminderMode = iota // leave the event's reminders unchanged (update only) ReminderInherit // revert to the calendar default ReminderNone // explicitly no reminders ReminderCustom // use the supplied list )
Reminder modes, shared by both frontends.
func ParseReminderMode ¶
func ParseReminderMode(s string) (ReminderMode, error)
ParseReminderMode maps the MCP reminders_mode string to a ReminderMode. "" and "keep" both mean keep.
type Service ¶
type Service struct {
// Notify receives short human-readable notices (e.g. which calendar
// was picked when no selector was given). Nil = silent.
Notify func(msg string)
// contains filtered or unexported fields
}
Service bundles the authenticated state shared by calendar operations. Construct with New; safe for concurrent use (the MCP server shares one).
func New ¶
New restores an authenticated Service from the persisted session (missing session yields a "run login" error). noCache disables the on-disk bootstrap cache. Call Close when done.
func NewDetached ¶
NewDetached returns a Service with config only - no session or client. For tests exercising validation paths; domain operations on it will panic.
func NewWithAPI ¶
NewWithAPI builds a Service whose reads are served by the given papi.API (both cached and fresh path), with no session client. A test seam for resolution and read-only ops; operations needing the *papi.Client (key unlock) will panic.
func (*Service) Calendars ¶
Calendars fetches the calendar list fresh (so a long-running MCP server sees newly created calendars; the cache-staleness escape hatch), still updating both caches.
func (*Service) CreateCalendar ¶
func (s *Service) CreateCalendar(ctx context.Context, in CreateCalendarInput) (*GotCalendar, error)
CreateCalendar creates a new owned calendar and returns its refreshed detail. It resolves the color (random when unset), unlocks the account keys to pick the signing address, then performs the two-step create (metadata + keys). On a key-setup failure the (keyless) calendar's ID is reported via the wrapped error so it can be completed or removed.
func (*Service) CreateEvent ¶
func (s *Service) CreateEvent(ctx context.Context, in CreateEventInput) (*CreatedEvent, error)
CreateEvent validates, resolves and creates an event.
func (*Service) DefaultCalendarID ¶
DefaultCalendarID returns the account's server-side default calendar ID (source of truth for default markers and unspecified selectors); cached fetch, empty = no default set.
func (*Service) DeleteCalendar ¶
func (s *Service) DeleteCalendar(ctx context.Context, in DeleteCalendarInput) error
DeleteCalendar removes a calendar. Owned (normal) calendars need the elevated scope (re-prove the login password via SRP); managed (holidays) use the managed route without a password. Subscribed calendars cannot be deleted this way.
func (*Service) DeleteEvent ¶
func (s *Service) DeleteEvent(ctx context.Context, in DeleteEventInput) (*event.DeleteResult, error)
DeleteEvent resolves and deletes an event, series or single occurrence (per event.SmartDelete).
func (*Service) EffectiveTimezone ¶
EffectiveTimezone resolves a timezone: the explicit override when given, else the configured / system timezone.
func (*Service) GetCalendar ¶
GetCalendar resolves a calendar by selector ("" = configured default, else first), unlocks it to read default settings, and reports whether it is the configured default. The bootstrap fetch is cached.
func (*Service) GetEvent ¶
GetEvent fetches, decrypts and (optionally) reconstructs the ICS of a single event. It must live in the resolved calendar (default/first unless Calendar is given); a missing event returns an error advising --calendar.
func (*Service) ListEvents ¶
ListEvents lists all event occurrences in the addressed window across one or more calendars, merged and sorted by occurrence start.
func (*Service) ResolveCalendarInfo ¶
ResolveCalendarInfo resolves a selector to a calendar's Info without unlocking keys. Used for pre-delete dry runs and to decide whether a password is needed.
func (*Service) UpdateCalendar ¶
func (s *Service) UpdateCalendar(ctx context.Context, in UpdateCalendarInput) (*GotCalendar, error)
UpdateCalendar applies metadata/settings/default changes and returns refreshed detail. Only owned (normal) calendars can be modified. Applied in order (metadata, settings, default); a later failure leaves earlier changes standing.
func (*Service) UpdateEvent ¶
func (s *Service) UpdateEvent(ctx context.Context, in UpdateEventInput) (*event.UpdateOutcome, error)
UpdateEvent validates, resolves and applies a partial update; series and single-occurrence semantics are handled per event.SmartUpdate.
type UpdateCalendarInput ¶
type UpdateCalendarInput struct {
Selector string
// Metadata (per-member): nil = keep.
Name *string
Description *string
Color *string // a Proton color name or hex; validated to a palette hex
// Default settings: nil = keep.
DefaultDuration *int
PartDayReminders *[]caltypes.Notification
FullDayReminders *[]caltypes.Notification
MakesUserBusy *bool
// MakeDefault sets this calendar as the account's default calendar.
MakeDefault bool
}
UpdateCalendarInput describes a partial update to a calendar's metadata/settings plus an optional make-default request. Pointer fields nil = leave unchanged; an empty string clears Description, but Name/Color must be non-empty.
type UpdateEventInput ¶
type UpdateEventInput struct {
EventID string
Summary *string
Description *string
Location *string
Start string // "" = keep
End string // "" = keep
Occurrence string // "" = whole event/series; else the ORIGINAL start of one occurrence
NoRepeat bool
Recurrence Recurrence
Calendar string
TZ string
// Reminders: nil = keep current; non-nil overrides (frontend resolves the
// keep/inherit/none/custom intent into the event tri-state type).
Reminders *event.RemindersUpdate
// Color: nil = keep current; non-nil overrides. Proton has no color "inherit"
// sentinel - reverting means setting the calendar's own color, which UpdateEvent
// resolves, so this carries the intent rather than a literal hex.
Color *ColorUpdate
}
UpdateEventInput describes a partial update with user-shaped strings. Text fields are pointers so frontends choose presence semantics (CLI sets on flag presence, empty clears; MCP only for non-empty values).