package models import ( "encoding/json" "fmt" "strings" ) type Node struct { ID string `json:"id"` Title string `json:"title"` Content string `json:"content,omitempty"` DueDate string `json:"due_date,omitempty"` CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` tags []string relations map[string][]string } func NewNode() *Node { return &Node{ relations: make(map[string][]string), } } var PropertyPrefixes = []string{"_type::", "_status::", "_prio::"} func (n *Node) MarshalJSON() ([]byte, error) { type Alias Node return json.Marshal(&struct { *Alias Tags []string `json:"tags,omitempty"` Relations map[string][]string `json:"relations,omitempty"` }{ Alias: (*Alias)(n), Tags: n.tags, Relations: n.relations, }) } func (n *Node) UnmarshalJSON(data []byte) error { type Alias Node aux := &struct { *Alias Tags []string `json:"tags,omitempty"` Relations map[string][]string `json:"relations,omitempty"` }{ Alias: (*Alias)(n), } if err := json.Unmarshal(data, &aux); err != nil { return err } n.tags = aux.Tags if aux.Relations != nil { n.relations = aux.Relations } else { n.relations = make(map[string][]string) } return nil } func (n *Node) Tags() []string { res := make([]string, len(n.tags)) copy(res, n.tags) return res } func (n *Node) Relations() map[string][]string { res := make(map[string][]string) for k, v := range n.relations { cv := make([]string, len(v)) copy(cv, v) res[k] = cv } return res } func (n *Node) HasTag(tag string) bool { for _, t := range n.tags { if t == tag { return true } } return false } func (n *Node) AddTag(tag string) { if tag == "" { return } // check if it's a property tag for _, prefix := range PropertyPrefixes { if strings.HasPrefix(tag, prefix) { // remove existing tags with this prefix var newTags []string for _, t := range n.tags { if !strings.HasPrefix(t, prefix) { newTags = append(newTags, t) } } n.tags = newTags break } } if !n.HasTag(tag) { n.tags = append(n.tags, tag) } } func (n *Node) RemoveTag(tag string) error { if strings.HasPrefix(tag, "_type::") { return fmt.Errorf("cannot remove _type tag") } var newTags []string for _, t := range n.tags { if t != tag { newTags = append(newTags, t) } } n.tags = newTags return nil } func (n *Node) HasRelation(relType RelType, target string) bool { for _, tgt := range n.relations[string(relType)] { if tgt == target { return true } } return false } func (n *Node) AddRelation(relType RelType, target string) { if n.relations == nil { n.relations = make(map[string][]string) } if relType == RelAssignee || relType == RelCreated || relType == RelInNamespace { n.relations[string(relType)] = []string{target} return } if !n.HasRelation(relType, target) { n.relations[string(relType)] = append(n.relations[string(relType)], target) } } func (n *Node) RemoveRelation(relType RelType, target string) error { if relType == RelCreated { return fmt.Errorf("cannot remove created relation") } if n.relations == nil { return nil } var newTgts []string for _, tgt := range n.relations[string(relType)] { if tgt != target { newTgts = append(newTgts, tgt) } } if len(newTgts) == 0 { delete(n.relations, string(relType)) } else { n.relations[string(relType)] = newTgts } return nil } func (n *Node) GetProperty(k string) string { for _, t := range n.tags { if strings.HasPrefix(t, "_") { if p := strings.SplitN(t[1:], "::", 2); len(p) == 2 && p[0] == k { return p[1] } } } return "" } func (n *Node) GetDisplayTags() []string { var tags []string for _, t := range n.tags { if !strings.HasPrefix(t, "_") { tags = append(tags, t) } } return tags }