inbound_util.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. package service
  2. import "strings"
  3. // sqliteMaxVars is a safe ceiling for the number of bind parameters in a
  4. // single SQL statement. SQLite's SQLITE_MAX_VARIABLE_NUMBER is 999 on builds
  5. // before 3.32 and 32766 after; staying under 999 keeps queries portable
  6. // across forks/old binaries and also bounds per-query memory on truly large
  7. // installs (>32k clients) where even modern SQLite would refuse a single IN.
  8. const sqliteMaxVars = 900
  9. // normalizeSubSortIndex clamps the 1-based subscription sort order. Values
  10. // below 1 arrive from clients that predate the field (omitted form key binds
  11. // to 0) and must not sort ahead of explicitly ranked inbounds.
  12. func normalizeSubSortIndex(v int) int {
  13. if v < 1 {
  14. return 1
  15. }
  16. return v
  17. }
  18. // uniqueNonEmptyStrings returns a deduplicated copy of in with empty strings
  19. // removed, preserving the order of first occurrence.
  20. func uniqueNonEmptyStrings(in []string) []string {
  21. if len(in) == 0 {
  22. return nil
  23. }
  24. seen := make(map[string]struct{}, len(in))
  25. out := make([]string, 0, len(in))
  26. for _, v := range in {
  27. if v == "" {
  28. continue
  29. }
  30. if _, ok := seen[v]; ok {
  31. continue
  32. }
  33. seen[v] = struct{}{}
  34. out = append(out, v)
  35. }
  36. return out
  37. }
  38. // trimmedUniqueEmails trims each address before deduplicating, so the bulk
  39. // client operations treat " a@x " and "a@x" as the same row.
  40. func trimmedUniqueEmails(in []string) []string {
  41. trimmed := make([]string, 0, len(in))
  42. for _, e := range in {
  43. trimmed = append(trimmed, strings.TrimSpace(e))
  44. }
  45. return uniqueNonEmptyStrings(trimmed)
  46. }
  47. // uniqueInts returns a deduplicated copy of in, preserving order of first occurrence.
  48. func uniqueInts(in []int) []int {
  49. if len(in) == 0 {
  50. return nil
  51. }
  52. seen := make(map[int]struct{}, len(in))
  53. out := make([]int, 0, len(in))
  54. for _, v := range in {
  55. if _, ok := seen[v]; ok {
  56. continue
  57. }
  58. seen[v] = struct{}{}
  59. out = append(out, v)
  60. }
  61. return out
  62. }
  63. // chunkStrings splits s into consecutive sub-slices of at most size elements.
  64. // Returns nil for an empty input or non-positive size.
  65. func chunkStrings(s []string, size int) [][]string {
  66. if size <= 0 || len(s) == 0 {
  67. return nil
  68. }
  69. out := make([][]string, 0, (len(s)+size-1)/size)
  70. for i := 0; i < len(s); i += size {
  71. end := min(i+size, len(s))
  72. out = append(out, s[i:end])
  73. }
  74. return out
  75. }
  76. // chunkInts splits s into consecutive sub-slices of at most size elements.
  77. // Returns nil for an empty input or non-positive size.
  78. func chunkInts(s []int, size int) [][]int {
  79. if size <= 0 || len(s) == 0 {
  80. return nil
  81. }
  82. out := make([][]int, 0, (len(s)+size-1)/size)
  83. for i := 0; i < len(s); i += size {
  84. end := min(i+size, len(s))
  85. out = append(out, s[i:end])
  86. }
  87. return out
  88. }