mirror of
https://github.com/rclone/rclone.git
synced 2024-11-25 07:40:08 +08:00
config: replace defaultConfig with a thread-safe in-memory implementation
This commit is contained in:
parent
6ef7178ee4
commit
732bc08ced
|
@ -121,7 +121,7 @@ func init() {
|
||||||
fs.ConfigFileGet = FileGetFlag
|
fs.ConfigFileGet = FileGetFlag
|
||||||
fs.ConfigFileSet = SetValueAndSave
|
fs.ConfigFileSet = SetValueAndSave
|
||||||
configPath = makeConfigPath()
|
configPath = makeConfigPath()
|
||||||
data = defaultStorage{}
|
data = newDefaultStorage()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join directory with filename, and check if exists
|
// Join directory with filename, and check if exists
|
||||||
|
|
|
@ -1,61 +1,114 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
// Default config.Storage which panics with a useful error when used
|
import (
|
||||||
type defaultStorage struct{}
|
"encoding/json"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
var noConfigStorage = "internal error: no config file system found. Did you call configfile.Install()?"
|
// defaultStorage implements config.Storage, providing in-memory config.
|
||||||
|
// Indexed by section, then key.
|
||||||
// GetSectionList returns a slice of strings with names for all the
|
type defaultStorage struct {
|
||||||
// sections
|
mu sync.RWMutex
|
||||||
func (defaultStorage) GetSectionList() []string {
|
sections map[string]map[string]string
|
||||||
panic(noConfigStorage)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasSection returns true if section exists in the config file
|
func newDefaultStorage() *defaultStorage {
|
||||||
func (defaultStorage) HasSection(section string) bool {
|
return &defaultStorage{
|
||||||
panic(noConfigStorage)
|
sections: map[string]map[string]string{},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteSection removes the named section and all config from the
|
// GetSectionList returns a slice of strings with names for all the sections.
|
||||||
// config file
|
func (s *defaultStorage) GetSectionList() []string {
|
||||||
func (defaultStorage) DeleteSection(section string) {
|
s.mu.RLock()
|
||||||
panic(noConfigStorage)
|
defer s.mu.RUnlock()
|
||||||
|
sections := make([]string, 0, len(s.sections))
|
||||||
|
for section := range s.sections {
|
||||||
|
sections = append(sections, section)
|
||||||
|
}
|
||||||
|
return sections
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetKeyList returns the keys in this section
|
// HasSection returns true if section exists in the config.
|
||||||
func (defaultStorage) GetKeyList(section string) []string {
|
func (s *defaultStorage) HasSection(section string) bool {
|
||||||
panic(noConfigStorage)
|
s.mu.RLock()
|
||||||
|
defer s.mu.RUnlock()
|
||||||
|
_, hasSection := s.sections[section]
|
||||||
|
return hasSection
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetValue returns the key in section with a found flag
|
// DeleteSection deletes the specified section.
|
||||||
func (defaultStorage) GetValue(section string, key string) (value string, found bool) {
|
func (s *defaultStorage) DeleteSection(section string) {
|
||||||
panic(noConfigStorage)
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
delete(s.sections, section)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetValue sets the value under key in section
|
// GetKeyList returns the keys in this section.
|
||||||
func (defaultStorage) SetValue(section string, key string, value string) {
|
func (s *defaultStorage) GetKeyList(section string) []string {
|
||||||
panic(noConfigStorage)
|
s.mu.RLock()
|
||||||
|
defer s.mu.RUnlock()
|
||||||
|
theSection := s.sections[section]
|
||||||
|
keys := make([]string, 0, len(theSection))
|
||||||
|
for key := range theSection {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
return keys
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteKey removes the key under section
|
// GetValue returns the key in section with a found flag.
|
||||||
func (defaultStorage) DeleteKey(section string, key string) bool {
|
func (s *defaultStorage) GetValue(section string, key string) (value string, found bool) {
|
||||||
panic(noConfigStorage)
|
s.mu.RLock()
|
||||||
|
defer s.mu.RUnlock()
|
||||||
|
theSection, hasSection := s.sections[section]
|
||||||
|
if !hasSection {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
value, hasValue := theSection[key]
|
||||||
|
return value, hasValue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the config from permanent storage
|
func (s *defaultStorage) SetValue(section string, key string, value string) {
|
||||||
func (defaultStorage) Load() error {
|
s.mu.Lock()
|
||||||
panic(noConfigStorage)
|
defer s.mu.Unlock()
|
||||||
|
theSection, hasSection := s.sections[section]
|
||||||
|
if !hasSection {
|
||||||
|
theSection = map[string]string{}
|
||||||
|
s.sections[section] = theSection
|
||||||
|
}
|
||||||
|
theSection[key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the config to permanent storage
|
func (s *defaultStorage) DeleteKey(section string, key string) bool {
|
||||||
func (defaultStorage) Save() error {
|
s.mu.Lock()
|
||||||
panic(noConfigStorage)
|
defer s.mu.Unlock()
|
||||||
|
theSection, hasSection := s.sections[section]
|
||||||
|
if !hasSection {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, hasKey := theSection[key]
|
||||||
|
if !hasKey {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
delete(s.sections[section], key)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *defaultStorage) Load() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *defaultStorage) Save() error {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialize the config into a string
|
// Serialize the config into a string
|
||||||
func (defaultStorage) Serialize() (string, error) {
|
func (s *defaultStorage) Serialize() (string, error) {
|
||||||
panic(noConfigStorage)
|
s.mu.RLock()
|
||||||
|
defer s.mu.RUnlock()
|
||||||
|
j, err := json.Marshal(s.sections)
|
||||||
|
return string(j), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the interface is satisfied
|
// Check the interface is satisfied
|
||||||
var _ Storage = defaultStorage{}
|
var _ Storage = newDefaultStorage()
|
||||||
|
|
42
fs/config/default_storage_test.go
Normal file
42
fs/config/default_storage_test.go
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDefaultStorage(t *testing.T) {
|
||||||
|
a := assert.New(t)
|
||||||
|
|
||||||
|
ds := newDefaultStorage()
|
||||||
|
|
||||||
|
section := "test"
|
||||||
|
key := "key"
|
||||||
|
val := "something"
|
||||||
|
ds.SetValue(section, key, val)
|
||||||
|
ds.SetValue("some other section", key, val)
|
||||||
|
|
||||||
|
v, hasVal := ds.GetValue(section, key)
|
||||||
|
a.True(hasVal)
|
||||||
|
a.Equal(val, v)
|
||||||
|
|
||||||
|
a.ElementsMatch([]string{section, "some other section"}, ds.GetSectionList())
|
||||||
|
a.True(ds.HasSection(section))
|
||||||
|
a.False(ds.HasSection("nope"))
|
||||||
|
|
||||||
|
a.Equal([]string{key}, ds.GetKeyList(section))
|
||||||
|
|
||||||
|
_, err := ds.Serialize()
|
||||||
|
a.NoError(err)
|
||||||
|
|
||||||
|
a.True(ds.DeleteKey(section, key))
|
||||||
|
a.False(ds.DeleteKey(section, key))
|
||||||
|
a.False(ds.DeleteKey("not there", key))
|
||||||
|
|
||||||
|
_, hasVal = ds.GetValue(section, key)
|
||||||
|
a.False(hasVal)
|
||||||
|
|
||||||
|
ds.DeleteSection(section)
|
||||||
|
a.False(ds.HasSection(section))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user