clash_yaml.go 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. package sub
  2. import (
  3. "reflect"
  4. "regexp"
  5. "strings"
  6. "github.com/goccy/go-yaml"
  7. )
  8. type yamlQuotedString string
  9. func (s yamlQuotedString) MarshalYAML() ([]byte, error) {
  10. return []byte("'" + strings.ReplaceAll(string(s), "'", "''") + "'"), nil
  11. }
  12. var yamlNonStringWords = map[string]bool{
  13. "~": true, "null": true, "Null": true, "NULL": true,
  14. "true": true, "True": true, "TRUE": true,
  15. "false": true, "False": true, "FALSE": true,
  16. "yes": true, "Yes": true, "YES": true,
  17. "no": true, "No": true, "NO": true,
  18. "on": true, "On": true, "ON": true,
  19. "off": true, "Off": true, "OFF": true,
  20. "y": true, "Y": true, "n": true, "N": true,
  21. }
  22. var yamlNonStringNumber = regexp.MustCompile(`^(?:` +
  23. `[-+]?[0-9]+|` +
  24. `0[oO]?[0-7]+|0[xX][0-9a-fA-F]+|` +
  25. `[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+|` +
  26. `[-+]?(?:[0-9]*\.[0-9]+|[0-9]+\.?[0-9]*)(?:[eE][-+]?[0-9]+)?|` +
  27. `[-+]?\.(?:inf|Inf|INF)|\.(?:nan|NaN|NAN)|` +
  28. `[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}(?:[Tt ].*)?` +
  29. `)$`)
  30. func yamlScalarIsAmbiguous(s string) bool {
  31. if s == "" {
  32. return false
  33. }
  34. return yamlNonStringWords[s] || yamlNonStringNumber.MatchString(s)
  35. }
  36. func quoteAmbiguousYAMLScalars(v any) any {
  37. if v == nil {
  38. return nil
  39. }
  40. if s, ok := v.(string); ok {
  41. if yamlScalarIsAmbiguous(s) {
  42. return yamlQuotedString(s)
  43. }
  44. return s
  45. }
  46. rv := reflect.ValueOf(v)
  47. switch rv.Kind() {
  48. case reflect.Map:
  49. out := make(map[string]any, rv.Len())
  50. iter := rv.MapRange()
  51. for iter.Next() {
  52. key, ok := iter.Key().Interface().(string)
  53. if !ok {
  54. return v
  55. }
  56. out[key] = quoteAmbiguousYAMLScalars(iter.Value().Interface())
  57. }
  58. return out
  59. case reflect.Slice, reflect.Array:
  60. if rv.Kind() == reflect.Slice && rv.Type().Elem().Kind() == reflect.Uint8 {
  61. return v
  62. }
  63. out := make([]any, rv.Len())
  64. for i := range rv.Len() {
  65. out[i] = quoteAmbiguousYAMLScalars(rv.Index(i).Interface())
  66. }
  67. return out
  68. case reflect.Pointer, reflect.Interface:
  69. if rv.IsNil() {
  70. return v
  71. }
  72. return quoteAmbiguousYAMLScalars(rv.Elem().Interface())
  73. default:
  74. return v
  75. }
  76. }
  77. func marshalClashYAML(config any) ([]byte, error) {
  78. return yaml.Marshal(quoteAmbiguousYAMLScalars(config))
  79. }