source: code/trunk/config/config.go@ 153

Last change on this file since 153 was 98, checked in by contact, 5 years ago

Rename project to soju

File size: 3.2 KB
Line 
1package config
2
3import (
4 "bufio"
5 "fmt"
6 "io"
7 "os"
8 "strings"
9 "unicode"
10)
11
12type TLS struct {
13 CertPath, KeyPath string
14}
15
16type Server struct {
17 Addr string
18 Hostname string
19 TLS *TLS
20 SQLDriver string
21 SQLSource string
22}
23
24func Defaults() *Server {
25 hostname, err := os.Hostname()
26 if err != nil {
27 hostname = "localhost"
28 }
29 return &Server{
30 Addr: ":6667",
31 Hostname: hostname,
32 SQLDriver: "sqlite3",
33 SQLSource: "soju.db",
34 }
35}
36
37func Load(path string) (*Server, error) {
38 f, err := os.Open(path)
39 if err != nil {
40 return nil, err
41 }
42 defer f.Close()
43
44 return Parse(f)
45}
46
47func Parse(r io.Reader) (*Server, error) {
48 p := parser{br: bufio.NewReader(r)}
49 directives, err := p.file()
50 if err != nil {
51 return nil, fmt.Errorf("failed to parse config file: %v", err)
52 }
53
54 srv := Defaults()
55 for _, d := range directives {
56 switch d.Name {
57 case "listen":
58 if err := d.parseParams(&srv.Addr); err != nil {
59 return nil, err
60 }
61 case "hostname":
62 if err := d.parseParams(&srv.Hostname); err != nil {
63 return nil, err
64 }
65 case "tls":
66 tls := &TLS{}
67 if err := d.parseParams(&tls.CertPath, &tls.KeyPath); err != nil {
68 return nil, err
69 }
70 srv.TLS = tls
71 case "sql":
72 if err := d.parseParams(&srv.SQLDriver, &srv.SQLSource); err != nil {
73 return nil, err
74 }
75 default:
76 return nil, fmt.Errorf("unknown directive %q", d.Name)
77 }
78 }
79
80 return srv, nil
81}
82
83type directive struct {
84 Name string
85 Params []string
86}
87
88func (d *directive) parseParams(out ...*string) error {
89 if len(d.Params) != len(out) {
90 return fmt.Errorf("directive %q has wrong number of parameters: expected %v, got %v", d.Name, len(out), len(d.Params))
91 }
92 for i := range out {
93 *out[i] = d.Params[i]
94 }
95 return nil
96}
97
98type parser struct {
99 br *bufio.Reader
100}
101
102func (p *parser) skipSpace() error {
103 for {
104 r, _, err := p.br.ReadRune()
105 if err == io.EOF {
106 break
107 } else if err != nil {
108 return err
109 }
110 if !unicode.IsSpace(r) || r == '\n' {
111 p.br.UnreadRune()
112 break
113 }
114 }
115 return nil
116}
117
118func (p *parser) atom() (string, error) {
119 var sb strings.Builder
120 for {
121 r, _, err := p.br.ReadRune()
122 if err == io.EOF && sb.Len() > 0 {
123 break
124 } else if err != nil {
125 return "", err
126 }
127 if unicode.IsSpace(r) {
128 p.br.UnreadRune()
129 if err := p.skipSpace(); err != nil {
130 return "", err
131 }
132 break
133 }
134 sb.WriteRune(r)
135 }
136 return sb.String(), nil
137}
138
139func (p *parser) directive() (*directive, error) {
140 name, err := p.atom()
141 if err == io.EOF {
142 return nil, io.EOF
143 } else if err != nil {
144 return nil, fmt.Errorf("failed to read directive name: %v", err)
145 }
146
147 var params []string
148 for {
149 r, _, err := p.br.ReadRune()
150 if err == io.EOF {
151 break
152 } else if err != nil {
153 return nil, err
154 }
155 if r == '\n' {
156 break
157 }
158 p.br.UnreadRune()
159
160 param, err := p.atom()
161 if err == io.EOF {
162 break
163 } else if err != nil {
164 return nil, fmt.Errorf("failed to read directive param: %v", err)
165 }
166 params = append(params, param)
167 }
168
169 return &directive{name, params}, nil
170}
171
172func (p *parser) file() ([]directive, error) {
173 var l []directive
174 for {
175 d, err := p.directive()
176 if err == io.EOF {
177 break
178 } else if err != nil {
179 return nil, err
180 }
181 l = append(l, *d)
182 }
183 return l, nil
184}
Note: See TracBrowser for help on using the repository browser.