cuelang.org/go@v0.10.1/cue/ast/astutil/util.go (about) 1 // Copyright 2019 CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package astutil 16 17 import ( 18 "path" 19 "strconv" 20 "strings" 21 22 "cuelang.org/go/cue/ast" 23 "cuelang.org/go/cue/token" 24 ) 25 26 // ImportPathName derives the package name from the given import path. 27 // 28 // Examples: 29 // 30 // string string 31 // foo.com/bar bar 32 // foo.com/bar:baz baz 33 func ImportPathName(id string) string { 34 name := path.Base(id) 35 if p := strings.LastIndexByte(name, ':'); p > 0 { 36 name = name[p+1:] 37 } 38 return name 39 } 40 41 // ImportInfo describes the information contained in an ImportSpec. 42 type ImportInfo struct { 43 Ident string // identifier used to refer to the import 44 PkgName string // name of the package 45 ID string // full import path, including the name 46 Dir string // import path, excluding the name 47 } 48 49 // ParseImportSpec returns the name and full path of an ImportSpec. 50 func ParseImportSpec(spec *ast.ImportSpec) (info ImportInfo, err error) { 51 str, err := strconv.Unquote(spec.Path.Value) 52 if err != nil { 53 return info, err 54 } 55 56 info.ID = str 57 58 if p := strings.LastIndexByte(str, ':'); p > 0 { 59 info.Dir = str[:p] 60 info.PkgName = str[p+1:] 61 } else { 62 info.Dir = str 63 info.PkgName = path.Base(str) 64 } 65 66 if spec.Name != nil { 67 info.Ident = spec.Name.Name 68 } else { 69 info.Ident = info.PkgName 70 } 71 72 return info, nil 73 } 74 75 // CopyComments associates comments of one node with another. 76 // It may change the relative position of comments. 77 func CopyComments(to, from ast.Node) { 78 if from == nil { 79 return 80 } 81 ast.SetComments(to, ast.Comments(from)) 82 } 83 84 // CopyPosition sets the position of one node to another. 85 func CopyPosition(to, from ast.Node) { 86 if from == nil { 87 return 88 } 89 ast.SetPos(to, from.Pos()) 90 } 91 92 // CopyMeta copies comments and position information from one node to another. 93 // It returns the destination node. 94 func CopyMeta(to, from ast.Node) ast.Node { 95 if from == nil { 96 return to 97 } 98 ast.SetComments(to, ast.Comments(from)) 99 ast.SetPos(to, from.Pos()) 100 return to 101 } 102 103 // insertImport looks up an existing import with the given name and path or will 104 // add spec if it doesn't exist. It returns a spec in decls matching spec. 105 func insertImport(decls *[]ast.Decl, spec *ast.ImportSpec) *ast.ImportSpec { 106 x, _ := ParseImportSpec(spec) 107 108 a := *decls 109 110 var imports *ast.ImportDecl 111 var orig *ast.ImportSpec 112 113 p := 0 114 outer: 115 for i := 0; i < len(a); i++ { 116 d := a[i] 117 switch t := d.(type) { 118 default: 119 break outer 120 121 case *ast.Package: 122 p = i + 1 123 case *ast.CommentGroup: 124 p = i + 1 125 case *ast.Attribute: 126 continue 127 case *ast.ImportDecl: 128 p = i + 1 129 imports = t 130 for _, s := range t.Specs { 131 y, _ := ParseImportSpec(s) 132 if y.ID != x.ID { 133 continue 134 } 135 orig = s 136 if x.Ident == "" || y.Ident == x.Ident { 137 return s 138 } 139 } 140 } 141 } 142 143 // Import not found, add one. 144 if imports == nil { 145 imports = &ast.ImportDecl{} 146 preamble := append(a[:p:p], imports) 147 a = append(preamble, a[p:]...) 148 *decls = a 149 } 150 151 if orig != nil { 152 CopyComments(spec, orig) 153 } 154 imports.Specs = append(imports.Specs, spec) 155 ast.SetRelPos(imports.Specs[0], token.NoRelPos) 156 157 return spec 158 }