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  }