package engine import ( "fmt" "strings" ) type Modifier struct { SetAttributes map[string]*string // key -> value (nil = clear) AddTags []string RemoveTags []string } func NewModifier() *Modifier { return &Modifier{ SetAttributes: make(map[string]*string), AddTags: []string{}, RemoveTags: []string{}, } } // ParseModifier parses command-line args into Modifier func ParseModifier(args []string) (*Modifier, error) { m := NewModifier() for _, arg := range args { if strings.HasPrefix(arg, "+") { // Add tag m.AddTags = append(m.AddTags, strings.TrimPrefix(arg, "+")) } else if strings.HasPrefix(arg, "-") && !strings.Contains(arg, ":") { // Remove tag m.RemoveTags = append(m.RemoveTags, strings.TrimPrefix(arg, "-")) } else if strings.Contains(arg, ":") { // Attribute modification parts := strings.SplitN(arg, ":", 2) key := parts[0] value := parts[1] if value == "" { // Clear attribute (priority: with no value) m.SetAttributes[key] = nil } else { m.SetAttributes[key] = &value } } } return m, nil } // Apply applies modifier to task func (m *Modifier) Apply(task *Task) error { // Apply attribute changes for key, valuePtr := range m.SetAttributes { switch key { case "priority": if valuePtr == nil { task.Priority = PriorityDefault } else { task.Priority = Priority(priorityStringToInt(*valuePtr)) } case "project": task.Project = valuePtr case "due": if valuePtr == nil { task.Due = nil } else { parsed, err := ParseDate(*valuePtr) if err != nil { return fmt.Errorf("invalid due date: %w", err) } task.Due = &parsed } case "scheduled": if valuePtr == nil { task.Scheduled = nil } else { parsed, err := ParseDate(*valuePtr) if err != nil { return fmt.Errorf("invalid scheduled date: %w", err) } task.Scheduled = &parsed } case "wait": if valuePtr == nil { task.Wait = nil } else { parsed, err := ParseDate(*valuePtr) if err != nil { return fmt.Errorf("invalid wait date: %w", err) } task.Wait = &parsed } case "until": if valuePtr == nil { task.Until = nil } else { parsed, err := ParseDate(*valuePtr) if err != nil { return fmt.Errorf("invalid until date: %w", err) } task.Until = &parsed } case "recur": if valuePtr == nil { task.RecurrenceDuration = nil } else { duration, err := ParseRecurrencePattern(*valuePtr) if err != nil { return fmt.Errorf("invalid recurrence: %w", err) } task.RecurrenceDuration = &duration } } } // Apply tag changes for _, tag := range m.AddTags { if err := task.AddTag(tag); err != nil { return err } } for _, tag := range m.RemoveTags { if err := task.RemoveTag(tag); err != nil { return err } } task.Modified = timeNow() return task.Save() } // ApplyToNew applies modifier to a new task (before it's saved) // This is used when creating tasks with modifiers func (m *Modifier) ApplyToNew(task *Task) error { // Apply attribute changes (same as Apply but without Save) for key, valuePtr := range m.SetAttributes { switch key { case "priority": if valuePtr == nil { task.Priority = PriorityDefault } else { task.Priority = Priority(priorityStringToInt(*valuePtr)) } case "project": task.Project = valuePtr case "due": if valuePtr == nil { task.Due = nil } else { parsed, err := ParseDate(*valuePtr) if err != nil { return fmt.Errorf("invalid due date: %w", err) } task.Due = &parsed } case "scheduled": if valuePtr == nil { task.Scheduled = nil } else { parsed, err := ParseDate(*valuePtr) if err != nil { return fmt.Errorf("invalid scheduled date: %w", err) } task.Scheduled = &parsed } case "wait": if valuePtr == nil { task.Wait = nil } else { parsed, err := ParseDate(*valuePtr) if err != nil { return fmt.Errorf("invalid wait date: %w", err) } task.Wait = &parsed } case "until": if valuePtr == nil { task.Until = nil } else { parsed, err := ParseDate(*valuePtr) if err != nil { return fmt.Errorf("invalid until date: %w", err) } task.Until = &parsed } case "recur": if valuePtr == nil { task.RecurrenceDuration = nil } else { duration, err := ParseRecurrencePattern(*valuePtr) if err != nil { return fmt.Errorf("invalid recurrence: %w", err) } task.RecurrenceDuration = &duration } } } // Note: Tags are added after task is saved (in CreateTask function) return nil }