csv.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. // Copyright 2021 gorse Project Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package base
  15. import (
  16. "bufio"
  17. "fmt"
  18. "strings"
  19. )
  20. // ValidateId validates user/item id. Id cannot be empty and contain [/,].
  21. func ValidateId(text string) error {
  22. text = strings.TrimSpace(text)
  23. if text == "" {
  24. return fmt.Errorf("id cannot be empty")
  25. } else if strings.Contains(text, "/") {
  26. return fmt.Errorf("id cannot contain `/`")
  27. }
  28. return nil
  29. }
  30. // ValidateLabel validates label. Label cannot be empty and contain [/,|].
  31. func ValidateLabel(text string) error {
  32. text = strings.TrimSpace(text)
  33. if text == "" {
  34. return fmt.Errorf("label cannot be empty")
  35. } else if strings.Contains(text, "/") {
  36. return fmt.Errorf("label cannot contain `/`")
  37. } else if strings.Contains(text, "|") {
  38. return fmt.Errorf("label cannot contain `|`")
  39. }
  40. return nil
  41. }
  42. // Escape text for csv.
  43. func Escape(text string) string {
  44. // check if need escape
  45. if !strings.Contains(text, ",") &&
  46. !strings.Contains(text, "\"") &&
  47. !strings.Contains(text, "\n") &&
  48. !strings.Contains(text, "\r") {
  49. return text
  50. }
  51. // start to encode
  52. builder := strings.Builder{}
  53. builder.WriteRune('"')
  54. for _, c := range text {
  55. if c == '"' {
  56. builder.WriteString("\"\"")
  57. } else {
  58. builder.WriteRune(c)
  59. }
  60. }
  61. builder.WriteRune('"')
  62. return builder.String()
  63. }
  64. // ReadLines parse fields of each line for csv file.
  65. func ReadLines(sc *bufio.Scanner, sep string, handler func(int, []string) bool) error {
  66. lineCount := 0 // line number of current position
  67. fields := make([]string, 0) // fields for current line
  68. builder := strings.Builder{} // string builder for current field
  69. quoted := false // whether current position in quote
  70. for sc.Scan() {
  71. // read line
  72. lineStr := sc.Text()
  73. line := []rune(lineStr)
  74. // start of line
  75. if quoted {
  76. builder.WriteString("\r\n")
  77. }
  78. // parse line
  79. for i := 0; i < len(line); i++ {
  80. if string(line[i]) == sep && !quoted {
  81. // end of field
  82. fields = append(fields, builder.String())
  83. builder.Reset()
  84. } else if line[i] == '"' {
  85. if quoted {
  86. if i+1 >= len(line) || line[i+1] != '"' {
  87. // end of quoted
  88. quoted = false
  89. } else {
  90. i++
  91. builder.WriteRune('"')
  92. }
  93. } else {
  94. // start of quoted
  95. quoted = true
  96. }
  97. } else {
  98. builder.WriteRune(line[i])
  99. }
  100. }
  101. // end of line
  102. if !quoted {
  103. fields = append(fields, builder.String())
  104. builder.Reset()
  105. if !handler(lineCount, fields) {
  106. return nil
  107. }
  108. fields = []string{}
  109. }
  110. // increase line count
  111. lineCount++
  112. }
  113. return sc.Err()
  114. }