github.com/octohelm/cuemod@v0.9.4/pkg/cuemod/modfile/load.go (about)

     1  package modfile
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"cuelang.org/go/cue/ast"
    10  	"cuelang.org/go/cue/parser"
    11  	"cuelang.org/go/cue/token"
    12  )
    13  
    14  func LoadModFile(dir string, m *ModFile) (bool, error) {
    15  	f := filepath.Join(dir, ModFilename)
    16  
    17  	if m.comments == nil {
    18  		m.comments = map[string][]*ast.CommentGroup{}
    19  	}
    20  
    21  	if m.Replace == nil {
    22  		m.Replace = map[VersionedPathIdentity]ReplaceTarget{}
    23  	}
    24  
    25  	if m.Require == nil {
    26  		m.Require = map[string]Requirement{}
    27  	}
    28  
    29  	data, err := os.ReadFile(f)
    30  	if err != nil && !os.IsNotExist(err) {
    31  		return false, err
    32  	}
    33  
    34  	if len(data) > 0 {
    35  		f, err := parser.ParseFile(ModFilename, data, parser.ParseComments)
    36  		if err != nil {
    37  			return false, err
    38  		}
    39  
    40  		for i := range f.Decls {
    41  			decl := f.Decls[i]
    42  			if field, ok := decl.(*ast.Field); ok {
    43  				directive := stringValue(field.Label)
    44  
    45  				if directive != "" {
    46  					switch directive {
    47  					case "module":
    48  						if module := stringValue(field.Value); module != "" {
    49  							m.Module = module
    50  						}
    51  					case "replace":
    52  						if s, ok := field.Value.(*ast.StructLit); ok {
    53  							for i := range s.Elts {
    54  								if subField, ok := s.Elts[i].(*ast.Field); ok {
    55  									from := stringValue(subField.Label)
    56  									to := stringValue(subField.Value)
    57  
    58  									if from != "" {
    59  										cg := ast.Comments(subField)
    60  
    61  										// from: xxx: xxx
    62  										if s.Lbrace == token.NoPos {
    63  											cg = ast.Comments(field)
    64  										}
    65  
    66  										m.comments[directive+"://"+from] = cg
    67  
    68  										r, err := ParsePathMayWithVersion(from)
    69  										if err != nil {
    70  											return false, err
    71  										}
    72  
    73  										replaceTarget := ReplaceTarget{}
    74  
    75  										if to != "" {
    76  											if err := replaceTarget.UnmarshalText([]byte(to)); err != nil {
    77  												return false, err
    78  											}
    79  										}
    80  
    81  										for i := range subField.Attrs {
    82  											k, v := subField.Attrs[i].Split()
    83  
    84  											switch k {
    85  											case "import":
    86  												value, _ := strconv.Unquote(v)
    87  												replaceTarget.Import = value
    88  											}
    89  										}
    90  
    91  										if replaceTarget.Path == "" {
    92  											replaceTarget.Path = r.Path
    93  										}
    94  
    95  										m.Replace[*r] = replaceTarget
    96  									}
    97  								}
    98  							}
    99  						}
   100  					case "require":
   101  						if s, ok := field.Value.(*ast.StructLit); ok {
   102  							for i := range s.Elts {
   103  								if subField, ok := s.Elts[i].(*ast.Field); ok {
   104  									module := stringValue(subField.Label)
   105  									version := stringValue(subField.Value)
   106  
   107  									if module != "" && version != "" {
   108  										cg := ast.Comments(subField)
   109  
   110  										// require: xxx: xxx
   111  										if s.Lbrace == token.NoPos {
   112  											cg = ast.Comments(subField)
   113  										}
   114  
   115  										m.comments[directive+"://"+module] = cg
   116  
   117  										r := Requirement{}
   118  
   119  										vv := strings.Split(version, "#")
   120  										if len(vv) > 1 {
   121  											r.VcsRef = ""
   122  										}
   123  										r.Version = vv[0]
   124  
   125  										for _, attr := range subField.Attrs {
   126  											k, v := attr.Split()
   127  
   128  											switch k {
   129  											case "vcs":
   130  												// TODO remove in future
   131  												value, _ := strconv.Unquote(v)
   132  												r.VcsRef = value
   133  											case "indirect":
   134  												r.Indirect = true
   135  											}
   136  										}
   137  
   138  										m.Require[module] = r
   139  									}
   140  								}
   141  							}
   142  						}
   143  					}
   144  				}
   145  			}
   146  		}
   147  
   148  		return true, nil
   149  	}
   150  
   151  	return false, nil
   152  }
   153  
   154  func stringValue(node ast.Node) string {
   155  	switch v := node.(type) {
   156  	case *ast.Ident:
   157  		return v.Name
   158  	case *ast.BasicLit:
   159  		switch v.Kind {
   160  		case token.STRING:
   161  			s, _ := strconv.Unquote(v.Value)
   162  			return s
   163  		case token.INT, token.FLOAT, token.FALSE, token.TRUE:
   164  			return v.Value
   165  		}
   166  	}
   167  
   168  	return ""
   169  }