package up
import (
"bufio"
"errors"
"fmt"
"io"
"strings"
)
// ParseUpfile to build a Config tree.
func ParseUpfile(r io.Reader) (*Config, error) {
t := &Config{
Commands: map[string][]string{},
Vars: map[string]string{},
}
var cmd, line string
scn := bufio.NewScanner(r)
for i := 1; scn.Scan(); i++ {
// We might be continuing a previous line from a trailing '\'
curLine := scn.Text()
trimmedLine := strings.TrimSpace(curLine)
if trimmedLine == "" {
continue
}
if trimmedLine[0] == '#' {
continue
}
if cmd != "" && curLine[0] != '\t' {
cmd = ""
}
// We're parsing a command
if cmd != "" {
line += trimmedLine
if line[len(line)-1] == '\\' {
line = line[:len(line)-1]
continue
}
t.Commands[cmd] = append(t.Commands[cmd], line)
line = ""
continue
}
// We're not parsing a command. Either we're parsing a variable
// or a command name. Attempt to parse a variable first.
varParts := strings.SplitN(curLine, "=", 2)
if len(varParts) == 2 {
t.Vars[varParts[0]] = varParts[1]
continue
}
// We're not parsing a variable, so we're parsing a command
// name.
if strings.Index(curLine, ":") == -1 {
return nil, fmt.Errorf("line %d: missing colon in command", i)
}
parts := strings.SplitN(curLine, ":", 2)
cmd = parts[0]
if parts[1] != "" {
return nil, fmt.Errorf("line %d: unexpected content after colon", i)
}
if t.DefaultCommand == "" {
t.DefaultCommand = cmd
}
}
if err := scn.Err(); err != nil {
return nil, fmt.Errorf("scan: %w", err)
}
if len(t.Commands) == 0 {
return nil, errors.New("no commands")
}
return t, nil
}