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

     1  package cuemod
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"path/filepath"
     7  	"strings"
     8  
     9  	"golang.org/x/mod/module"
    10  
    11  	"github.com/octohelm/cuemod/pkg/cueify"
    12  	"github.com/octohelm/cuemod/pkg/cuemod/modfile"
    13  	"github.com/pkg/errors"
    14  )
    15  
    16  func PathFor(mod *Mod, importPath string) *Path {
    17  	i := &Path{}
    18  	i.Mod = *mod
    19  
    20  	if importPath != "" {
    21  		// when use replace with custom forked repo
    22  		// mod.Module may not same as mod.Repo
    23  		// then should to overwrite this path.Module with mode.Repo
    24  		if !strings.HasPrefix(importPath, i.Module) {
    25  			if strings.HasPrefix(importPath, i.Repo) {
    26  				i.Module = i.Repo
    27  			}
    28  		}
    29  
    30  		i.Module = i.ModuleRoot()
    31  		i.SubPath, _ = subDir(i.Module, importPath)
    32  	}
    33  
    34  	return i
    35  }
    36  
    37  type Path struct {
    38  	Mod
    39  	SubPath string
    40  	Replace *ReplaceRule
    41  }
    42  
    43  func (i Path) String() string {
    44  	s := strings.Builder{}
    45  
    46  	if i.Replace != nil {
    47  		s.WriteString(i.Replace.From)
    48  
    49  		if i.SubPath != "" {
    50  			s.WriteString("/")
    51  			s.WriteString(i.SubPath)
    52  		}
    53  
    54  		s.WriteString(" => ")
    55  	}
    56  
    57  	s.WriteString(i.Module)
    58  
    59  	if i.SubPath != "" {
    60  		s.WriteString("/")
    61  		s.WriteString(i.SubPath)
    62  	}
    63  
    64  	return s.String()
    65  }
    66  
    67  type ReplaceRule struct {
    68  	From string
    69  	modfile.ReplaceTarget
    70  }
    71  
    72  func (i Path) WithReplace(from string, replaceTarget modfile.ReplaceTarget) *Path {
    73  	i.Replace = &ReplaceRule{
    74  		From:          from,
    75  		ReplaceTarget: replaceTarget,
    76  	}
    77  	return &i
    78  }
    79  
    80  func (i *Path) SymlinkOrImport(ctx context.Context, root string) error {
    81  	if root == i.Dir {
    82  		// skip root dir
    83  		return nil
    84  	}
    85  
    86  	if !i.Mod.Root {
    87  		return nil
    88  	}
    89  
    90  	gen := ""
    91  
    92  	if i.Replace != nil && i.Replace.Import != "" {
    93  		gen = i.Replace.Import
    94  	}
    95  
    96  	resolveDest := func(base string, withPathMajor bool, genSource bool) string {
    97  		// for generated code
    98  		//
    99  		// pkg/
   100  		//   .cuemod/<import_path> -> <GOMODCACHE>/<import_path>@<version>/
   101  		if genSource {
   102  			return filepath.Join(root, "cue.mod/pkg/.cuemod", base)
   103  		}
   104  		// support multi versions of pkg
   105  		//
   106  		// gen/
   107  		//   <import_path>/v2  -> <GOMODCACHE>/<import_path>@<version>/
   108  		// pkg/
   109  		//   <import_path> -> <GOMODCACHE>/<import_path>@<version>/
   110  		if withPathMajor {
   111  			return filepath.Join(root, "cue.mod/gen", base)
   112  		}
   113  		return filepath.Join(root, "cue.mod/pkg", base)
   114  	}
   115  
   116  	_, pathMajor, ok := module.SplitPathVersion(i.ImportPathRoot())
   117  	withPathMajor := ok && pathMajor != ""
   118  
   119  	if err := i.symlink(ctx, i.ImportPathRootDir(), resolveDest(i.ImportPathRoot(), withPathMajor, gen != "")); err != nil {
   120  		return err
   121  	}
   122  
   123  	if gen != "" {
   124  		if err := cueify.ExtractToDir(
   125  			ctx,
   126  			gen,
   127  			resolveDest(i.ImportPath(), withPathMajor, gen != ""),
   128  			resolveDest(i.ImportPath(), withPathMajor, false),
   129  		); err != nil {
   130  			return err
   131  		}
   132  	}
   133  
   134  	return nil
   135  }
   136  
   137  func (i *Path) symlink(ctx context.Context, from string, to string) error {
   138  	if _, err := os.Stat(from); err != nil {
   139  		return errors.Wrapf(err, "invalid path %s", from)
   140  	}
   141  	return forceSymlink(from, to)
   142  }
   143  
   144  func forceSymlink(from, to string) error {
   145  	if err := os.RemoveAll(to); err != nil {
   146  		return err
   147  	}
   148  	if err := os.MkdirAll(filepath.Dir(to), 0755); err != nil {
   149  		return err
   150  	}
   151  	return os.Symlink(from, to)
   152  }
   153  
   154  func (i *Path) shouldReplace() bool {
   155  	return i.Replace != nil && i.Replace.From != i.Replace.Path
   156  }
   157  
   158  func (i *Path) ImportPath() string {
   159  	if i.shouldReplace() {
   160  		return i.Replace.From + filepath.Join(i.Module, i.SubPath)[len(i.Replace.Path):]
   161  	}
   162  	return filepath.Join(i.Module, i.SubPath)
   163  }
   164  
   165  func (i *Path) ResolvedImportPath() string {
   166  	return filepath.Join(i.Dir, i.SubPath)
   167  }
   168  
   169  func (i Path) ImportPathRoot() string {
   170  	if i.Replace != nil {
   171  		return i.Replace.From
   172  	}
   173  	return i.Repo
   174  }
   175  
   176  func (i Path) ImportPathRootDir() string {
   177  	ipr := i.ImportPathRoot()
   178  	ip := i.ImportPath()
   179  	if ip == ipr {
   180  		return i.Dir
   181  	}
   182  	rel, _ := subDir(ipr, ip)
   183  	dir := i.Dir
   184  	if i.SubPath != "" {
   185  		dir = filepath.Join(dir, i.SubPath)
   186  	}
   187  	return dir[0 : len(dir)-len("/"+rel)]
   188  }