github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/gnovm/pkg/gnomod/preprocess.go (about)

     1  package gnomod
     2  
     3  import (
     4  	"golang.org/x/mod/modfile"
     5  	"golang.org/x/mod/module"
     6  	"golang.org/x/mod/semver"
     7  )
     8  
     9  func removeDups(syntax *modfile.FileSyntax, require *[]*modfile.Require, replace *[]*modfile.Replace) {
    10  	if require != nil {
    11  		purged := removeRequireDups(require)
    12  		cleanSyntaxTree(syntax, purged)
    13  	}
    14  	if replace != nil {
    15  		purged := removeReplaceDups(replace)
    16  		cleanSyntaxTree(syntax, purged)
    17  	}
    18  }
    19  
    20  // removeRequireDups removes duplicate requirements.
    21  // Requirements with higher version takes priority.
    22  func removeRequireDups(require *[]*modfile.Require) map[*modfile.Line]bool {
    23  	purge := make(map[*modfile.Line]bool)
    24  
    25  	keepRequire := make(map[string]string)
    26  	for _, r := range *require {
    27  		if v, ok := keepRequire[r.Mod.Path]; ok {
    28  			if semver.Compare(r.Mod.Version, v) == 1 {
    29  				keepRequire[r.Mod.Path] = r.Mod.Version
    30  			}
    31  			continue
    32  		}
    33  		keepRequire[r.Mod.Path] = r.Mod.Version
    34  	}
    35  	var req []*modfile.Require
    36  	added := make(map[string]bool)
    37  	for _, r := range *require {
    38  		if v, ok := keepRequire[r.Mod.Path]; ok && !added[r.Mod.Path] && v == r.Mod.Version {
    39  			req = append(req, r)
    40  			added[r.Mod.Path] = true
    41  			continue
    42  		}
    43  		purge[r.Syntax] = true
    44  	}
    45  	*require = req
    46  
    47  	return purge
    48  }
    49  
    50  // removeReplaceDups removes duplicate replacements.
    51  // Later replacements take priority over earlier ones.
    52  func removeReplaceDups(replace *[]*modfile.Replace) map[*modfile.Line]bool {
    53  	purge := make(map[*modfile.Line]bool)
    54  
    55  	haveReplace := make(map[module.Version]bool)
    56  	for i := len(*replace) - 1; i >= 0; i-- {
    57  		x := (*replace)[i]
    58  		if haveReplace[x.Old] { // duplicate detected
    59  			purge[x.Syntax] = true
    60  			continue
    61  		}
    62  		haveReplace[x.Old] = true
    63  	}
    64  	var repl []*modfile.Replace
    65  	for _, r := range *replace {
    66  		if !purge[r.Syntax] {
    67  			repl = append(repl, r)
    68  		}
    69  	}
    70  	*replace = repl
    71  
    72  	return purge
    73  }
    74  
    75  // cleanSyntaxTree removes purged statements from the syntax tree.
    76  func cleanSyntaxTree(syntax *modfile.FileSyntax, purge map[*modfile.Line]bool) {
    77  	stmts := make([]modfile.Expr, 0, len(syntax.Stmt))
    78  	for _, stmt := range syntax.Stmt {
    79  		switch stmt := stmt.(type) {
    80  		case *modfile.Line:
    81  			if purge[stmt] {
    82  				continue
    83  			}
    84  		case *modfile.LineBlock:
    85  			var lines []*modfile.Line
    86  			for _, line := range stmt.Line {
    87  				if !purge[line] {
    88  					lines = append(lines, line)
    89  				}
    90  			}
    91  			stmt.Line = lines
    92  			if len(lines) == 0 {
    93  				continue
    94  			}
    95  		}
    96  		stmts = append(stmts, stmt)
    97  	}
    98  	syntax.Stmt = stmts
    99  }