intrange.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. package intrange
  2. import (
  3. "strconv"
  4. "strings"
  5. "unicode"
  6. mapset "github.com/deckarep/golang-set/v2"
  7. )
  8. // IntRange stores a max and min amount for range.
  9. type IntRange struct {
  10. min int
  11. max int
  12. }
  13. // IntRanges is a slice of IntRange.
  14. type IntRanges []IntRange
  15. func makeIntRange(min, max int) IntRange {
  16. return IntRange{
  17. min,
  18. max,
  19. }
  20. }
  21. // Get returns true if the argument n is included in the closed range
  22. // between min and max.
  23. func (r IntRange) Get(n int) bool {
  24. return n >= r.min && n <= r.max
  25. }
  26. // Get returns true if the argument n is included in the closed range
  27. // between min and max of any of the provided IntRanges.
  28. func (rs IntRanges) Get(n int) bool {
  29. for _, r := range rs {
  30. if r.Get(n) {
  31. return true
  32. }
  33. }
  34. return false
  35. }
  36. // ParseNumberMenu parses input for number menus split by spaces or commas
  37. // supports individual selection: 1 2 3 4
  38. // supports range selections: 1-4 10-20
  39. // supports negation: ^1 ^1-4
  40. //
  41. // include and excule holds numbers that should be added and should not be added
  42. // respectively. other holds anything that can't be parsed as an int. This is
  43. // intended to allow words inside of number menus. e.g. 'all' 'none' 'abort'
  44. // of course the implementation is up to the caller, this function mearley parses
  45. // the input and organizes it.
  46. func ParseNumberMenu(input string) (include, exclude IntRanges,
  47. otherInclude, otherExclude mapset.Set[string],
  48. ) {
  49. include = make(IntRanges, 0)
  50. exclude = make(IntRanges, 0)
  51. otherInclude = mapset.NewThreadUnsafeSet[string]()
  52. otherExclude = mapset.NewThreadUnsafeSet[string]()
  53. words := strings.FieldsFunc(input, func(c rune) bool {
  54. return unicode.IsSpace(c) || c == ','
  55. })
  56. for _, word := range words {
  57. var (
  58. num1 int
  59. num2 int
  60. err error
  61. )
  62. invert := false
  63. other := otherInclude
  64. if word[0] == '^' {
  65. invert = true
  66. other = otherExclude
  67. word = word[1:]
  68. }
  69. ranges := strings.SplitN(word, "-", 2)
  70. num1, err = strconv.Atoi(ranges[0])
  71. if err != nil {
  72. other.Add(strings.ToLower(word))
  73. continue
  74. }
  75. if len(ranges) == 2 {
  76. num2, err = strconv.Atoi(ranges[1])
  77. if err != nil {
  78. other.Add(strings.ToLower(word))
  79. continue
  80. }
  81. } else {
  82. num2 = num1
  83. }
  84. mi := min(num1, num2)
  85. ma := max(num1, num2)
  86. if !invert {
  87. include = append(include, makeIntRange(mi, ma))
  88. } else {
  89. exclude = append(exclude, makeIntRange(mi, ma))
  90. }
  91. }
  92. return include, exclude, otherInclude, otherExclude
  93. }