...

Source file src/cuelang.org/go/cue/ast/astutil/util.go

Documentation: cuelang.org/go/cue/ast/astutil

     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, from.Comments())
    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, from.Comments())
    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  }
   159  

View as plain text