feat(parser): support multiple values per metadata key in Task
BREAKING CHANGE: The Task struct's Metadata field now maps to slices of strings (map[string][]string) instead of single strings. This allows tasks to have multiple values for the same metadata key. All code interacting with Metadata must be updated to handle slices. Version bumped to v3.0.0.
This commit is contained in:
@@ -23,7 +23,7 @@ type Task struct {
|
||||
Description string `json:"description"` // description with projects/contexts/metadata removed
|
||||
Projects []string `json:"projects"`
|
||||
Contexts []string `json:"contexts"`
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
Metadata map[string][]string `json:"metadata"`
|
||||
}
|
||||
|
||||
func (t Task) ToRemind() string {
|
||||
@@ -37,13 +37,19 @@ func (t Task) ToRemind() string {
|
||||
sb.WriteString(" ++5 INFO \"Calendar: TODO.TX\" ")
|
||||
|
||||
if len(t.Metadata) > 0 {
|
||||
for k, v := range t.Metadata {
|
||||
for k, values := range t.Metadata {
|
||||
if k == "due" {
|
||||
continue
|
||||
}
|
||||
// k must have the first letter uppercase for Remind
|
||||
k = strings.ToUpper(k[:1]) + k[1:]
|
||||
sb.WriteString(fmt.Sprintf("INFO \"%s: %s\" ", k, v))
|
||||
kUpper := strings.ToUpper(k[:1]) + k[1:]
|
||||
if len(values) == 1 {
|
||||
sb.WriteString(fmt.Sprintf("INFO \"%s: %s\" ", kUpper, values[0]))
|
||||
} else {
|
||||
for i, v := range values {
|
||||
sb.WriteString(fmt.Sprintf("INFO \"%s%d: %s\" ", kUpper, i+1, v))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,13 +88,20 @@ func (t Task) ToRemind() string {
|
||||
}
|
||||
|
||||
if len(t.Metadata) > 0 {
|
||||
for k := range t.Metadata {
|
||||
for k, values := range t.Metadata {
|
||||
if k == "due" {
|
||||
continue
|
||||
}
|
||||
// uppercase first letter for Remind
|
||||
k = strings.ToUpper(k[:1]) + k[1:]
|
||||
sb.WriteString(fmt.Sprintf("%%_%s: %%<%s>", k, k))
|
||||
kUpper := strings.ToUpper(k[:1]) + k[1:]
|
||||
if len(values) == 1 {
|
||||
sb.WriteString(fmt.Sprintf("%%_%s: %%<%s>", kUpper, kUpper))
|
||||
} else {
|
||||
for i := range values {
|
||||
kNumbered := fmt.Sprintf("%s%d", kUpper, i+1)
|
||||
sb.WriteString(fmt.Sprintf("%%_%s: %%<%s>", kNumbered, kNumbered))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +194,7 @@ func ParseReader(r io.Reader) ([]Task, []error) {
|
||||
func ParseLine(line string) (Task, error) {
|
||||
t := Task{
|
||||
Raw: line,
|
||||
Metadata: make(map[string]string),
|
||||
Metadata: make(map[string][]string),
|
||||
}
|
||||
|
||||
working := strings.TrimSpace(line)
|
||||
@@ -262,14 +275,14 @@ func ParseLine(line string) (Task, error) {
|
||||
}
|
||||
val += " " + toks[i]
|
||||
}
|
||||
t.Metadata["location"] = val
|
||||
t.Metadata["location"] = append(t.Metadata["location"], val)
|
||||
continue
|
||||
}
|
||||
if !strings.ContainsAny(tok, " \t") && strings.Contains(tok, ":") {
|
||||
parts := strings.SplitN(tok, ":", 2)
|
||||
k, v := parts[0], parts[1]
|
||||
if k != "" && v != "" && !isProtocolKey(k) {
|
||||
t.Metadata[k] = v
|
||||
t.Metadata[k] = append(t.Metadata[k], v)
|
||||
if k == "due" && dateRe.MatchString(v) {
|
||||
if dt, err := time.Parse(dateLayout, v); err == nil {
|
||||
t.DueDate = &dt
|
||||
@@ -277,6 +290,7 @@ func ParseLine(line string) (Task, error) {
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
descParts = append(descParts, tok)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user