1// Package pgservicefile is a parser for PostgreSQL service files (e.g. .pg_service.conf).
2package pgservicefile
3
4import (
5	"bufio"
6	"errors"
7	"fmt"
8	"io"
9	"os"
10	"strings"
11)
12
13type Service struct {
14	Name     string
15	Settings map[string]string
16}
17
18type Servicefile struct {
19	Services       []*Service
20	servicesByName map[string]*Service
21}
22
23// GetService returns the named service.
24func (sf *Servicefile) GetService(name string) (*Service, error) {
25	service, present := sf.servicesByName[name]
26	if !present {
27		return nil, errors.New("not found")
28	}
29	return service, nil
30}
31
32// ReadServicefile reads the file at path and parses it into a Servicefile.
33func ReadServicefile(path string) (*Servicefile, error) {
34	f, err := os.Open(path)
35	if err != nil {
36		return nil, err
37	}
38	defer f.Close()
39
40	return ParseServicefile(f)
41}
42
43// ParseServicefile reads r and parses it into a Servicefile.
44func ParseServicefile(r io.Reader) (*Servicefile, error) {
45	servicefile := &Servicefile{}
46
47	var service *Service
48	scanner := bufio.NewScanner(r)
49	lineNum := 0
50	for scanner.Scan() {
51		lineNum += 1
52		line := scanner.Text()
53		line = strings.TrimSpace(line)
54
55		if line == "" || strings.HasPrefix(line, "#") {
56			// ignore comments and empty lines
57		} else if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
58			service = &Service{Name: line[1 : len(line)-1], Settings: make(map[string]string)}
59			servicefile.Services = append(servicefile.Services, service)
60		} else {
61			parts := strings.SplitN(line, "=", 2)
62			if len(parts) != 2 {
63				return nil, fmt.Errorf("unable to parse line %d", lineNum)
64			}
65
66			key := strings.TrimSpace(parts[0])
67			value := strings.TrimSpace(parts[1])
68
69			service.Settings[key] = value
70		}
71	}
72
73	servicefile.servicesByName = make(map[string]*Service, len(servicefile.Services))
74	for _, service := range servicefile.Services {
75		servicefile.servicesByName[service.Name] = service
76	}
77
78	return servicefile, scanner.Err()
79}
80