validate.go

 1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
 2//
 3// SPDX-License-Identifier: AGPL-3.0-or-later
 4
 5// Package validate provides input validation helpers.
 6package validate
 7
 8import (
 9	"errors"
10	"fmt"
11	"strings"
12
13	"git.secluded.site/go-lunatask"
14	"github.com/google/uuid"
15
16	"git.secluded.site/lune/internal/deeplink"
17)
18
19// ErrInvalidID indicates an ID is not a valid UUID.
20var ErrInvalidID = errors.New("invalid ID: expected UUID format")
21
22// ErrInvalidReference indicates the input is not a valid UUID or deep link.
23var ErrInvalidReference = errors.New("invalid reference: expected UUID or lunatask:// deep link")
24
25// ID validates that the given string is a valid Lunatask ID (UUID).
26func ID(id string) error {
27	if _, err := uuid.Parse(id); err != nil {
28		return fmt.Errorf("%w: %s", ErrInvalidID, id)
29	}
30
31	return nil
32}
33
34// Reference parses a UUID or Lunatask deep link and returns the normalised UUID.
35// Accepts formats:
36//   - UUID: "3bbf1923-64ae-4bcf-96a9-9bb86c799dab"
37//   - Deep link: "lunatask://areas/3bbf1923-64ae-4bcf-96a9-9bb86c799dab"
38func Reference(input string) (string, error) {
39	id, err := deeplink.ParseID(input)
40	if err != nil {
41		return "", fmt.Errorf("%w: %s", ErrInvalidReference, input)
42	}
43
44	return id, nil
45}
46
47// ErrInvalidStatus indicates the status value is not recognized.
48var ErrInvalidStatus = errors.New("invalid status")
49
50// TaskStatus validates and normalizes a task status string.
51// Returns the corresponding lunatask.TaskStatus or an error if invalid.
52func TaskStatus(input string) (lunatask.TaskStatus, error) {
53	status := lunatask.TaskStatus(strings.ToLower(input))
54
55	switch status {
56	case lunatask.StatusLater, lunatask.StatusNext, lunatask.StatusStarted,
57		lunatask.StatusWaiting, lunatask.StatusCompleted:
58		return status, nil
59	default:
60		return "", fmt.Errorf("%w: %s", ErrInvalidStatus, input)
61	}
62}
63
64// ErrInvalidMotivation indicates the motivation value is not recognized.
65var ErrInvalidMotivation = errors.New("invalid motivation")
66
67// Motivation validates and normalizes a motivation string.
68// Returns the corresponding lunatask.Motivation or an error if invalid.
69func Motivation(input string) (lunatask.Motivation, error) {
70	motivation := lunatask.Motivation(strings.ToLower(input))
71
72	switch motivation {
73	case lunatask.MotivationUnknown, lunatask.MotivationMust,
74		lunatask.MotivationShould, lunatask.MotivationWant:
75		return motivation, nil
76	default:
77		return "", fmt.Errorf("%w: %s", ErrInvalidMotivation, input)
78	}
79}
80
81// ErrInvalidRelationship indicates the relationship strength is not recognized.
82var ErrInvalidRelationship = errors.New("invalid relationship strength")
83
84// RelationshipStrength validates and normalizes a relationship strength string.
85// Returns the corresponding lunatask.RelationshipStrength or an error if invalid.
86func RelationshipStrength(input string) (lunatask.RelationshipStrength, error) {
87	rel := lunatask.RelationshipStrength(strings.ToLower(input))
88
89	switch rel {
90	case lunatask.RelationshipFamily, lunatask.RelationshipIntimateFriend,
91		lunatask.RelationshipCloseFriend, lunatask.RelationshipCasualFriend,
92		lunatask.RelationshipAcquaintance, lunatask.RelationshipBusiness,
93		lunatask.RelationshipAlmostStranger:
94		return rel, nil
95	default:
96		return "", fmt.Errorf("%w: %s", ErrInvalidRelationship, input)
97	}
98}