Skip to content

Commit

Permalink
Improve go.mod parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
andyone committed May 10, 2023
1 parent db8b034 commit 82f9a91
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 137 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

<p align="center">
<a href="https://kaos.sh/g/depsy"><img src="https://gh.kaos.st/godoc.svg" alt="PkgGoDev" /></a>
<a href="https://kaos.sh/w/depsy/ci"><img src="https://kaos.sh/w/depsy/ci.svg" alt="GitHub Actions CI Status" /></a>
<a href="https://kaos.sh/r/depsy"><img src="https://kaos.sh/r/depsy.svg" alt="GoReportCard" /></a>
<a href="https://kaos.sh/c/depsy"><img src="https://kaos.sh/c/depsy.svg" alt="Coverage Status" /></a>
<a href="https://kaos.sh/l/depsy"><img src="https://kaos.sh/l/b51c11f14ac06e529437.svg" alt="Code Climate Maintainability" /></a>
<a href="https://kaos.sh/b/depsy"><img src="https://kaos.sh/b/d2067e6e-8722-4f20-8274-4398ffa09e97.svg" alt="Codebeat badge" /></a>
<br/>
<a href="https://kaos.sh/c/depsy"><img src="https://kaos.sh/c/depsy.svg" alt="Coverage Status" /></a>
<a href="https://kaos.sh/w/depsy/ci"><img src="https://kaos.sh/w/depsy/ci.svg" alt="GitHub Actions CI Status" /></a>
<a href="https://kaos.sh/w/depsy/codeql"><img src="https://kaos.sh/w/depsy/codeql.svg" alt="GitHub Actions CodeQL Status" /></a>
<a href="#license"><img src="https://gh.kaos.st/apache2.svg"></a>
</p>
Expand All @@ -32,7 +34,7 @@ Second reason — with debug package, you can't print only direct dependencies.

### Installation

