checkvist-api/models.go

210 lines
6.7 KiB
Go
Raw Normal View History

2026-01-14 12:42:00 +01:00
package checkvist
import (
"fmt"
"strings"
"time"
)
2026-01-14 12:42:00 +01:00
// models.go contains data structures for Checkvist entities:
// Checklist, Task, Note, User, Tags, TaskStatus, and DueDate.
// TaskStatus represents the status of a task.
type TaskStatus int
const (
// StatusOpen indicates the task is open/incomplete.
StatusOpen TaskStatus = 0
// StatusClosed indicates the task is completed.
StatusClosed TaskStatus = 1
// StatusInvalidated indicates the task has been invalidated.
StatusInvalidated TaskStatus = 2
)
// String returns the string representation of the TaskStatus.
func (s TaskStatus) String() string {
switch s {
case StatusOpen:
return "open"
case StatusClosed:
return "closed"
case StatusInvalidated:
return "invalidated"
default:
return fmt.Sprintf("unknown(%d)", s)
}
}
// Tags represents a set of tags as a map for efficient lookup.
type Tags map[string]bool
// APITime wraps time.Time with custom JSON unmarshaling for Checkvist API format.
// The Checkvist API returns timestamps in format "2006/01/02 15:04:05 +0000"
// instead of the standard RFC3339 format that Go expects.
type APITime struct {
time.Time
}
// UnmarshalJSON handles multiple date formats from the Checkvist API.
func (t *APITime) UnmarshalJSON(data []byte) error {
s := strings.Trim(string(data), `"`)
if s == "" || s == "null" {
return nil
}
// Try formats in order of likelihood
formats := []string{
"2006/01/02 15:04:05 -0700", // Checkvist API format
time.RFC3339, // ISO8601 with timezone
"2006-01-02T15:04:05Z", // RFC3339 without offset
}
for _, format := range formats {
if parsed, err := time.Parse(format, s); err == nil {
t.Time = parsed
return nil
}
}
return fmt.Errorf("cannot parse %q as time", s)
}
// MarshalJSON outputs time in RFC3339 format.
func (t APITime) MarshalJSON() ([]byte, error) {
if t.IsZero() {
return []byte("null"), nil
}
return []byte(`"` + t.Format(time.RFC3339) + `"`), nil
}
// NewAPITime creates an APITime from a time.Time value.
func NewAPITime(t time.Time) APITime {
return APITime{Time: t}
}
// Checklist represents a Checkvist checklist.
type Checklist struct {
// ID is the unique identifier of the checklist.
ID int `json:"id"`
// Name is the title of the checklist.
Name string `json:"name"`
// Public indicates whether the checklist is publicly accessible.
Public bool `json:"public"`
// Archived indicates whether the checklist is archived.
Archived bool `json:"archived"`
// ReadOnly indicates whether the checklist is read-only for the current user.
ReadOnly bool `json:"read_only"`
// TaskCount is the total number of tasks in the checklist.
TaskCount int `json:"task_count"`
// TaskCompleted is the number of completed tasks in the checklist.
TaskCompleted int `json:"task_completed"`
// Tags contains the parsed tags from TagsAsText.
Tags Tags `json:"-"`
// TagsAsText is the raw tags string from the API.
TagsAsText string `json:"tags_as_text"`
// UpdatedAt is the timestamp of the last update.
UpdatedAt APITime `json:"updated_at"`
}
// Task represents a task within a Checkvist checklist.
type Task struct {
// ID is the unique identifier of the task.
ID int `json:"id"`
// ChecklistID is the ID of the checklist this task belongs to.
ChecklistID int `json:"checklist_id"`
// ParentID is the ID of the parent task, or 0 if this is a root task.
ParentID int `json:"parent_id"`
// Content is the text content of the task.
Content string `json:"content"`
// Status is the current status of the task (open, closed, invalidated).
Status TaskStatus `json:"status"`
// Position is the position of the task within its siblings.
Position int `json:"position"`
// Priority is the priority level (1 = highest, 2 = high, 0 = normal).
Priority int `json:"priority"`
// Tags contains the parsed tags from TagsAsText.
Tags Tags `json:"-"`
// TagsAsText is the raw tags string from the API.
TagsAsText string `json:"tags_as_text"`
// DueDateRaw is the raw due date string from the API.
DueDateRaw string `json:"due"`
// DueDate is the parsed due date, if available in ISO format.
DueDate *time.Time `json:"-"`
// AssigneeIDs contains the IDs of users assigned to this task.
AssigneeIDs []int `json:"assignee_ids"`
// CommentsCount is the number of notes/comments on this task.
CommentsCount int `json:"comments_count"`
// UpdateLine contains brief update information.
UpdateLine string `json:"update_line"`
// UpdatedAt is the timestamp of the last update.
UpdatedAt APITime `json:"updated_at"`
// CreatedAt is the timestamp when the task was created.
CreatedAt APITime `json:"created_at"`
// Children contains nested child tasks (when fetched with tree structure).
Children []*Task `json:"tasks,omitempty"`
// Notes contains the comments/notes attached to this task.
Notes []Note `json:"notes,omitempty"`
}
// Note represents a comment/note on a task.
type Note struct {
// ID is the unique identifier of the note.
ID int `json:"id"`
// TaskID is the ID of the task this note belongs to.
TaskID int `json:"task_id"`
// Comment is the text content of the note.
Comment string `json:"comment"`
// UpdatedAt is the timestamp of the last update.
UpdatedAt APITime `json:"updated_at"`
// CreatedAt is the timestamp when the note was created.
CreatedAt APITime `json:"created_at"`
}
// User represents a Checkvist user.
type User struct {
// ID is the unique identifier of the user.
ID int `json:"id"`
// Username is the user's display name.
Username string `json:"username"`
// Email is the user's email address.
Email string `json:"email"`
}
// DueDate represents a due date for task creation using Checkvist's smart syntax.
type DueDate struct {
value string
}
// Common due date constants using Checkvist's smart syntax.
var (
// DueToday sets the due date to today.
DueToday = DueDate{value: "^today"}
// DueTomorrow sets the due date to tomorrow.
DueTomorrow = DueDate{value: "^tomorrow"}
// DueNextWeek sets the due date to next week.
DueNextWeek = DueDate{value: "^next week"}
// DueNextMonth sets the due date to next month.
DueNextMonth = DueDate{value: "^next month"}
)
// DueAt creates a DueDate from a Go time.Time value.
func DueAt(t time.Time) DueDate {
return DueDate{value: "^" + t.Format("2006-01-02")}
}
// DueString creates a DueDate from a raw smart syntax string.
// The string should use Checkvist's smart syntax (e.g., "^2026-02-01", "^friday").
func DueString(s string) DueDate {
return DueDate{value: s}
}
// DueInDays creates a DueDate for n days from now.
func DueInDays(n int) DueDate {
t := time.Now().AddDate(0, 0, n)
return DueDate{value: "^" + t.Format("2006-01-02")}
}
// String returns the smart syntax string for the due date.
func (d DueDate) String() string {
return d.value
}