iniconfig.go 13 KB

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