builders.go

  1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
  2//
  3// SPDX-License-Identifier: AGPL-3.0-or-later
  4
  5package lunatask
  6
  7import "time"
  8
  9// TaskBuilder constructs a [CreateTaskRequest] via method chaining.
 10//
 11//	req := lunatask.NewTask("Review PR").
 12//		InArea(areaID).
 13//		WithStatus(lunatask.StatusNext).
 14//		WithEstimate(30).
 15//		Build()
 16//	task, err := client.CreateTask(ctx, req)
 17type TaskBuilder struct {
 18	req CreateTaskRequest
 19}
 20
 21// NewTask starts building a task with the given name.
 22func NewTask(name string) *TaskBuilder {
 23	return &TaskBuilder{req: CreateTaskRequest{Name: name}} //nolint:exhaustruct
 24}
 25
 26// InArea assigns the task to an area. IDs are in the area's settings in the app.
 27func (b *TaskBuilder) InArea(areaID string) *TaskBuilder {
 28	b.req.AreaID = &areaID
 29
 30	return b
 31}
 32
 33// InGoal assigns the task to a goal. IDs are in the goal's settings in the app.
 34func (b *TaskBuilder) InGoal(goalID string) *TaskBuilder {
 35	b.req.GoalID = &goalID
 36
 37	return b
 38}
 39
 40// WithNote attaches a Markdown note to the task.
 41func (b *TaskBuilder) WithNote(note string) *TaskBuilder {
 42	b.req.Note = &note
 43
 44	return b
 45}
 46
 47// WithStatus sets the workflow status.
 48// Use one of the Status* constants (e.g., [StatusNext]).
 49func (b *TaskBuilder) WithStatus(status TaskStatus) *TaskBuilder {
 50	b.req.Status = &status
 51
 52	return b
 53}
 54
 55// WithMotivation sets why this task matters.
 56// Use one of the Motivation* constants (e.g., [MotivationMust]).
 57func (b *TaskBuilder) WithMotivation(motivation Motivation) *TaskBuilder {
 58	b.req.Motivation = &motivation
 59
 60	return b
 61}
 62
 63// WithEstimate sets the expected duration in minutes (0–720).
 64func (b *TaskBuilder) WithEstimate(minutes int) *TaskBuilder {
 65	b.req.Estimate = &minutes
 66
 67	return b
 68}
 69
 70// WithPriority sets importance from -2 (lowest) to 2 (highest).
 71func (b *TaskBuilder) WithPriority(priority int) *TaskBuilder {
 72	b.req.Priority = &priority
 73
 74	return b
 75}
 76
 77// WithEisenhower sets the matrix quadrant (0–4).
 78func (b *TaskBuilder) WithEisenhower(eisenhower int) *TaskBuilder {
 79	b.req.Eisenhower = &eisenhower
 80
 81	return b
 82}
 83
 84// ScheduledOn sets when the task should appear on your schedule.
 85func (b *TaskBuilder) ScheduledOn(date Date) *TaskBuilder {
 86	b.req.ScheduledOn = &date
 87
 88	return b
 89}
 90
 91// CompletedAt marks the task completed at a specific time.
 92func (b *TaskBuilder) CompletedAt(t time.Time) *TaskBuilder {
 93	b.req.CompletedAt = &t
 94
 95	return b
 96}
 97
 98// FromSource tags the task with a free-form origin identifier, useful for
 99// tracking tasks created by scripts or external integrations.
