diff options
author | Runxi Yu <me@runxiyu.org> | 2024-12-29 18:40:22 +0000 |
---|---|---|
committer | Runxi Yu <me@runxiyu.org> | 2024-12-29 18:40:22 +0000 |
commit | b6c8304364d1457fa7454da257f7ad3d71131b91 (patch) | |
tree | de85e19971aa6ac127abf35b80421ff8f6907727 /scfg/writer.go | |
parent | Initial commit (diff) | |
download | go-lindenii-common-b6c8304364d1457fa7454da257f7ad3d71131b91.tar.gz go-lindenii-common-b6c8304364d1457fa7454da257f7ad3d71131b91.tar.zst go-lindenii-common-b6c8304364d1457fa7454da257f7ad3d71131b91.zip |
Add ~emersion/go-scfg as a subdirectory
Diffstat (limited to 'scfg/writer.go')
-rw-r--r-- | scfg/writer.go | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/scfg/writer.go b/scfg/writer.go new file mode 100644 index 0000000..97148a3 --- /dev/null +++ b/scfg/writer.go @@ -0,0 +1,107 @@ +package scfg + +import ( + "errors" + "io" + "strings" +) + +var ( + errDirEmptyName = errors.New("scfg: directive with empty name") +) + +// Write writes a parsed configuration to the provided io.Writer. +func Write(w io.Writer, blk Block) error { + enc := newEncoder(w) + err := enc.encodeBlock(blk) + return err +} + +// encoder write SCFG directives to an output stream. +type encoder struct { + w io.Writer + lvl int + err error +} + +// newEncoder returns a new encoder that writes to w. +func newEncoder(w io.Writer) *encoder { + return &encoder{w: w} +} + +func (enc *encoder) push() { + enc.lvl++ +} + +func (enc *encoder) pop() { + enc.lvl-- +} + +func (enc *encoder) writeIndent() { + for i := 0; i < enc.lvl; i++ { + enc.write([]byte("\t")) + } +} + +func (enc *encoder) write(p []byte) { + if enc.err != nil { + return + } + _, enc.err = enc.w.Write(p) +} + +func (enc *encoder) encodeBlock(blk Block) error { + for _, dir := range blk { + enc.encodeDir(*dir) + } + return enc.err +} + +func (enc *encoder) encodeDir(dir Directive) error { + if enc.err != nil { + return enc.err + } + + if dir.Name == "" { + enc.err = errDirEmptyName + return enc.err + } + + enc.writeIndent() + enc.write([]byte(maybeQuote(dir.Name))) + for _, p := range dir.Params { + enc.write([]byte(" ")) + enc.write([]byte(maybeQuote(p))) + } + + if len(dir.Children) > 0 { + enc.write([]byte(" {\n")) + enc.push() + enc.encodeBlock(dir.Children) + enc.pop() + + enc.writeIndent() + enc.write([]byte("}")) + } + enc.write([]byte("\n")) + + return enc.err +} + +const specialChars = "\"\\\r\n'{} \t" + +func maybeQuote(s string) string { + if s == "" || strings.ContainsAny(s, specialChars) { + var sb strings.Builder + sb.WriteByte('"') + for _, ch := range s { + if strings.ContainsRune(`"\`, ch) { + sb.WriteByte('\\') + } + sb.WriteRune(ch) + } + sb.WriteByte('"') + return sb.String() + } + return s +} |