package common import ( "bytes" "errors" "fmt" "io" "strings" "unicode" ) type Submodule struct { Name string Path string Url string Update string Branch string Ignore string Shallow string } var SyntaxError error = errors.New("Syntax error") func (sub *Submodule) parseSubmoduleName(line string) error { line = strings.TrimSpace(line) if line[0:10] != "submodule " { return SyntaxError } r := strings.NewReader(line[10:]) ch, _, err := r.ReadRune() for ; ; ch, _, err = r.ReadRune() { if err != nil { return fmt.Errorf("%c %d", ch, 32) } if !unicode.IsSpace(ch) { break } } if ch != '"' { return fmt.Errorf("%c %d", ch, 36) // return SyntaxError } var b strings.Builder for ch, _, err = r.ReadRune(); ch != '"'; ch, _, err = r.ReadRune() { if err != nil { return fmt.Errorf("%c %d", ch, 32) } b.WriteRune(ch) } if ch != '"' { return fmt.Errorf("%c %d", ch, 45) // return SyntaxError } for ch, _, err = r.ReadRune(); unicode.IsSpace(ch); ch, _, err = r.ReadRune() { if err != nil { return fmt.Errorf("%d %w", 38, err) } } if ch != ']' || err != nil { return fmt.Errorf("%c %d, %w", ch, 50, err) // return SyntaxError } sub.Name = b.String() for ch, _, err = r.ReadRune(); ; ch, _, _ = r.ReadRune() { if err == io.EOF { return nil } if !unicode.IsSpace(ch) { return SyntaxError } } } func (s *Submodule) parseKeyValue(line string) error { eqLoc := strings.Index(line, "=") if eqLoc < 0 || eqLoc == len(line)-1 { return SyntaxError } key := strings.ToLower(strings.TrimSpace(line[0:eqLoc])) val := strings.TrimSpace(line[eqLoc+1:]) if len(val) == 0 { return SyntaxError } switch key { case "path": s.Path = val case "url": s.Url = val case "shallow": s.Shallow = val case "ignore": s.Ignore = val case "branch": s.Branch = val case "update": s.Update = val default: return SyntaxError } return nil } func ParseSubmodulesFile(reader io.Reader) ([]Submodule, error) { data, err := io.ReadAll(reader) if err != nil { return nil, err } var sub *Submodule ret := []Submodule{} lines := bytes.Split(data, []byte("\n")) for _, l := range lines { line := string(bytes.TrimSpace(l)) if len(line) == 0 { continue } if line[0] == '[' { if sub != nil { ret = append(ret, *sub) } sub = &Submodule{} if err := sub.parseSubmoduleName(line[1:]); err != nil { return nil, err } } else if sub == nil { return nil, SyntaxError } else { if err := sub.parseKeyValue(line); err != nil { return nil, err } } } if sub != nil { ret = append(ret, *sub) } return ret, nil } func writeValue(out io.Writer, key, value string) { if len(value) > 0 { out.Write([]byte(fmt.Sprintf("\t%s = %s\n", key, value))) } } func WriteSubmodules(subs []Submodule, out io.Writer) error { for _, sub := range subs { if len(sub.Name) < 1 { return fmt.Errorf("Submodule with no name.") } out.Write([]byte(fmt.Sprintf("[submodule \"%s\"]\n", sub.Name))) writeValue(out, "path", sub.Path) writeValue(out, "url", sub.Url) writeValue(out, "branch", sub.Branch) writeValue(out, "ignore", sub.Ignore) writeValue(out, "shallow", sub.Shallow) writeValue(out, "update", sub.Update) } return nil }