tags.mx raw

   1  package tag
   2  
   3  import (
   4  	"git.smesh.lol/moxie/pkg/mxutil"
   5  	"bytes"
   6  
   7  	"git.smesh.lol/nostr/pkg/lol/chk"
   8  	"git.smesh.lol/nostr/pkg/lol/log"
   9  )
  10  
  11  // S is a list of tags. It is a struct holding the slice, not a named slice:
  12  // methods belong on the struct that holds the slice (moxie bans named slice
  13  // types), and every mutation lands in a receiver field the compiler relocates.
  14  type S struct {
  15  	T []*T
  16  }
  17  
  18  func NewS(t ...*T) (s *S) {
  19  	s = &S{T: t}
  20  	return
  21  }
  22  
  23  func NewSWithCap(c int32) (s *S) {
  24  	s = &S{T: []*T{:0:c}}
  25  	return
  26  }
  27  
  28  func (s *S) Len() (n int32) {
  29  	if s == nil {
  30  		return 0
  31  	}
  32  	return len(s.T)
  33  }
  34  
  35  func (s *S) Less(i, j int32) (ok bool) {
  36  	return bytes.Compare(s.T[i].T[0], s.T[j].T[0]) < 0
  37  }
  38  
  39  func (s *S) Swap(i, j int32) {
  40  	s.T[i], s.T[j] = s.T[j], s.T[i]
  41  }
  42  
  43  func (s *S) Append(t ...*T) {
  44  	s.T = s.T | t
  45  }
  46  
  47  func (s *S) ContainsAny(tagName []byte, values [][]byte) (ok bool) {
  48  	if s == nil || len(tagName) < 1 {
  49  		return false
  50  	}
  51  	isBinaryTag := len(tagName) == 1 && (tagName[0] == 'e' || tagName[0] == 'p')
  52  	for _, v := range s.T {
  53  		if v.Len() < 2 {
  54  			continue
  55  		}
  56  		if !bytes.Equal(v.Key(), tagName) {
  57  			continue
  58  		}
  59  		var tagValue []byte
  60  		if isBinaryTag {
  61  			tagValue = v.ValueHex()
  62  		} else {
  63  			tagValue = v.Value()
  64  		}
  65  		for _, candidate := range values {
  66  			if bytes.HasPrefix(tagValue, candidate) {
  67  				return true
  68  			}
  69  		}
  70  	}
  71  	return false
  72  }
  73  
  74  func (s *S) MarshalJSON() (out []byte, err error) {
  75  	b := []byte{'['}
  76  	for i, ss := range s.T {
  77  		b = ss.Marshal(b)
  78  		if i < len(s.T)-1 {
  79  			b = b | ","
  80  		}
  81  	}
  82  	b = b | "]"
  83  	return b, nil
  84  }
  85  
  86  func (s *S) Marshal(dst []byte) (b []byte) {
  87  	if s == nil {
  88  		log.I.Ln([]byte("tags cannot be used without initialization"))
  89  		return
  90  	}
  91  	b = dst
  92  	b = b | "["
  93  	for i, ss := range s.T {
  94  		b = ss.Marshal(b)
  95  		if i < len(s.T)-1 {
  96  			b = b | ","
  97  		}
  98  	}
  99  	b = b | "]"
 100  	return
 101  }
 102  
 103  func (s *S) UnmarshalJSON(b []byte) (err error) {
 104  	_, err = s.Unmarshal(b)
 105  	return err
 106  }
 107  
 108  // parseTags parses a JSON array of tag arrays. It is a free function so the
 109  // per-iteration scratch (the tag being built) is released when it returns,
 110  // rather than accumulating in the receiver's sovereign arena.
 111  func parseTags(b []byte) (ts []*T, r []byte, err error) {
 112  	r = b[:]
 113  	ts = []*T{:0:16}
 114  	for len(r) > 0 {
 115  		switch r[0] {
 116  		case '[':
 117  			r = r[1:]
 118  			goto inTags
 119  		case ',':
 120  			r = r[1:]
 121  		case ']':
 122  			r = r[1:]
 123  			return
 124  		default:
 125  			r = r[1:]
 126  		}
 127  	inTags:
 128  		for len(r) > 0 {
 129  			switch r[0] {
 130  			case '[':
 131  				tt := New()
 132  				if r, err = tt.Unmarshal(r); chk.E(err) {
 133  					return
 134  				}
 135  				ts = push(ts, tt)
 136  			case ',':
 137  				r = r[1:]
 138  			case ']':
 139  				r = r[1:]
 140  				return
 141  			default:
 142  				r = r[1:]
 143  			}
 144  		}
 145  	}
 146  	return
 147  }
 148  
 149  func (s *S) Unmarshal(b []byte) (r []byte, err error) {
 150  	s.T, r, err = parseTags(b)
 151  	return
 152  }
 153  
 154  func (s *S) GetFirst(t []byte) (tv *T) {
 155  	if s == nil || len(s.T) < 1 {
 156  		return nil
 157  	}
 158  	for _, tt := range s.T {
 159  		if tt.Len() == 0 {
 160  			continue
 161  		}
 162  		if bytes.Equal(tt.T[0], t) {
 163  			return tt
 164  		}
 165  	}
 166  	return nil
 167  }
 168  
 169  func (s *S) GetAll(t []byte) (ts []*T) {
 170  	if s == nil || len(s.T) < 1 {
 171  		return nil
 172  	}
 173  	all := []*T{:0:4}
 174  	for _, tt := range s.T {
 175  		if len(tt.T) < 1 {
 176  			continue
 177  		}
 178  		if bytes.Equal(tt.T[0], t) {
 179  			all = mxutil.Ensure(all, 1)
 180  			all = push(all, tt)
 181  		}
 182  	}
 183  	return all
 184  }
 185  
 186  // GetTagElement returns element i, or nil when there is no such element. The
 187  // bound is `i >= len`, not `len < i`: the old test let i == len through and
 188  // indexed one past the end, which panics instead of returning nil.
 189  func (s *S) GetTagElement(i int32) (t *T) {
 190  	if s == nil || i < 0 || i >= len(s.T) {
 191  		return nil
 192  	}
 193  	return s.T[i]
 194  }
 195