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 = ¬e
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 = ¬e
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 = ¬ebookID
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}