1
0

inbound_util.go 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  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 maps omitted/zero to 1; explicit negatives are kept
  10. // so a primary inbound can sort ahead of the default.
  11. func normalizeSubSortIndex(v int) int {
  12. if v == 0 {
  13. return 1
  14. }
  15. return v
  16. }
  17. // uniqueNonEmptyStrings returns a deduplicated copy of in with empty strings
  18. // removed, preserving the order of first occurrence.
  19. func uniqueNonEmptyStrings(in []string) []string {
  20. if len(in) == 0 {
  21. return nil
  22. }
  23. seen := make(map[string]struct{}, len(in))
  24. out := make([]string, 0, len(in))
  25. for _, v := range in {
  26. if v == "" {
  27. continue
  28. }
  29. if _, ok := seen[v]; ok {
  30. continue
  31. }
  32. seen[v] = struct{}{}
  33. out = append(out, v)
  34. }
  35. return out
  36. }
  37. // trimmedUniqueEmails trims each address before deduplicating, so the bulk
  38. // client operations treat " a@x " and "a@x" as the same row.
  39. func trimmedUniqueEmails(in []string) []string {
  40. trimmed := make([]string, 0, len(in))
  41. for _, e := range in {
  42. trimmed = append(trimmed, strings.TrimSpace(e))
  43. }
  44. return uniqueNonEmptyStrings(trimmed)
  45. }
  46. // uniqueInts returns a deduplicated copy of in, preserving order of first occurrence.
  47. func uniqueInts(in []int) []int {
  48. if len(in) == 0 {
  49. return nil
  50. }
  51. seen := make(map[int]struct{}, len(in))
  52. out := make([]int, 0, len(in))
  53. for _, v := range in {
  54. if _, ok := seen[v]; ok {
  55. continue
  56. }
  57. seen[v] = struct{}{}
  58. out = append(out, v)
  59. }
  60. return out
  61. }
  62. // chunkStrings splits s into consecutive sub-slices of at most size elements.
  63. // Returns nil for an empty input or non-positive size.
  64. func chunkStrings(s []string, size int) [][]string {
  65. if size <= 0 || len(s) == 0 {
  66. return nil
  67. }
  68. out := make([][]string, 0, (len(s)+size-1)/size)
  69. for i := 0; i < len(s); i += size {
  70. end := min(i+size, len(s))
  71. out = append(out, s[i:end])
  72. }
  73. return out
  74. }
  75. // chunkInts splits s into consecutive sub-slices of at most size elements.
  76. // Returns nil for an empty input or non-positive size.
  77. func chunkInts(s []int, size int) [][]int {
  78. if size <= 0 || len(s) == 0 {
  79. return nil
  80. }
  81. out := make([][]int, 0, (len(s)+size-1)/size)
  82. for i := 0; i < len(s); i += size {
  83. end := min(i+size, len(s))
  84. out = append(out, s[i:end])
  85. }
  86. return out
  87. }