github.com/jacekolszak/noteo@v0.5.0/notes/top.go (about)

     1  package notes
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sort"
     7  	"time"
     8  )
     9  
    10  func Top(ctx context.Context, limit int, notes <-chan Note, less Less) (note <-chan Note, errors <-chan error) {
    11  	out := make(chan Note)
    12  	errs := make(chan error)
    13  
    14  	go func() {
    15  		defer close(out)
    16  		defer close(errs)
    17  		slice := collectNotes(ctx, notes)
    18  		sortNotes(slice, less, errs)
    19  		if len(slice) > limit {
    20  			slice = slice[:limit]
    21  		}
    22  		for _, note := range slice {
    23  			out <- note
    24  		}
    25  	}()
    26  	return out, errs
    27  }
    28  
    29  func collectNotes(ctx context.Context, notes <-chan Note) []Note {
    30  	var collectedNotes []Note
    31  	for {
    32  		select {
    33  		case note, ok := <-notes:
    34  			if !ok {
    35  				return collectedNotes
    36  			}
    37  			collectedNotes = append(collectedNotes, note)
    38  		case <-ctx.Done():
    39  			return nil
    40  		}
    41  	}
    42  }
    43  
    44  func sortNotes(slice []Note, less Less, errs chan<- error) {
    45  	sort.Slice(slice, func(i, j int) bool {
    46  		l, err := less(slice[i], slice[j])
    47  		if err != nil {
    48  			errs <- fmt.Errorf("comparing notes failed %s and %s: %v", slice[i].Path(), slice[j].Path(), err)
    49  		}
    50  		return l
    51  	})
    52  }
    53  
    54  type Less func(i, j Note) (bool, error)
    55  
    56  var ModifiedDesc Less = func(i, j Note) (bool, error) {
    57  	firstModified, e := i.Modified()
    58  	if e != nil {
    59  		return false, e
    60  	}
    61  	secondModified, e := j.Modified()
    62  	if e != nil {
    63  		return false, e
    64  	}
    65  	return firstModified.After(secondModified), nil
    66  }
    67  
    68  var ModifiedAsc Less = func(i, j Note) (bool, error) {
    69  	firstModified, e := i.Modified()
    70  	if e != nil {
    71  		return false, e
    72  	}
    73  	secondModified, e := j.Modified()
    74  	if e != nil {
    75  		return false, e
    76  	}
    77  	return firstModified.Before(secondModified), nil
    78  }
    79  
    80  var CreatedDesc Less = func(first, second Note) (bool, error) {
    81  	firstCreated, e := first.Created()
    82  	if e != nil {
    83  		return false, e
    84  	}
    85  	secondCreated, e := second.Created()
    86  	if e != nil {
    87  		return false, e
    88  	}
    89  	return firstCreated.After(secondCreated), nil
    90  }
    91  
    92  var CreatedAsc Less = func(first, second Note) (bool, error) {
    93  	firstCreated, e := first.Created()
    94  	if e != nil {
    95  		return false, e
    96  	}
    97  	secondCreated, e := second.Created()
    98  	if e != nil {
    99  		return false, e
   100  	}
   101  	return firstCreated.Before(secondCreated), nil
   102  }
   103  
   104  func TagDateDesc(name string) Less {
   105  	return tagDateLess(name, func(first, second time.Time) bool {
   106  		return first.After(second)
   107  	})
   108  }
   109  
   110  func TagDateAsc(name string) Less {
   111  	return tagDateLess(name, func(first, second time.Time) bool {
   112  		return first.Before(second)
   113  	})
   114  }
   115  
   116  func tagDateLess(name string, less func(first, second time.Time) bool) Less {
   117  	return func(first, second Note) (bool, error) {
   118  		firstTag, found, err := FindTagByName(first, name)
   119  		if err != nil || !found {
   120  			return false, err
   121  		}
   122  		secondTag, found, err := FindTagByName(second, name)
   123  		if err != nil || !found {
   124  			return true, err
   125  		}
   126  		firstDate, err := firstTag.AbsoluteDate()
   127  		if err != nil {
   128  			return false, err
   129  		}
   130  		secondDate, err := secondTag.AbsoluteDate()
   131  		if err != nil {
   132  			return false, err
   133  		}
   134  		return less(firstDate, secondDate), nil
   135  	}
   136  }
   137  
   138  func TagNumberDesc(name string) Less {
   139  	return tagNumberLess(name, func(first, second int) bool {
   140  		return first > second
   141  	})
   142  }
   143  
   144  func TagNumberAsc(name string) Less {
   145  	return tagNumberLess(name, func(first, second int) bool {
   146  		return first < second
   147  	})
   148  }
   149  
   150  func tagNumberLess(name string, less func(first, second int) bool) Less {
   151  	return func(first, second Note) (bool, error) {
   152  		firstTag, found, err := FindTagByName(first, name)
   153  		if err != nil || !found {
   154  			return false, err
   155  		}
   156  		secondTag, found, err := FindTagByName(second, name)
   157  		if err != nil || !found {
   158  			return true, err
   159  		}
   160  		firstNumber, err := firstTag.Number()
   161  		if err != nil {
   162  			return false, err
   163  		}
   164  		secondNumber, err := secondTag.Number()
   165  		if err != nil {
   166  			return false, err
   167  		}
   168  		return less(firstNumber, secondNumber), nil
   169  	}
   170  }