100func (b *TaskBuilder) FromSource(source, sourceID string) *TaskBuilder {
101	b.req.Source = &source
102	b.req.SourceID = &sourceID
103
104	return b
105}
106
107// Build returns the constructed request.
108func (b *TaskBuilder) Build() *CreateTaskRequest {
109	return &b.req
110}
111
112// TaskUpdateBuilder constructs an [UpdateTaskRequest] via method chaining.
113// Only fields you set will be modified; others remain unchanged.
114//
115//	req := lunatask.NewTaskUpdate().
116//		WithStatus(lunatask.StatusCompleted).
117//		CompletedAt(time.Now()).
118//		Build()
119//	task, err := client.UpdateTask(ctx, taskID, req)
120type TaskUpdateBuilder struct {
121	req UpdateTaskRequest
122}
123
124// NewTaskUpdate starts building a task update.
125func NewTaskUpdate() *TaskUpdateBuilder {
126	return &TaskUpdateBuilder{} //nolint:exhaustruct
127}
128
129// Name changes the task's name.
130func (b *TaskUpdateBuilder) Name(name string) *TaskUpdateBuilder {
131	b.req.Name = &name
132
133	return b
134}
135
136// InArea moves the task to an area. IDs are in the area's settings in the app.
137func (b *TaskUpdateBuilder) InArea(areaID string) *TaskUpdateBuilder {
138	b.req.AreaID = &areaID
139
140	return b
141}
142
143// InGoal moves the task to a goal. IDs are in the goal's settings in the app.
144func (b *TaskUpdateBuilder) InGoal(goalID string) *TaskUpdateBuilder {
145	b.req.GoalID = &goalID
146
147	return b
148}
149
150// WithNote replaces the task's Markdown note.
151func (b *TaskUpdateBuilder) WithNote(note string) *TaskUpdateBuilder {
152	b.req.Note = &note
153
154	return b
155}
156
157// WithStatus sets the workflow status.
158// Use one of the Status* constants (e.g., [StatusNext]).
159func (b *TaskUpdateBuilder) WithStatus(status TaskStatus) *TaskUpdateBuilder {
160	b.req.Status = &status
161
162	return b
163}
164
165// WithMotivation sets why this task matters.
166// Use one of the Motivation* constants (e.g., [MotivationMust]).
167func (b *TaskUpdateBuilder) WithMotivation(motivation Motivation) *TaskUpdateBuilder {
168	b.req.Motivation = &motivation
169
170	return b
171}
172
173// WithEstimate sets the expected duration in minutes (0–720).
174func (b *TaskUpdateBuilder) WithEstimate(minutes int) *TaskUpdateBuilder {
175	b.req.Estimate = &minutes
176
177	return b
178}
179
180// WithPriority sets importance from -2 (lowest) to 2 (highest).
181func (b *TaskUpdateBuilder) WithPriority(priority int) *TaskUpdateBuilder {
182	b.req.Priority = &priority
183
184	return b
185}
186
187// WithEisenhower sets the matrix quadrant (0–4).
188func (b *TaskUpdateBuilder) WithEisenhower(eisenhower int) *TaskUpdateBuilder {
189	b.req.Eisenhower = &eisenhower
190
191	return b
192}
193
194// ScheduledOn sets when the task should appear on your schedule.
195func (b *TaskUpdateBuilder) ScheduledOn(date Date) *TaskUpdateBuilder {
196	b.req.ScheduledOn = &date
197
198	return b
199}
200
201// CompletedAt marks the task completed at a specific time.
202func (b *TaskUpdateBuilder) CompletedAt(t time.Time) *TaskUpdateBuilder {
203	b.req.CompletedAt = &t
204
205	return b
206}
207
208// Build returns the constructed request.
209func (b *TaskUpdateBuilder) Build() *UpdateTaskRequest {
210	return &b.req
211}
212
213// NoteBuilder constructs a [CreateNoteRequest] via method chaining.
214// Note fields are encrypted client-side by Lunatask; the API accepts them
215// on create but returns null on read.
216//
217//	req := lunatask.NewNote().
218//		WithName("Meeting notes").
219//		WithContent("# Summary\n\n...").
220//		InNotebook(notebookID).
221//		Build()
222//	note, err := client.CreateNote(ctx, req)
223type NoteBuilder struct {
224	req CreateNoteRequest
225}
226
227// NewNote starts building a note.
228func NewNote() *NoteBuilder {
229	return &NoteBuilder{} //nolint:exhaustruct
230}
231
232// WithName sets the note's title.
233func (b *NoteBuilder) WithName(name string) *NoteBuilder {
234	b.req.Name = &name
235
236	return b
237}
238
239// WithContent sets the Markdown body.
240func (b *NoteBuilder) WithContent(content string) *NoteBuilder {
241	b.req.Content = &content
242
243	return b
244}
245
246// InNotebook places the note in a notebook. IDs are in the notebook's settings in the app.
247func (b *NoteBuilder) InNotebook(notebookID string) *NoteBuilder {
248	b.req.NotebookID = &notebookID
249
250	return b
251}
252
253// FromSource tags the note with a free-form origin identifier, useful for
254// tracking notes created by scripts or external integrations.
255func (b *NoteBuilder) FromSource(source, sourceID string) *NoteBuilder {
256	b.req.Source = &source
257	b.req.SourceID = &sourceID
258
259	return b
260}
261
262// Build returns the constructed request.
263func (b *NoteBuilder) Build() *CreateNoteRequest {
264	return &b.req
265}
266
267// JournalEntryBuilder constructs a [CreateJournalEntryRequest] via method chaining.
268// Journal content is encrypted client-side; the API accepts it on create but
269// returns null on read.
270//
271//	req := lunatask.NewJournalEntry(lunatask.Today()).
272//		WithContent("Shipped the new feature!").
273//		Build()
274//	entry, err := client.CreateJournalEntry(ctx, req)
275type JournalEntryBuilder struct {
276	req CreateJournalEntryRequest
277}
278
279// NewJournalEntry starts building a journal entry for the given date.
280func NewJournalEntry(date Date) *JournalEntryBuilder {
281	return &JournalEntryBuilder{req: CreateJournalEntryRequest{DateOn: date}} //nolint:exhaustruct
282}
283
284// WithName sets the entry's title. Defaults to the weekday name if omitted.
285func (b *JournalEntryBuilder) WithName(name string) *JournalEntryBuilder {
286	b.req.Name = &name
287
288	return b
289}
290
291// WithContent sets the Markdown body.
292func (b *JournalEntryBuilder) WithContent(content string) *JournalEntryBuilder {
293	b.req.Content = &content
294
295	return b
296}
297
298// Build returns the constructed request.
299func (b *JournalEntryBuilder) Build() *CreateJournalEntryRequest {
300	return &b.req
301}
302
303// PersonBuilder constructs a [CreatePersonRequest] via method chaining.
304// Name fields are encrypted client-side; the API accepts them on create but
305// returns null on read.
306//
307//	req := lunatask.NewPerson().
308//		WithFirstName("Ada").
309//		WithLastName("Lovelace").
310//		WithRelationshipStrength(lunatask.RelationshipCloseFriend).
311//		Build()
312//	person, err := client.CreatePerson(ctx, req)
313type PersonBuilder struct {
314	req CreatePersonRequest
315}
316
317// NewPerson starts building a person entry.
318func NewPerson() *PersonBuilder {
319	return &PersonBuilder{} //nolint:exhaustruct
320}
321
322// WithFirstName sets the person's first name.
323func (b *PersonBuilder) WithFirstName(name string) *PersonBuilder {
324	b.req.FirstName = &name
325
326	return b
327}
328
329// WithLastName sets the person's last name.
330func (b *PersonBuilder) WithLastName(name string) *PersonBuilder {
331	b.req.LastName = &name
332
333	return b
334}
335
336// WithRelationshipStrength categorizes the closeness of the relationship.
337// Use one of the Relationship* constants (e.g., [RelationshipCloseFriend]).
338func (b *PersonBuilder) WithRelationshipStrength(strength RelationshipStrength) *PersonBuilder {
339	b.req.RelationshipStrength = &strength
340
341	return b
342}
343
344// FromSource tags the person with a free-form origin identifier, useful for
345// tracking entries created by scripts or external integrations.
346func (b *PersonBuilder) FromSource(source, sourceID string) *PersonBuilder {
347	b.req.Source = &source
348	b.req.SourceID = &sourceID
349
350	return b
351}
352
353// WithCustomField sets an arbitrary custom field. Lunatask supports "email",
354// "birthday", and "phone" out of the box; other fields must first be defined
355// in the app or [ErrUnprocessableEntity] is returned.
356func (b *PersonBuilder) WithCustomField(key string, value any) *PersonBuilder {
357	if b.req.CustomFields == nil {
358		b.req.CustomFields = make(map[string]any)
359	}
360
361	b.req.CustomFields[key] = value
362
363	return b
364}
365
366// Build returns the constructed request.
367func (b *PersonBuilder) Build() *CreatePersonRequest {
368	return &b.req
369}
370
371// TimelineNoteBuilder constructs a [CreatePersonTimelineNoteRequest] via method chaining.
372// Content is encrypted client-side; the API accepts it on create but returns null on read.
373//
374//	req := lunatask.NewTimelineNote(personID).
375//		OnDate(lunatask.Today()).
376//		WithContent("Had coffee, discussed the project.").
377//		Build()
378//	note, err := client.CreatePersonTimelineNote(ctx, req)
379type TimelineNoteBuilder struct {
380	req CreatePersonTimelineNoteRequest
381}
382
383// NewTimelineNote starts building a timeline note for the given person.
384// Get person IDs from [Client.ListPeople].
385func NewTimelineNote(personID string) *TimelineNoteBuilder {
386	return &TimelineNoteBuilder{req: CreatePersonTimelineNoteRequest{PersonID: personID}} //nolint:exhaustruct
387}
388
389// OnDate sets when this interaction occurred.
390func (b *TimelineNoteBuilder) OnDate(date Date) *TimelineNoteBuilder {
391	b.req.DateOn = &date
392
393	return b
394}
395
396// WithContent sets the Markdown body describing the interaction.
397func (b *TimelineNoteBuilder) WithContent(content string) *TimelineNoteBuilder {
398	b.req.Content = &content
399
400	return b
401}
402
403// Build returns the constructed request.
404func (b *TimelineNoteBuilder) Build() *CreatePersonTimelineNoteRequest {
405	return &b.req
406}