// Copyright 2019 CUE Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package astutil import ( "path" "strconv" "strings" "cuelang.org/go/cue/ast" "cuelang.org/go/cue/token" ) // ImportPathName derives the package name from the given import path. // // Examples: // // string string // foo.com/bar bar // foo.com/bar:baz baz func ImportPathName(id string) string { name := path.Base(id) if p := strings.LastIndexByte(name, ':'); p > 0 { name = name[p+1:] } return name } // ImportInfo describes the information contained in an ImportSpec. type ImportInfo struct { Ident string // identifier used to refer to the import PkgName string // name of the package ID string // full import path, including the name Dir string // import path, excluding the name } // ParseImportSpec returns the name and full path of an ImportSpec. func ParseImportSpec(spec *ast.ImportSpec) (info ImportInfo, err error) { str, err := strconv.Unquote(spec.Path.Value) if err != nil { return info, err } info.ID = str if p := strings.LastIndexByte(str, ':'); p > 0 { info.Dir = str[:p] info.PkgName = str[p+1:] } else { info.Dir = str info.PkgName = path.Base(str) } if spec.Name != nil { info.Ident = spec.Name.Name } else { info.Ident = info.PkgName } return info, nil } // CopyComments associates comments of one node with another. // It may change the relative position of comments. func CopyComments(to, from ast.Node) { if from == nil { return } ast.SetComments(to, from.Comments()) } // CopyPosition sets the position of one node to another. func CopyPosition(to, from ast.Node) { if from == nil { return } ast.SetPos(to, from.Pos()) } // CopyMeta copies comments and position information from one node to another. // It returns the destination node. func CopyMeta(to, from ast.Node) ast.Node { if from == nil { return to } ast.SetComments(to, from.Comments()) ast.SetPos(to, from.Pos()) return to } // insertImport looks up an existing import with the given name and path or will // add spec if it doesn't exist. It returns a spec in decls matching spec. func insertImport(decls *[]ast.Decl, spec *ast.ImportSpec) *ast.ImportSpec { x, _ := ParseImportSpec(spec) a := *decls var imports *ast.ImportDecl var orig *ast.ImportSpec p := 0 outer: for i := 0; i < len(a); i++ { d := a[i] switch t := d.(type) { default: break outer case *ast.Package: p = i + 1 case *ast.CommentGroup: p = i + 1 case *ast.Attribute: continue case *ast.ImportDecl: p = i + 1 imports = t for _, s := range t.Specs { y, _ := ParseImportSpec(s) if y.ID != x.ID { continue } orig = s if x.Ident == "" || y.Ident == x.Ident { return s } } } } // Import not found, add one. if imports == nil { imports = &ast.ImportDecl{} preamble := append(a[:p:p], imports) a = append(preamble, a[p:]...) *decls = a } if orig != nil { CopyComments(spec, orig) } imports.Specs = append(imports.Specs, spec) ast.SetRelPos(imports.Specs[0], token.NoRelPos) return spec }