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