cuelang.org/go@v0.13.0/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  //
    34  // Deprecated: use [ast.ParseImportPath] instead to obtain the
    35  // qualifier.
    36  func ImportPathName(id string) string {
    37  	// TODO use ast.ParseImportPath(id).Qualifier and change
    38  	// callers to understand that they might receive an empty string.
    39  	name := path.Base(id)
    40  	if p := strings.LastIndexByte(name, ':'); p > 0 {
    41  		name = name[p+1:]
    42  	}
    43  	return name
    44  }
    45  
    46  // ImportInfo describes the information contained in an ImportSpec.
    47  type ImportInfo struct {
    48  	Ident   string // identifier used to refer to the import
    49  	PkgName string // name of the package
    50  	ID      string // full import path, including the name
    51  	Dir     string // import path, excluding the name
    52  }
    53  
    54  // ParseImportSpec returns the name and full path of an ImportSpec.
    55  func ParseImportSpec(spec *ast.ImportSpec) (ImportInfo, error) {
    56  	str, err := strconv.Unquote(spec.Path.Value)
    57  	if err != nil {
    58  		return ImportInfo{}, err
    59  	}
    60  	ip := ast.ParseImportPath(str)
    61  	info := ImportInfo{
    62  		ID:      str,
    63  		Ident:   ip.Qualifier,
    64  		PkgName: ip.Qualifier,
    65  		Dir:     ip.Unqualified().String(),
    66  	}
    67  	if spec.Name != nil {
    68  		info.Ident = spec.Name.Name
    69  	}
    70  	return info, nil
    71  }
    72  
    73  // CopyComments associates comments of one node with another.
    74  // It may change the relative position of comments.
    75  func CopyComments(to, from ast.Node) {
    76  	if from == nil {
    77  		return
    78  	}
    79  	ast.SetComments(to, ast.Comments(from))
    80  }
    81  
    82  // CopyPosition sets the position of one node to another.
    83  func CopyPosition(to, from ast.Node) {
    84  	if from == nil {
    85  		return
    86  	}
    87  	ast.SetPos(to, from.Pos())
    88  }
    89  
    90  // CopyMeta copies comments and position information from one node to another.
    91  // It returns the destination node.
    92  func CopyMeta(to, from ast.Node) ast.Node {
    93  	if from == nil {
    94  		return to
    95  	}
    96  	ast.SetComments(to, ast.Comments(from))
    97  	ast.SetPos(to, from.Pos())
    98  	return to
    99  }
   100  
   101  // insertImport looks up an existing import with the given name and path or will
   102  // add spec if it doesn't exist. It returns a spec in decls matching spec.
   103  func insertImport(decls *[]ast.Decl, spec *ast.ImportSpec) *ast.ImportSpec {
   104  	x, _ := ParseImportSpec(spec)
   105  
   106  	a := *decls
   107  
   108  	var imports *ast.ImportDecl
   109  	var orig *ast.ImportSpec
   110  
   111  	p := 0
   112  outer:
   113  	for i := 0; i < len(a); i++ {
   114  		d := a[i]
   115  		switch t := d.(type) {
   116  		default:
   117  			break outer
   118  
   119  		case *ast.Package:
   120  			p = i + 1
   121  		case *ast.CommentGroup:
   122  			p = i + 1
   123  		case *ast.Attribute:
   124  			continue
   125  		case *ast.ImportDecl:
   126  			p = i + 1
   127  			imports = t
   128  			for _, s := range t.Specs {
   129  				y, _ := ParseImportSpec(s)
   130  				if y.ID != x.ID {
   131  					continue
   132  				}
   133  				orig = s
   134  				if x.Ident == "" || y.Ident == x.Ident {
   135  					return s
   136  				}
   137  			}
   138  		}
   139  	}
   140  
   141  	// Import not found, add one.
   142  	if imports == nil {
   143  		imports = &ast.ImportDecl{}
   144  		preamble := append(a[:p:p], imports)
   145  		a = append(preamble, a[p:]...)
   146  		*decls = a
   147  	}
   148  
   149  	if orig != nil {
   150  		CopyComments(spec, orig)
   151  	}
   152  	imports.Specs = append(imports.Specs, spec)
   153  	ast.SetRelPos(imports.Specs[0], token.NoRelPos)
   154  
   155  	return spec
   156  }