parsecfg: handle streams and strings when saving or loading configuration (#5554)

This commit is contained in:
Anatoly Galiulin 2017-04-06 13:10:08 +07:00 • committed by Andreas Rumpf
commit 3374bcb373
2 changed files with 124 additions and 79 deletions

View file

@ -430,40 +430,46 @@ proc newConfig*(): Config =
## Useful when wanting to create a configuration file. ## Useful when wanting to create a configuration file.
result = newOrderedTable[string, OrderedTableRef[string, string]]() result = newOrderedTable[string, OrderedTableRef[string, string]]()
proc loadConfig*(filename: string): Config = proc loadConfig*(stream: Stream, filename: string = "[stream]"): Config =
## Load the specified configuration file into a new Config instance. ## Load the specified configuration from stream into a new Config instance.
## `filename` parameter is only used for nicer error messages.
var dict = newOrderedTable[string, OrderedTableRef[string, string]]() var dict = newOrderedTable[string, OrderedTableRef[string, string]]()
var curSection = "" ## Current section, var curSection = "" ## Current section,
## the default value of the current section is "", ## the default value of the current section is "",
## which means that the current section is a common ## which means that the current section is a common
var p: CfgParser var p: CfgParser
var fileStream = newFileStream(filename, fmRead) open(p, stream, filename)
if fileStream != nil: while true:
open(p, fileStream, filename) var e = next(p)
while true: case e.kind
var e = next(p) of cfgEof:
case e.kind break
of cfgEof: of cfgSectionStart: # Only look for the first time the Section
break curSection = e.section
of cfgSectionStart: # Only look for the first time the Section of cfgKeyValuePair:
curSection = e.section var t = newOrderedTable[string, string]()
of cfgKeyValuePair: if dict.hasKey(curSection):
var t = newOrderedTable[string, string]() t = dict[curSection]
if dict.hasKey(curSection): t[e.key] = e.value
t = dict[curSection] dict[curSection] = t
t[e.key] = e.value of cfgOption:
dict[curSection] = t var c = newOrderedTable[string, string]()
of cfgOption: if dict.hasKey(curSection):
var c = newOrderedTable[string, string]() c = dict[curSection]
if dict.hasKey(curSection): c["--" & e.key] = e.value
c = dict[curSection] dict[curSection] = c
c["--" & e.key] = e.value of cfgError:
dict[curSection] = c break
of cfgError: close(p)
break
close(p)
result = dict result = dict
proc loadConfig*(filename: string): Config =
## Load the specified configuration file into a new Config instance.
let file = open(filename, fmRead)
let fileStream = newFileStream(file)
defer: fileStream.close()
result = fileStream.loadConfig(filename)
proc replace(s: string): string = proc replace(s: string): string =
var d = "" var d = ""
var i = 0 var i = 0
@ -482,54 +488,61 @@ proc replace(s: string): string =
inc(i) inc(i)
result = d result = d
proc writeConfig*(dict: Config, stream: Stream) =
## Writes the contents of the table to the specified stream
##
## **Note:** Comment statement will be ignored.
for section, sectionData in dict.pairs():
if section != "": ## Not general section
if not allCharsInSet(section, SymChars): ## Non system character
stream.writeLine("[\"" & section & "\"]")
else:
stream.writeLine("[" & section & "]")
for key, value in sectionData.pairs():
var kv, segmentChar: string
if key.len > 1 and key[0] == '-' and key[1] == '-': ## If it is a command key
segmentChar = ":"
if not allCharsInSet(key[2..key.len()-1], SymChars):
kv.add("--\"")
kv.add(key[2..key.len()-1])
kv.add("\"")
else:
kv = key
else:
segmentChar = "="
kv = key
if value != "": ## If the key is not empty
if not allCharsInSet(value, SymChars):
if find(value, '"') == -1:
kv.add(segmentChar)
kv.add("\"")
kv.add(replace(value))
kv.add("\"")
else:
kv.add(segmentChar)
kv.add("\"\"\"")
kv.add(replace(value))
kv.add("\"\"\"")
else:
kv.add(segmentChar)
kv.add(value)
stream.writeLine(kv)
proc `$`*(dict: Config): string =
## Writes the contents of the table to string.
## Note: Comment statement will be ignored.
let stream = newStringStream()
defer: stream.close()
dict.writeConfig(stream)
result = stream.data
proc writeConfig*(dict: Config, filename: string) = proc writeConfig*(dict: Config, filename: string) =
## Writes the contents of the table to the specified configuration file. ## Writes the contents of the table to the specified configuration file.
## Note: Comment statement will be ignored. ## Note: Comment statement will be ignored.
var file: File let file = open(filename, fmWrite)
if file.open(filename, fmWrite): let fileStream = newFileStream(filename)
try: defer: fileStream.close()
var section, key, value, kv, segmentChar:string dict.writeConfig(fileStream)
for pair in dict.pairs():
section = pair[0]
if section != "": ## Not general section
if not allCharsInSet(section, SymChars): ## Non system character
file.writeLine("[\"" & section & "\"]")
else:
file.writeLine("[" & section & "]")
for pair2 in pair[1].pairs():
key = pair2[0]
value = pair2[1]
if key[0] == '-' and key[1] == '-': ## If it is a command key
segmentChar = ":"
if not allCharsInSet(key[2..key.len()-1], SymChars):
kv.add("--\"")
kv.add(key[2..key.len()-1])
kv.add("\"")
else:
kv = key
else:
segmentChar = "="
kv = key
if value != "": ## If the key is not empty
if not allCharsInSet(value, SymChars):
if find(value, '"') == -1:
kv.add(segmentChar)
kv.add("\"")
kv.add(replace(value))
kv.add("\"")
else:
kv.add(segmentChar)
kv.add("\"\"\"")
kv.add(replace(value))
kv.add("\"\"\"")
else:
kv.add(segmentChar)
kv.add(value)
file.writeLine(kv)
except:
raise
finally:
file.close()
proc getSectionValue*(dict: Config, section, key: string): string = proc getSectionValue*(dict: Config, section, key: string): string =
## Gets the Key value of the specified Section. ## Gets the Key value of the specified Section.

View file

@ -1,4 +1,31 @@
import parsecfg discard """
output: '''
utf-8
on
hello
lihf8515
10214028
lihaifeng@wxm.com
===
charset=utf-8
[Package]
name=hello
--threads:on
[Author]
name=lhf
qq=10214028
email="lihaifeng@wxm.com"
===
charset=utf-8
[Package]
name=hello
--threads:on
[Author]
name=lihf8515
qq=10214028
'''
"""
import parsecfg, streams
## Creating a configuration file. ## Creating a configuration file.
var dict1=newConfig() var dict1=newConfig()
@ -8,10 +35,11 @@ dict1.setSectionKey("Package","--threads","on")
dict1.setSectionKey("Author","name","lihf8515") dict1.setSectionKey("Author","name","lihf8515")
dict1.setSectionKey("Author","qq","10214028") dict1.setSectionKey("Author","qq","10214028")
dict1.setSectionKey("Author","email","lihaifeng@wxm.com") dict1.setSectionKey("Author","email","lihaifeng@wxm.com")
dict1.writeConfig("config.ini") var ss = newStringStream()
dict1.writeConfig(ss)
## Reading a configuration file. ## Reading a configuration file.
var dict2 = loadConfig("config.ini") var dict2 = loadConfig(newStringStream(ss.data))
var charset = dict2.getSectionValue("","charset") var charset = dict2.getSectionValue("","charset")
var threads = dict2.getSectionValue("Package","--threads") var threads = dict2.getSectionValue("Package","--threads")
var pname = dict2.getSectionValue("Package","name") var pname = dict2.getSectionValue("Package","name")
@ -25,13 +53,17 @@ echo name
echo qq echo qq
echo email echo email
echo "==="
## Modifying a configuration file. ## Modifying a configuration file.
var dict3 = loadConfig("config.ini") var dict3 = loadConfig(newStringStream(ss.data))
dict3.setSectionKey("Author","name","lhf") dict3.setSectionKey("Author","name","lhf")
dict3.writeConfig("config.ini") write(stdout, $dict3)
echo "==="
## Deleting a section key in a configuration file. ## Deleting a section key in a configuration file.
var dict4 = loadConfig("config.ini") var dict4 = loadConfig(newStringStream(ss.data))
dict4.delSectionKey("Author","email") dict4.delSectionKey("Author","email")
dict4.writeConfig("config.ini") write(stdout, $dict4)