Make sure you have a working Go 1.18+ workspace (_[instructions](https://golang.org/doc/install)_), then:
Make sure you have a working Go 1.18+ workspace (_[instructions](https://go.dev/doc/install)_), then:

````bash
go get github.com/essentialkaos/depsy
Expand Down
186 changes: 150 additions & 36 deletions depsy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package depsy

// ////////////////////////////////////////////////////////////////////////////////// //
// //
// Copyright (c) 2022 ESSENTIAL KAOS //
// Copyright (c) 2023 ESSENTIAL KAOS //
// Apache License, Version 2.0 <https://www.apache.org/licenses/LICENSE-2.0> //
// //
// ////////////////////////////////////////////////////////////////////////////////// //
Expand All @@ -21,12 +21,26 @@ type Dependency struct {
Extra string
}

// Dependencies is slice with dependencies info
type Dependencies []Dependency

// ////////////////////////////////////////////////////////////////////////////////// //

type replacement struct {
From Dependency
To Dependency
}

type replacements []replacement

// ////////////////////////////////////////////////////////////////////////////////// //

// Extract extracts data from go.mod data
func Extract(data []byte, withIndirect bool) []Dependency {
var result []Dependency
var replaceSection bool
func Extract(data []byte, withIndirect bool) Dependencies {
var deps Dependencies
var repls replacements

var reqSection, replSection bool

buf := bytes.NewBuffer(data)

Expand All @@ -39,59 +53,93 @@ func Extract(data []byte, withIndirect bool) []Dependency {

line = strings.Trim(line, "\n\r\t")

if !replaceSection {
if line == "require (" {
replaceSection = true
continue
} else if strings.HasPrefix(line, "require ") {
line = line[8:]
} else {
continue
}
} else {
if line == ")" {
replaceSection = false
continue
}
if line == "" || strings.HasPrefix(line, "//") {
continue
}

if !withIndirect && strings.HasSuffix(line, "// indirect") {
continue
}

result = append(result, parseDependencyLine(line))
switch {
case line == "require (":
reqSection = true
case line == "replace (":
replSection = true
case line == ")":
reqSection, replSection = false, false
case strings.HasPrefix(line, "require "):
deps = addDep(deps, parseDependencyLine(line[8:]))
case strings.HasPrefix(line, "replace "):
repls = addRepl(repls, parseReplacementLine(line[8:]))
case reqSection:
deps = addDep(deps, parseDependencyLine(line))
case replSection:
repls = addRepl(repls, parseReplacementLine(line))
}
}

return result
REPL:
for _, repl := range repls {
for index, dep := range deps {
if repl.From.Version == "" {
if dep.Path == repl.From.Path {
deps[index] = repl.To
continue REPL
}
} else {
if dep.String() == repl.From.String() {
deps[index] = repl.To
continue REPL
}
}
}
}

return deps
}

// ////////////////////////////////////////////////////////////////////////////////// //

// parseDependencyLine parses line from go.mod with info about module
func parseDependencyLine(data string) Dependency {
info := strings.Fields(data)

if len(info) < 2 {
return Dependency{}
// String returns string representation of dependency
func (d Dependency) String() string {
if d.Extra == "" {
return d.Path + ":" + d.Version
}

var path, version, extra string
return d.Path + ":" + d.Version + "+" + d.Extra
}

path = info[0]
version = info[1]
// ////////////////////////////////////////////////////////////////////////////////// //

if strings.HasPrefix(version, "v") {
version = version[1:]
// addDep appends dep to slice with dependencies if not empty
func addDep(deps Dependencies, dep Dependency) Dependencies {
if dep.Path == "" {
return deps
}

version = strings.ReplaceAll(version, "+incompatible", "")
return append(deps, dep)
}

if strings.Contains(version, "-") {
index := strings.Index(version, "-")
extra = version[index+1:]
version = version[:index]
// addRepl appends replacement to slice with replacements if not empty
func addRepl(repls replacements, repl replacement) replacements {
if repl.To.Path == "" || repl.From.Path == "" {
return repls
}

return append(repls, repl)
}

// parseDependencyLine parses line from go.mod with info about module
func parseDependencyLine(data string) Dependency {
info := strings.Fields(data)

if len(info) < 2 {
return Dependency{}
}

path := getField(info, 0)
version, extra := parseVersion(getField(info, 1))
majorVersion := getMajorVersion(version)

if strings.HasSuffix(path, "/v"+majorVersion) {
Expand All @@ -105,6 +153,63 @@ func parseDependencyLine(data string) Dependency {
}
}

// parseReplacementLine parses line from go.mod with info module replacement
func parseReplacementLine(data string) replacement {
info := strings.Fields(data)

if len(info) < 3 {
return replacement{}
}

var fromPath, fromVer, fromExtra string
var toPath, toVer, toExtra string

fromPath = getField(info, 0)

switch getField(info, 1) {
case "=>":
toPath = getField(info, 2)
toVer = getField(info, 3)
default:
fromVer = getField(info, 1)
toPath = getField(info, 3)
toVer = getField(info, 4)
}

if toPath[0] == '.' {
return replacement{}
}

fromVer, fromExtra = parseVersion(fromVer)
toVer, toExtra = parseVersion(toVer)
majorVersion := getMajorVersion(toVer)

if strings.HasSuffix(toPath, "/v"+majorVersion) {
toPath = toPath[:len(toPath)-(len(majorVersion)+2)]
}

return replacement{
From: Dependency{fromPath, fromVer, fromExtra},
To: Dependency{toPath, toVer, toExtra},
}
}

// parseVersion parses version info
func parseVersion(version string) (string, string) {
if strings.HasPrefix(version, "v") {
version = version[1:]
}

version = strings.ReplaceAll(version, "+incompatible", "")

if strings.Contains(version, "-") {
index := strings.Index(version, "-")
return version[:index], version[index+1:]
}

return version, ""
}

// getMajorVersion returns major version for semver string
func getMajorVersion(v string) string {
if !strings.Contains(v, ".") {
Expand All @@ -113,3 +218,12 @@ func getMajorVersion(v string) string {

return v[:strings.Index(v, ".")]
}

// getField returns item with given index from slice
func getField(data []string, index int) string {
if index >= len(data) {
return ""
}

return data[index]
}
14 changes: 12 additions & 2 deletions depsy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package depsy

// ////////////////////////////////////////////////////////////////////////////////// //
// //
// Copyright (c) 2022 ESSENTIAL KAOS //
// Copyright (c) 2023 ESSENTIAL KAOS //
// Apache License, Version 2.0 <https://www.apache.org/licenses/LICENSE-2.0> //
// //
// ////////////////////////////////////////////////////////////////////////////////// //
Expand All @@ -27,7 +27,7 @@ var _ = Suite(&DepsySuite{})
// ////////////////////////////////////////////////////////////////////////////////// //

func (s *DepsySuite) TestExtract(c *C) {
data, err := ioutil.ReadFile("testdata/etcd.mod")
data, err := ioutil.ReadFile("testdata/test1.mod")
c.Assert(err, IsNil)
deps := Extract(data, false)
c.Assert(len(deps), Equals, 20)
Expand All @@ -43,9 +43,19 @@ func (s *DepsySuite) TestExtract(c *C) {
deps = Extract(data, true)
c.Assert(len(deps), Equals, 4)
c.Assert(deps[0], DeepEquals, Dependency{"github.com/essentialkaos/check", "1.4.0", ""})

data, err = ioutil.ReadFile("testdata/test2.mod")
c.Assert(err, IsNil)
deps = Extract(data, true)

c.Assert(deps[5], DeepEquals, Dependency{"github.com/milvus-io/pulsar-client-go", "0.6.10", ""})
}

func (s *DepsySuite) TestAux(c *C) {
c.Assert(parseDependencyLine(""), DeepEquals, Dependency{})
c.Assert(parseReplacementLine(""), DeepEquals, replacement{})
c.Assert(getMajorVersion("12"), Equals, "12")

var deps Dependencies
c.Assert(addDep(deps, Dependency{}), IsNil)
}
Loading

0 comments on commit 82f9a91

Please sign in to comment.