// Copyright 2018 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package confyg import ( "bytes" "errors" "fmt" "sort" "strconv" "strings" ) type File struct { Syntax *FileSyntax } func Parse(file string, data []byte) (*File, error) { fs, err := parse(file, data) if err != nil { return nil, err } f := &File{ Syntax: fs, } var errs bytes.Buffer for _, x := range fs.Stmt { switch x := x.(type) { case *Line: f.add(&errs, x, x.Token[0], x.Token[1:]) case *LineBlock: if len(x.Token) > 1 { fmt.Fprintf(&errs, "%s:%d: unknown block type: %s\n", file, x.Start.Line, strings.Join(x.Token, " ")) continue } switch x.Token[0] { default: fmt.Fprintf(&errs, "%s:%d: unknown block type: %s\n", file, x.Start.Line, strings.Join(x.Token, " ")) continue case "module", "require", "exclude", "replace": for _, l := range x.Line { f.add(&errs, l, x.Token[0], l.Token) } } } } if errs.Len() > 0 { return nil, errors.New(strings.TrimRight(errs.String(), "\n")) } return f, nil } func (f *File) add(errs *bytes.Buffer, line *Line, verb string, args []string) { // TODO: We should pass in a flag saying whether this module is a dependency. // If so, we should ignore all unknown directives and not attempt to parse // replace and exclude either. They don't matter, and it will work better for // forward compatibility if we can depend on modules that have local changes. // TODO: For the target module (not dependencies), maybe we should // relax the semver requirement and rewrite the file with updated info // after resolving any versions. That would let people type commit hashes // or tags or branch names, and then vgo would fix them. switch verb { default: fmt.Fprintf(errs, "%s:%d: unknown directive: %s\n", f.Syntax.Name, line.Start.Line, verb) } } func isDirectoryPath(ns string) bool { // Because go.mod files can move from one system to another, // we check all known path syntaxes, both Unix and Windows. return strings.HasPrefix(ns, "./") || strings.HasPrefix(ns, "../") || strings.HasPrefix(ns, "/") || strings.HasPrefix(ns, `.\`) || strings.HasPrefix(ns, `..\`) || strings.HasPrefix(ns, `\`) || len(ns) >= 2 && ('A' <= ns[0] && ns[0] <= 'Z' || 'a' <= ns[0] && ns[0] <= 'z') && ns[1] == ':' } func isString(s string) bool { return s != "" && s[0] == '"' } func parseString(s *string) (string, error) { t, err := strconv.Unquote(*s) if err != nil { return "", err } *s = strconv.Quote(t) return t, nil } func (f *File) Format() ([]byte, error) { return Format(f.Syntax), nil } func (f *File) SortBlocks() { for _, stmt := range f.Syntax.Stmt { block, ok := stmt.(*LineBlock) if !ok { continue } sort.Slice(block.Line, func(i, j int) bool { li := block.Line[i] lj := block.Line[j] for k := 0; k < len(li.Token) && k < len(lj.Token); k++ { if li.Token[k] != lj.Token[k] { return li.Token[k] < lj.Token[k] } } return len(li.Token) < len(lj.Token) }) } }