github.com/jacekolszak/noteo@v0.5.0/notes/filter.go (about) 1 package notes 2 3 import ( 4 "context" 5 "fmt" 6 "regexp" 7 "time" 8 9 "github.com/jacekolszak/noteo/date" 10 "github.com/jacekolszak/noteo/tag" 11 ) 12 13 func Filter(ctx context.Context, notes <-chan Note, predicates ...Predicate) (note <-chan Note, errors <-chan error) { 14 out := make(chan Note) 15 errs := make(chan error) 16 go func() { 17 defer close(out) 18 defer close(errs) 19 filterLoop(ctx, notes, predicates, out, errs) 20 }() 21 return out, errs 22 } 23 24 func filterLoop(ctx context.Context, notes <-chan Note, predicates []Predicate, out chan<- Note, errs chan<- error) { 25 for { 26 select { 27 case note, ok := <-notes: 28 if !ok { 29 return 30 } 31 if noteMatches(note, predicates, errs) { 32 out <- note 33 } 34 case <-ctx.Done(): 35 return 36 } 37 } 38 } 39 40 func noteMatches(note Note, predicates []Predicate, errs chan<- error) bool { 41 for _, predicate := range predicates { 42 matches, err := predicate(note) 43 if err != nil { 44 errs <- fmt.Errorf("executing predicate failed on note %s: %v", note.Path(), err) 45 return false 46 } 47 if !matches { 48 return false 49 } 50 } 51 return true 52 } 53 54 type Predicate func(note Note) (bool, error) 55 56 func Tag(t string) Predicate { 57 return func(note Note) (bool, error) { 58 tags, err := note.Tags() 59 if err != nil { 60 return false, err 61 } 62 for _, anotherTag := range tags { 63 if anotherTag.String() == t { 64 return true, nil 65 } 66 } 67 return false, nil 68 } 69 } 70 71 func NoTag(t string) Predicate { 72 return func(note Note) (bool, error) { 73 tags, err := note.Tags() 74 if err != nil { 75 return false, err 76 } 77 for _, anotherTag := range tags { 78 if anotherTag.String() == t { 79 return false, nil 80 } 81 } 82 return true, nil 83 } 84 } 85 86 func TagGrep(regex *regexp.Regexp) Predicate { 87 return func(note Note) (bool, error) { 88 tags, err := note.Tags() 89 if err != nil { 90 return false, err 91 } 92 for _, t := range tags { 93 if regex.MatchString(t.String()) { 94 return true, nil 95 } 96 } 97 return false, nil 98 } 99 } 100 101 func TagGreater(tagNameValue string) (Predicate, error) { 102 return tagNumber(tagNameValue, func(anotherNumber, number int) bool { 103 return anotherNumber > number 104 }) 105 } 106 107 func TagLower(tagNameValue string) (Predicate, error) { 108 return tagNumber(tagNameValue, func(anotherNumber, number int) bool { 109 return anotherNumber < number 110 }) 111 } 112 113 func tagNumber(tagNameValue string, f func(anotherNumber, number int) bool) (Predicate, error) { 114 kv, err := tag.New(tagNameValue) 115 if err != nil { 116 return nil, err 117 } 118 number, err := kv.Number() 119 if err != nil { 120 return nil, err 121 } 122 return func(note Note) (bool, error) { 123 tags, err := note.Tags() 124 if err != nil { 125 return false, err 126 } 127 for _, another := range tags { 128 if another.Name() == kv.Name() { 129 anotherNumber, err := another.Number() 130 if err != nil { 131 return false, fmt.Errorf("error getting number from tag \"%s\": %v", another, err) 132 } 133 return f(anotherNumber, number), nil 134 } 135 } 136 return false, nil 137 }, nil 138 } 139 140 func TagAfter(tagNameValue string) (Predicate, error) { 141 return tagDate(tagNameValue, func(anotherDate, date time.Time) bool { 142 return anotherDate.After(date) 143 }) 144 } 145 146 func TagBefore(tagNameValue string) (Predicate, error) { 147 return tagDate(tagNameValue, func(anotherDate, date time.Time) bool { 148 return anotherDate.Before(date) 149 }) 150 } 151 152 func tagDate(tagNameValue string, f func(anotherDate, date time.Time) bool) (Predicate, error) { 153 kv, err := tag.New(tagNameValue) 154 if err != nil { 155 return nil, err 156 } 157 relativeDate, err := kv.RelativeDate() 158 if err != nil { 159 return nil, err 160 } 161 return func(note Note) (bool, error) { 162 tags, err := note.Tags() 163 if err != nil { 164 return false, err 165 } 166 for _, another := range tags { 167 if another.Name() == kv.Name() { 168 anotherDate, err := another.AbsoluteDate() 169 if err != nil { 170 return false, fmt.Errorf("error getting date from tag \"%s\": %v", another, err) 171 } 172 return f(anotherDate, relativeDate), nil 173 } 174 } 175 return false, nil 176 }, nil 177 } 178 179 func NoTags() Predicate { 180 return func(note Note) (bool, error) { 181 tags, err := note.Tags() 182 if err != nil { 183 return false, err 184 } 185 return len(tags) == 0, nil 186 } 187 } 188 189 func ModifiedAfter(modifiedAfter string) (Predicate, error) { 190 t, err := date.Parse(modifiedAfter) 191 if err != nil { 192 return nil, err 193 } 194 return func(note Note) (bool, error) { 195 modified, err := note.Modified() 196 if err != nil { 197 return false, err 198 } 199 return modified.After(t), nil 200 }, nil 201 } 202 203 func ModifiedBefore(modifiedBefore string) (Predicate, error) { 204 t, err := date.Parse(modifiedBefore) 205 if err != nil { 206 return nil, err 207 } 208 return func(note Note) (bool, error) { 209 modified, err := note.Modified() 210 if err != nil { 211 return false, err 212 } 213 return modified.Before(t), nil 214 }, nil 215 } 216 217 func CreatedAfter(createdAfter string) (Predicate, error) { 218 t, err := date.Parse(createdAfter) 219 if err != nil { 220 return nil, err 221 } 222 return func(note Note) (bool, error) { 223 created, err := note.Created() 224 if err != nil { 225 return false, err 226 } 227 return created.After(t), nil 228 }, nil 229 } 230 231 func CreatedBefore(createdBefore string) (Predicate, error) { 232 t, err := date.Parse(createdBefore) 233 if err != nil { 234 return nil, err 235 } 236 return func(note Note) (bool, error) { 237 created, err := note.Created() 238 if err != nil { 239 return false, err 240 } 241 return created.Before(t), nil 242 }, nil 243 } 244 245 func Grep(expr string) (Predicate, error) { 246 regex, err := regexp.Compile(expr) 247 if err != nil { 248 return nil, err 249 } 250 return func(note Note) (bool, error) { 251 body, err := note.Body() 252 if err != nil { 253 return false, err 254 } 255 return regex.MatchString(body), nil 256 }, nil 257 }