github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/fs/config/configmap/configmap.go (about) 1 // Package configmap provides an abstraction for reading and writing config 2 package configmap 3 4 import ( 5 "encoding/base64" 6 "encoding/json" 7 "fmt" 8 "sort" 9 "strings" 10 "unicode" 11 ) 12 13 // Priority of getters 14 type Priority int8 15 16 // Priority levels for AddGetter 17 const ( 18 PriorityNormal Priority = iota 19 PriorityConfig // use for reading from the config 20 PriorityDefault // use for default values 21 PriorityMax 22 ) 23 24 // Getter provides an interface to get config items 25 type Getter interface { 26 // Get should get an item with the key passed in and return 27 // the value. If the item is found then it should return true, 28 // otherwise false. 29 Get(key string) (value string, ok bool) 30 } 31 32 // Setter provides an interface to set config items 33 type Setter interface { 34 // Set should set an item into persistent config store. 35 Set(key, value string) 36 } 37 38 // Mapper provides an interface to read and write config 39 type Mapper interface { 40 Getter 41 Setter 42 } 43 44 // Map provides a wrapper around multiple Setter and 45 // Getter interfaces. 46 type Map struct { 47 setters []Setter 48 getters []getprio 49 } 50 51 type getprio struct { 52 getter Getter 53 priority Priority 54 } 55 56 // New returns an empty Map 57 func New() *Map { 58 return &Map{} 59 } 60 61 // AddGetter appends a getter onto the end of the getters in priority order 62 func (c *Map) AddGetter(getter Getter, priority Priority) *Map { 63 c.getters = append(c.getters, getprio{getter, priority}) 64 sort.SliceStable(c.getters, func(i, j int) bool { 65 return c.getters[i].priority < c.getters[j].priority 66 }) 67 return c 68 } 69 70 // AddSetter appends a setter onto the end of the setters 71 func (c *Map) AddSetter(setter Setter) *Map { 72 c.setters = append(c.setters, setter) 73 return c 74 } 75 76 // ClearSetters removes all the setters set so far 77 func (c *Map) ClearSetters() *Map { 78 c.setters = nil 79 return c 80 } 81 82 // ClearGetters removes all the getters with the priority given 83 func (c *Map) ClearGetters(priority Priority) *Map { 84 getters := c.getters[:0] 85 for _, item := range c.getters { 86 if item.priority != priority { 87 getters = append(getters, item) 88 } 89 } 90 c.getters = getters 91 return c 92 } 93 94 // GetPriority gets an item with the key passed in and return the 95 // value from the first getter to return a result with priority <= 96 // maxPriority. If the item is found then it returns true, otherwise 97 // false. 98 func (c *Map) GetPriority(key string, maxPriority Priority) (value string, ok bool) { 99 for _, item := range c.getters { 100 if item.priority > maxPriority { 101 break 102 } 103 value, ok = item.getter.Get(key) 104 if ok { 105 return value, ok 106 } 107 } 108 return "", false 109 } 110 111 // Get gets an item with the key passed in and return the value from 112 // the first getter. If the item is found then it returns true, 113 // otherwise false. 114 func (c *Map) Get(key string) (value string, ok bool) { 115 return c.GetPriority(key, PriorityMax) 116 } 117 118 // Set sets an item into all the stored setters. 119 func (c *Map) Set(key, value string) { 120 for _, do := range c.setters { 121 do.Set(key, value) 122 } 123 } 124 125 // Simple is a simple Mapper for testing 126 type Simple map[string]string 127 128 // Get the value 129 func (c Simple) Get(key string) (value string, ok bool) { 130 value, ok = c[key] 131 return value, ok 132 } 133 134 // Set the value 135 func (c Simple) Set(key, value string) { 136 c[key] = value 137 } 138 139 // String the map value the same way the config parser does, but with 140 // sorted keys for reproducibility. 141 func (c Simple) String() string { 142 var ks = make([]string, 0, len(c)) 143 for k := range c { 144 ks = append(ks, k) 145 } 146 sort.Strings(ks) 147 var out strings.Builder 148 for _, k := range ks { 149 if out.Len() > 0 { 150 out.WriteRune(',') 151 } 152 out.WriteString(k) 153 out.WriteRune('=') 154 out.WriteRune('\'') 155 for _, ch := range c[k] { 156 out.WriteRune(ch) 157 // Escape ' as '' 158 if ch == '\'' { 159 out.WriteRune(ch) 160 } 161 } 162 out.WriteRune('\'') 163 } 164 return out.String() 165 } 166 167 // Encode from c into a string suitable for putting on the command line 168 func (c Simple) Encode() (string, error) { 169 if len(c) == 0 { 170 return "", nil 171 } 172 buf, err := json.Marshal(c) 173 if err != nil { 174 return "", fmt.Errorf("encode simple map: %w", err) 175 } 176 return base64.RawStdEncoding.EncodeToString(buf), nil 177 } 178 179 // Decode an Encode~d string in into c 180 func (c Simple) Decode(in string) error { 181 // Remove all whitespace from the input string 182 in = strings.Map(func(r rune) rune { 183 if unicode.IsSpace(r) { 184 return -1 185 } 186 return r 187 }, in) 188 if len(in) == 0 { 189 return nil 190 } 191 decodedM, err := base64.RawStdEncoding.DecodeString(in) 192 if err != nil { 193 return fmt.Errorf("decode simple map: %w", err) 194 } 195 err = json.Unmarshal(decodedM, &c) 196 if err != nil { 197 return fmt.Errorf("parse simple map: %w", err) 198 } 199 return nil 200 }