iniconfig.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. package config
  2. import (
  3. "bufio"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "os"
  8. "reflect"
  9. "sort"
  10. "strconv"
  11. "strings"
  12. )
  13. type configSection struct {
  14. name string
  15. values map[string]interface{}
  16. }
  17. /*
  18. Config holds the contents of an ini file organized into sections.
  19. */
  20. type Config struct {
  21. configSection
  22. sections map[string]*configSection
  23. }
  24. /*
  25. LoadConfiguration takes a path, treats it as a file and scans it for an ini configuration.
  26. */
  27. func LoadConfiguration(path string) (*Config, error) {
  28. config := new(Config)
  29. err := config.InitializeFromPath(path)
  30. if err != nil {
  31. if os.Getenv("CAN_GO_TEST") == "1" {
  32. config.values = make(map[string]interface{})
  33. return config, nil
  34. }
  35. return nil, err
  36. }
  37. return config, nil
  38. }
  39. /*
  40. LoadConfigurationFromReader takes a reader and scans it for an ini configuration.
  41. The caller should close the reader.
  42. */
  43. func LoadConfigurationFromReader(input io.Reader) (*Config, error) {
  44. config := new(Config)
  45. err := config.InitializeFromReader(input)
  46. if err != nil {
  47. return nil, err
  48. }
  49. return config, nil
  50. }
  51. /*
  52. InitializeFromPath takes a path, treats it as a file and scans it for an ini configuration.
  53. */
  54. func (config *Config) InitializeFromPath(path string) error {
  55. f, err := os.Open(path)
  56. if err != nil {
  57. return err
  58. }
  59. defer f.Close()
  60. return config.InitializeFromReader(bufio.NewReader(f))
  61. }
  62. /*
  63. InitializeFromReader takes a reader and scans it for an ini configuration.
  64. The caller should close the reader.
  65. */
  66. func (config *Config) InitializeFromReader(input io.Reader) error {
  67. var currentSection *configSection
  68. scanner := bufio.NewScanner(input)
  69. config.values = make(map[string]interface{})
  70. config.sections = make(map[string]*configSection)
  71. for scanner.Scan() {
  72. curLine := scanner.Text()
  73. curLine = strings.TrimSpace(curLine)
  74. if len(curLine) == 0 {
  75. continue // ignore empty lines
  76. }
  77. if strings.HasPrefix(curLine, ";") || strings.HasPrefix(curLine, "#") {
  78. continue // comment
  79. }
  80. if strings.HasPrefix(curLine, "[") {
  81. if !strings.HasSuffix(curLine, "]") {
  82. return errors.New("mini: section names must be surrounded by [ and ], as in [section]")
  83. }
  84. sectionName := curLine[1 : len(curLine)-1]
  85. if sect, ok := config.sections[sectionName]; !ok { //reuse sections
  86. currentSection = new(configSection)
  87. currentSection.name = sectionName
  88. currentSection.values = make(map[string]interface{})
  89. config.sections[currentSection.name] = currentSection
  90. } else {
  91. currentSection = sect
  92. }
  93. continue
  94. }
  95. index := strings.Index(curLine, "=")
  96. if index <= 0 {
  97. return errors.New("mini: configuration format requires an equals between the key and value")
  98. }
  99. key := strings.ToLower(strings.TrimSpace(curLine[0:index]))
  100. isArray := strings.HasSuffix(key, "[]")
  101. if isArray {
  102. key = key[0 : len(key)-2]
  103. }
  104. value := strings.TrimSpace(curLine[index+1:])
  105. value = strings.Trim(value, "\"'") //clear quotes
  106. valueMap := config.values
  107. if currentSection != nil {
  108. valueMap = currentSection.values
  109. }
  110. if isArray {
  111. arr := valueMap[key]
  112. if arr == nil {
  113. arr = make([]interface{}, 0)
  114. valueMap[key] = arr
  115. }
  116. valueMap[key] = append(arr.([]interface{}), value)
  117. } else {
  118. valueMap[key] = value
  119. }
  120. }
  121. return scanner.Err()
  122. }
  123. /*
  124. SetName sets the config's name, which allows it to be returned in SectionNames, or in get functions that take a name.
  125. */
  126. func (config *Config) SetName(name string) {
  127. config.name = name
  128. }
  129. //Return non-array values
  130. func get(values map[string]interface{}, key string) interface{} {
  131. if len(key) == 0 || values == nil {
  132. return nil
  133. }
  134. key = strings.ToLower(key)
  135. val, ok := values[key]
  136. if ok {
  137. switch val.(type) {
  138. case []interface{}:
  139. return nil
  140. default:
  141. return val
  142. }
  143. }
  144. return nil
  145. }
  146. //Return array values
  147. func getArray(values map[string]interface{}, key string) []interface{} {
  148. if len(key) == 0 || values == nil {
  149. return nil
  150. }
  151. key = strings.ToLower(key)
  152. val, ok := values[key]
  153. if ok {
  154. switch v := val.(type) {
  155. case []interface{}:
  156. return v
  157. default:
  158. retVal := make([]interface{}, 1)
  159. retVal[0] = val
  160. return retVal
  161. }
  162. }
  163. return nil
  164. }
  165. func getString(values map[string]interface{}, key string, def string) string {
  166. val := get(values, key)
  167. if val != nil {
  168. str, err := strconv.Unquote(fmt.Sprintf("\"%v\"", val))
  169. if err == nil {
  170. return str
  171. }
  172. return def
  173. }
  174. return def
  175. }
  176. func getBoolean(values map[string]interface{}, key string, def bool) bool {
  177. val := get(values, key)
  178. if val != nil {
  179. retVal, err := strconv.ParseBool(fmt.Sprint(val))
  180. if err != nil {
  181. return def
  182. }
  183. return retVal
  184. }
  185. return def
  186. }
  187. func getInteger(values map[string]interface{}, key string, def int64) int64 {
  188. val := get(values, key)
  189. if val != nil {
  190. retVal, err := strconv.ParseInt(fmt.Sprint(val), 0, 64)
  191. if err != nil {
  192. return def
  193. }
  194. return retVal
  195. }
  196. return def
  197. }
  198. func getFloat(values map[string]interface{}, key string, def float64) float64 {
  199. val := get(values, key)
  200. if val != nil {
  201. retVal, err := strconv.ParseFloat(fmt.Sprint(val), 64)
  202. if err != nil {
  203. return def
  204. }
  205. return retVal
  206. }
  207. return def
  208. }
  209. func getStrings(values map[string]interface{}, key string) []string {
  210. val := getArray(values, key)
  211. if val != nil {
  212. retVal := make([]string, len(val))
  213. var err error
  214. for i, v := range val {
  215. retVal[i], err = strconv.Unquote(fmt.Sprintf("\"%v\"", v))
  216. if err != nil {
  217. return nil
  218. }
  219. }
  220. return retVal
  221. }
  222. return nil
  223. }
  224. func getIntegers(values map[string]interface{}, key string) []int64 {
  225. val := getArray(values, key)
  226. if val != nil {
  227. retVal := make([]int64, len(val))
  228. var err error
  229. for i, v := range val {
  230. retVal[i], err = strconv.ParseInt(fmt.Sprint(v), 0, 64)
  231. if err != nil {
  232. return nil
  233. }
  234. }
  235. return retVal
  236. }
  237. return nil
  238. }
  239. func getFloats(values map[string]interface{}, key string) []float64 {
  240. val := getArray(values, key)
  241. if val != nil {
  242. retVal := make([]float64, len(val))
  243. var err error
  244. for i, v := range val {
  245. retVal[i], err = strconv.ParseFloat(fmt.Sprint(v), 64)
  246. if err != nil {
  247. return nil
  248. }
  249. }
  250. return retVal
  251. }
  252. return nil
  253. }
  254. /*
  255. String looks for the specified key and returns it as a string. If not found the default value def is returned.
  256. */
  257. func (config *Config) String(key string, def string) string {
  258. return getString(config.values, key, def)
  259. }
  260. /*
  261. Boolean looks for the specified key and returns it as a bool. If not found the default value def is returned.
  262. */
  263. func (config *Config) Boolean(key string, def bool) bool {
  264. return getBoolean(config.values, key, def)
  265. }
  266. /*
  267. Integer looks for the specified key and returns it as an int. If not found the default value def is returned.
  268. */
  269. func (config *Config) Integer(key string, def int64) int64 {
  270. return getInteger(config.values, key, def)
  271. }
  272. /*
  273. Float looks for the specified key and returns it as a float. If not found the default value def is returned.
  274. */
  275. func (config *Config) Float(key string, def float64) float64 {
  276. return getFloat(config.values, key, def)
  277. }
  278. /*
  279. Strings looks for an array of strings under the provided key.
  280. If no matches are found nil is returned. If only one matches an array of 1 is returned.
  281. */
  282. func (config *Config) Strings(key string) []string {
  283. return getStrings(config.values, key)
  284. }
  285. /*
  286. Integers looks for an array of ints under the provided key.
  287. If no matches are found nil is returned.
  288. */
  289. func (config *Config) Integers(key string) []int64 {
  290. return getIntegers(config.values, key)
  291. }
  292. /*
  293. Floats looks for an array of floats under the provided key.
  294. If no matches are found nil is returned.
  295. */
  296. func (config *Config) Floats(key string) []float64 {
  297. return getFloats(config.values, key)
  298. }
  299. func (config *Config) sectionForName(sectionName string) *configSection {
  300. if len(sectionName) == 0 || sectionName == config.name {
  301. return &(config.configSection)
  302. }
  303. return config.sections[sectionName]
  304. }
  305. /*
  306. StringFromSection looks for the specified key and returns it as a string. If not found the default value def is returned.
  307. If the section name matches the config.name or "" the global data is searched.
  308. */
  309. func (config *Config) StringFromSection(sectionName string, key string, def string) string {
  310. section := config.sectionForName(sectionName)
  311. if section != nil {
  312. return getString(section.values, key, def)
  313. }
  314. return def
  315. }
  316. /*
  317. BooleanFromSection looks for the specified key and returns it as a boolean. If not found the default value def is returned.
  318. If the section name matches the config.name or "" the global data is searched.
  319. */
  320. func (config *Config) BooleanFromSection(sectionName string, key string, def bool) bool {
  321. section := config.sectionForName(sectionName)
  322. if section != nil {
  323. return getBoolean(section.values, key, def)
  324. }
  325. return def
  326. }
  327. /*
  328. IntegerFromSection looks for the specified key and returns it as an int64. If not found the default value def is returned.
  329. If the section name matches the config.name or "" the global data is searched.
  330. */
  331. func (config *Config) IntegerFromSection(sectionName string, key string, def int64) int64 {
  332. section := config.sectionForName(sectionName)
  333. if section != nil {
  334. return getInteger(section.values, key, def)
  335. }
  336. return def
  337. }
  338. /*
  339. FloatFromSection looks for the specified key and returns it as a float. If not found the default value def is returned.
  340. If the section name matches the config.name or "" the global data is searched.
  341. */
  342. func (config *Config) FloatFromSection(sectionName string, key string, def float64) float64 {
  343. section := config.sectionForName(sectionName)
  344. if section != nil {
  345. return getFloat(section.values, key, def)
  346. }
  347. return def
  348. }
  349. /*
  350. StringsFromSection returns the value of an array key, if the value of the key is a non-array, then
  351. that value is returned in an array of length 1.
  352. If the section name matches the config.name or "" the global data is searched.
  353. */
  354. func (config *Config) StringsFromSection(sectionName string, key string) []string {
  355. section := config.sectionForName(sectionName)
  356. if section != nil {
  357. return getStrings(section.values, key)
  358. }
  359. return nil
  360. }
  361. /*
  362. IntegersFromSection looks for an array of integers in the provided section and under the provided key.
  363. If no matches are found nil is returned.
  364. */
  365. func (config *Config) IntegersFromSection(sectionName string, key string) []int64 {
  366. section := config.sectionForName(sectionName)
  367. if section != nil {
  368. return getIntegers(section.values, key)
  369. }
  370. return nil
  371. }
  372. /*
  373. FloatsFromSection looks for an array of floats in the provided section and under the provided key.
  374. If no matches are found nil is returned.
  375. If the section name matches the config.name or "" the global data is searched.
  376. */
  377. func (config *Config) FloatsFromSection(sectionName string, key string) []float64 {
  378. section := config.sectionForName(sectionName)
  379. if section != nil {
  380. return getFloats(section.values, key)
  381. }
  382. return nil
  383. }
  384. /*
  385. DataFromSection reads the values of a section into a struct. The values should be of the types:
  386. bool
  387. string
  388. []string
  389. int64
  390. []int64
  391. float64
  392. []float64
  393. Values that are missing in the section are not set, and values that are missing in the
  394. struct but present in the section are ignored.
  395. If the section name matches the config.name or "" the global data is searched.
  396. */
  397. func (config *Config) DataFromSection(sectionName string, data interface{}) bool {
  398. section := config.sectionForName(sectionName)
  399. if section == nil {
  400. return false
  401. }
  402. values := section.values
  403. fields := reflect.ValueOf(data).Elem()
  404. dataType := fields.Type()
  405. for i := 0; i < fields.NumField(); i++ {
  406. field := fields.Field(i)
  407. if !field.CanSet() {
  408. continue
  409. }
  410. fieldType := dataType.Field(i)
  411. fieldName := fieldType.Name
  412. switch field.Type().Kind() {
  413. case reflect.Bool:
  414. field.SetBool(getBoolean(values, fieldName, field.Interface().(bool)))
  415. case reflect.Int64:
  416. field.SetInt(getInteger(values, fieldName, field.Interface().(int64)))
  417. case reflect.Float64:
  418. field.SetFloat(getFloat(values, fieldName, field.Interface().(float64)))
  419. case reflect.String:
  420. field.SetString(getString(values, fieldName, field.Interface().(string)))
  421. case reflect.Array, reflect.Slice:
  422. switch fieldType.Type.Elem().Kind() {
  423. case reflect.Int64:
  424. ints := getIntegers(values, fieldName)
  425. if ints != nil {
  426. field.Set(reflect.ValueOf(ints))
  427. }
  428. case reflect.Float64:
  429. floats := getFloats(values, fieldName)
  430. if floats != nil {
  431. field.Set(reflect.ValueOf(floats))
  432. }
  433. case reflect.String:
  434. strings := getStrings(values, fieldName)
  435. if strings != nil {
  436. field.Set(reflect.ValueOf(strings))
  437. }
  438. }
  439. }
  440. }
  441. return true
  442. }
  443. /*
  444. Keys returns all of the global keys in the config.
  445. */
  446. func (config *Config) Keys() []string {
  447. keys := make([]string, 0, len(config.values))
  448. for key := range config.values {
  449. keys = append(keys, key)
  450. }
  451. sort.Strings(keys)
  452. return keys
  453. }
  454. /*
  455. KeysForSection returns all of the keys found in the section named sectionName.
  456. If the section name matches the config.name or "" the global data is searched.
  457. */
  458. func (config *Config) KeysForSection(sectionName string) []string {
  459. section := config.sectionForName(sectionName)
  460. if section != nil {
  461. keys := make([]string, 0, len(section.values))
  462. for key := range section.values {
  463. keys = append(keys, key)
  464. }
  465. sort.Strings(keys)
  466. return keys
  467. }
  468. return nil
  469. }
  470. /*
  471. SectionNames returns the names for each of the sections in a config structure. If the config was assigned
  472. a name, that name is included in the list. If the name is not set, then only explicitely named sections are returned.
  473. */
  474. func (config *Config) SectionNames() []string {
  475. sectionNames := make([]string, 0, len(config.sections))
  476. for name := range config.sections {
  477. sectionNames = append(sectionNames, name)
  478. }
  479. if len(config.name) > 0 {
  480. sectionNames = append(sectionNames, config.name)
  481. }
  482. sort.Strings(sectionNames)
  483. return sectionNames
  484. }