diff --git a/models/node.go b/models/node.go index 7b136ef..00946e0 100644 --- a/models/node.go +++ b/models/node.go @@ -1,82 +1,30 @@ package models import ( - "encoding/json" - "fmt" "strings" ) +var PropertyPrefixes = []string{"_type::", "_status::", "_prio::"} + 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 + 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 `json:"tags,omitempty"` + Relations map[string][]string `json:"relations,omitempty"` } func NewNode() *Node { return &Node{ - relations: make(map[string][]string), + 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 { + for _, t := range n.Tags { if t == tag { return true } @@ -88,41 +36,35 @@ 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 { + for _, t := range n.Tags { if !strings.HasPrefix(t, prefix) { newTags = append(newTags, t) } } - n.tags = newTags + n.Tags = newTags break } } if !n.HasTag(tag) { - n.tags = append(n.tags, 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") - } +func (n *Node) RemoveTag(tag string) { var newTags []string - for _, t := range n.tags { + for _, t := range n.Tags { if t != tag { newTags = append(newTags, t) } } - n.tags = newTags - return nil + n.Tags = newTags } func (n *Node) HasRelation(relType RelType, target string) bool { - for _, tgt := range n.relations[string(relType)] { + for _, tgt := range n.Relations[string(relType)] { if tgt == target { return true } @@ -131,41 +73,37 @@ func (n *Node) HasRelation(relType RelType, target string) bool { } func (n *Node) AddRelation(relType RelType, target string) { - if n.relations == nil { - n.relations = make(map[string][]string) + if n.Relations == nil { + n.Relations = make(map[string][]string) } if relType == RelAssignee || relType == RelCreated || relType == RelInNamespace { - n.relations[string(relType)] = []string{target} + n.Relations[string(relType)] = []string{target} return } if !n.HasRelation(relType, target) { - n.relations[string(relType)] = append(n.relations[string(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 +func (n *Node) RemoveRelation(relType RelType, target string) { + if n.Relations == nil { + return } var newTgts []string - for _, tgt := range n.relations[string(relType)] { + for _, tgt := range n.Relations[string(relType)] { if tgt != target { newTgts = append(newTgts, tgt) } } if len(newTgts) == 0 { - delete(n.relations, string(relType)) + delete(n.Relations, string(relType)) } else { - n.relations[string(relType)] = newTgts + n.Relations[string(relType)] = newTgts } - return nil } func (n *Node) GetProperty(k string) string { - for _, t := range n.tags { + 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] @@ -177,7 +115,7 @@ func (n *Node) GetProperty(k string) string { func (n *Node) GetDisplayTags() []string { var tags []string - for _, t := range n.tags { + for _, t := range n.Tags { if !strings.HasPrefix(t, "_") { tags = append(tags, t) } diff --git a/output/output.go b/output/output.go index b252746..3f2a269 100644 --- a/output/output.go +++ b/output/output.go @@ -77,7 +77,7 @@ func PrintNodes(w io.Writer, svc service.NodeService, nodes []*models.Node, json }) for _, n := range nodes { - n_rels := n.Relations() + n_rels := n.Relations ns_rel_node_ids := n_rels[string(models.RelInNamespace)] ns_rel_node_titles := make([]string, 0, len(ns_rel_node_ids)) for _, id := range ns_rel_node_ids { @@ -125,7 +125,7 @@ func PrintNode(w io.Writer, svc service.NodeService, n *models.Node, jsonOut boo fmt.Fprintf(w, "\n tags: %s\n", cPrimary.Sprint(strings.Join(tags, " • "))) } - n_rels := n.Relations() + n_rels := n.Relations for relType := range n_rels { rel_node_ids := n_rels[string(relType)] if len(rel_node_ids) > 0 { diff --git a/service/node_service_impl.go b/service/node_service_impl.go index bf337f0..9fd70ef 100644 --- a/service/node_service_impl.go +++ b/service/node_service_impl.go @@ -70,7 +70,7 @@ func (s *nodeServiceImpl) getAccessContext() (*accessContext, error) { if err != nil { return nil, err } - rels := userNode.Relations() + rels := userNode.Relations for _, nsID := range rels[string(models.RelHasWriteAccess)] { ctx.writable[nsID] = true ctx.readable[nsID] = true @@ -83,7 +83,7 @@ func (s *nodeServiceImpl) getAccessContext() (*accessContext, error) { // nodeNamespaceID returns the first in_namespace target of n, or "" if none. func (s *nodeServiceImpl) nodeNamespaceID(n *models.Node) string { - ids := n.Relations()[string(models.RelInNamespace)] + ids := n.Relations[string(models.RelInNamespace)] if len(ids) == 0 { return "" } @@ -375,18 +375,17 @@ func (s *nodeServiceImpl) Update(id string, input UpdateInput) (*models.Node, er // Compute new tag set using the model's AddTag/RemoveTag to preserve // property-prefix replacement semantics. tmp := models.NewNode() - for _, t := range current.Tags() { + for _, t := range current.Tags { tmp.AddTag(t) } for _, t := range input.AddTags { tmp.AddTag(t) } for _, t := range input.RemoveTags { - tmp.RemoveTag(t) //nolint: the error is only for _type:: removal, which is intentionally prevented + tmp.RemoveTag(t) } - // Sync tags to store. - currentTags, newTags := current.Tags(), tmp.Tags() + currentTags, newTags := current.Tags, tmp.Tags for _, t := range currentTags { if !slices.Contains(newTags, t) { if err := st.RemoveTag(id, t); err != nil { @@ -409,7 +408,7 @@ func (s *nodeServiceImpl) Update(id string, input UpdateInput) (*models.Node, er } } - currentRels := current.Relations() + currentRels := current.Relations for _, ri := range input.AddRels { resolved, err := s.resolveRelTarget(st, ri) if err != nil { @@ -502,7 +501,7 @@ func (s *nodeServiceImpl) checkBlockers(id string) error { func (s *nodeServiceImpl) syncMentions(st store.Store, id string, current *models.Node, newTitle, newContent string) error { existingMentionIDs := make(map[string]bool) - for _, uid := range current.Relations()[string(models.RelMentions)] { + for _, uid := range current.Relations[string(models.RelMentions)] { existingMentionIDs[uid] = true } mentionedUserIDs := make(map[string]bool)