1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5package lunatask
6
7import (
8 "errors"
9 "fmt"
10 "strings"
11)
12
13// Errors returned by deep link operations.
14var (
15 // ErrInvalidDeepLink is returned when parsing a malformed deep link.
16 ErrInvalidDeepLink = errors.New("invalid deep link")
17 // ErrInvalidResource is returned when a resource type is unknown.
18 ErrInvalidResource = errors.New("invalid resource")
19 // ErrInvalidUUID is returned when the UUID portion is empty.
20 ErrInvalidUUID = errors.New("invalid UUID")
21)
22
23// Resource represents a Lunatask resource type for deep links.
24type Resource string
25
26// Valid resource types for deep links.
27const (
28 ResourceArea Resource = "areas"
29 ResourceGoal Resource = "goals"
30 ResourceTask Resource = "tasks"
31 ResourceNote Resource = "notes"
32 ResourcePerson Resource = "people"
33 ResourceNotebook Resource = "notebooks"
34)
35
36// ParseDeepLink extracts resource type and UUID from a Lunatask deep link
37// or plain UUID. Accepts "lunatask://tasks/uuid" or plain UUID strings.
38// When a plain UUID is provided, the resource type will be empty.
39func ParseDeepLink(input string) (Resource, string, error) {
40 if input == "" {
41 return "", "", fmt.Errorf("%w: empty input", ErrInvalidDeepLink)
42 }
43
44 // Check for lunatask:// prefix
45 const prefix = "lunatask://"
46 if !strings.HasPrefix(input, prefix) {
47 // Treat as plain UUID
48 return "", input, nil
49 }
50
51 // Remove prefix and split
52 remainder := strings.TrimPrefix(input, prefix)
53 parts := strings.SplitN(remainder, "/", 2) //nolint:mnd // split resource/uuid
54
55 if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
56 return "", "", fmt.Errorf("%w: %q", ErrInvalidDeepLink, input)
57 }
58
59 resource := Resource(parts[0])
60 uuid := parts[1]
61
62 // Validate resource type
63 switch resource {
64 case ResourceArea, ResourceGoal, ResourceTask, ResourceNote, ResourcePerson, ResourceNotebook:
65 // valid
66 default:
67 return "", "", fmt.Errorf("%w: %q", ErrInvalidResource, resource)
68 }
69
70 return resource, uuid, nil
71}
72
73// BuildDeepLink constructs a Lunatask deep link from resource type and ID.
74// Returns "lunatask://resource/uuid".
75func BuildDeepLink(resource Resource, id string) (string, error) {
76 if id == "" {
77 return "", ErrInvalidUUID
78 }
79
80 // Validate resource type
81 switch resource {
82 case ResourceArea, ResourceGoal, ResourceTask, ResourceNote, ResourcePerson, ResourceNotebook:
83 // valid
84 default:
85 return "", fmt.Errorf("%w: %q", ErrInvalidResource, resource)
86 }
87
88 return fmt.Sprintf("lunatask://%s/%s", resource, id), nil
89}