Skip to content

Commit

Permalink
refactor: generic language support
Browse files Browse the repository at this point in the history
  • Loading branch information
JanDeDobbeleer committed Feb 4, 2021
1 parent 7e81389 commit c43cbac
Show file tree
Hide file tree
Showing 7 changed files with 314 additions and 207 deletions.
18 changes: 10 additions & 8 deletions src/segment_dotnet.go
Expand Up @@ -25,15 +25,17 @@ func (d *dotnet) string() string {

func (d *dotnet) init(props *properties, env environmentInfo) {
d.language = &language{
env: env,
props: props,
commands: []string{"dotnet"},
versionParam: "--version",
extensions: []string{"*.cs", "*.vb", "*.sln", "*.csproj", "*.vbproj"},
version: &version{
regex: `(?:(?P<version>((?P<major>[0-9]+).(?P<minor>[0-9]+).(?:\d{2})(?P<patch>[0-9]{1}))))`,
urlTemplate: "[%1s](https://github.com/dotnet/core/blob/master/release-notes/%[2]s.%[3]s/%[2]s.%[3]s.%[4]s/%[2]s.%[3]s.%[4]s.md)",
env: env,
props: props,
extensions: []string{"*.cs", "*.vb", "*.sln", "*.csproj", "*.vbproj"},
commands: []*cmd{
{
executable: "dotnet",
args: []string{"--version"},
regex: `(?:(?P<version>((?P<major>[0-9]+).(?P<minor>[0-9]+).(?:\d{2})(?P<patch>[0-9]{1}))))`,
},
},
versionURLTemplate: "[%1s](https://github.com/dotnet/core/blob/master/release-notes/%[2]s.%[3]s/%[2]s.%[3]s.%[4]s/%[2]s.%[3]s.%[4]s.md)",
}
}

Expand Down
18 changes: 10 additions & 8 deletions src/segment_golang.go
Expand Up @@ -10,15 +10,17 @@ func (g *golang) string() string {

func (g *golang) init(props *properties, env environmentInfo) {
g.language = &language{
env: env,
props: props,
commands: []string{"go"},
versionParam: "version",
extensions: []string{"*.go", "go.mod"},
version: &version{
regex: `(?:go(?P<version>((?P<major>[0-9]+).(?P<minor>[0-9]+).(?P<patch>[0-9]+))))`,
urlTemplate: "[%s](https://golang.org/doc/go%s.%s)",
env: env,
props: props,
extensions: []string{"*.go", "go.mod"},
commands: []*cmd{
{
executable: "go",
args: []string{"version"},
regex: `(?:go(?P<version>((?P<major>[0-9]+).(?P<minor>[0-9]+).(?P<patch>[0-9]+))))`,
},
},
versionURLTemplate: "[%s](https://golang.org/doc/go%s.%s)",
}
}

Expand Down
16 changes: 9 additions & 7 deletions src/segment_julia.go
Expand Up @@ -10,13 +10,15 @@ func (j *julia) string() string {

func (j *julia) init(props *properties, env environmentInfo) {
j.language = &language{
env: env,
props: props,
commands: []string{"julia"},
versionParam: "--version",
extensions: []string{"*.jl"},
version: &version{
regex: `julia version (?P<version>[0-9]+.[0-9]+.[0-9]+)`,
env: env,
props: props,
extensions: []string{"*.jl"},
commands: []*cmd{
{
executable: "julia",
args: []string{"--version"},
regex: `julia version (?P<version>[0-9]+.[0-9]+.[0-9]+)`,
},
},
}
}
Expand Down
133 changes: 66 additions & 67 deletions src/segment_language.go
Expand Up @@ -11,38 +11,63 @@ type loadContext func()
type inContext func() bool

type version struct {
full string
major string
minor string
patch string
regex string
urlTemplate string
full string
major string
minor string
patch string
}

func (v *version) parse(versionInfo string) error {
values := findNamedRegexMatch(v.regex, versionInfo)
type cmd struct {
executable string
args []string
regex string
version *version
}

func (c *cmd) parse(versionInfo string) error {
values := findNamedRegexMatch(c.regex, versionInfo)
if len(values) == 0 {
return errors.New("cannot parse version string")
}

v.full = values["version"]
v.major = values["major"]
v.minor = values["minor"]
v.patch = values["patch"]
c.version = &version{}
c.version.full = values["version"]
c.version.major = values["major"]
c.version.minor = values["minor"]
c.version.patch = values["patch"]
return nil
}

func (c *cmd) buildVersionURL(template string) string {
if template == "" {
return c.version.full
}
truncatingSprintf := func(str string, args ...interface{}) (string, error) {
n := strings.Count(str, "%s")
if n > len(args) {
return "", errors.New("Too many parameters")
}
if n == 0 {
return fmt.Sprintf(str, args...), nil
}
return fmt.Sprintf(str, args[:n]...), nil
}
version, err := truncatingSprintf(template, c.version.full, c.version.major, c.version.minor, c.version.patch)
if err != nil {
return c.version.full
}
return version
}

type language struct {
props *properties
env environmentInfo
extensions []string
commands []string
executable string
versionParam string
version *version
exitCode int
loadContext loadContext
inContext inContext
props *properties
env environmentInfo
extensions []string
commands []*cmd
versionURLTemplate string
activeCommand *cmd
exitCode int
loadContext loadContext
inContext inContext
}

const (
Expand All @@ -66,24 +91,16 @@ func (l *language) string() string {
if !l.props.getBool(DisplayVersion, true) {
return ""
}
if !l.hasCommand() {
return l.props.getString(MissingCommandTextProperty, MissingCommandText)
}

err := l.setVersion()
if err != nil {
return ""
return err.Error()
}

// build release notes hyperlink
if l.props.getBool(EnableHyperlink, false) && l.version.urlTemplate != "" {
version, err := TruncatingSprintf(l.version.urlTemplate, l.version.full, l.version.major, l.version.minor, l.version.patch)
if err != nil {
return l.version.full
}
return version
if l.props.getBool(EnableHyperlink, false) {
return l.activeCommand.buildVersionURL(l.versionURLTemplate)
}
return l.version.full
return l.activeCommand.version.full
}

func (l *language) enabled() bool {
Expand Down Expand Up @@ -119,30 +136,23 @@ func (l *language) hasLanguageFiles() bool {

// setVersion parses the version string returned by the command
func (l *language) setVersion() error {
versionInfo, err := l.env.runCommand(l.executable, l.versionParam)
if exitErr, ok := err.(*commandError); ok {
l.exitCode = exitErr.exitCode
return errors.New("error executing command")
}
err = l.version.parse(versionInfo)
if err != nil {
return err
}
return nil
}

// hasCommand checks if one of the commands exists and sets it as executable
func (l *language) hasCommand() bool {
for i, command := range l.commands {
if l.env.hasCommand(command) {
l.executable = command
break
for _, command := range l.commands {
if !l.env.hasCommand(command.executable) {
continue
}
if i == len(l.commands)-1 {
return false
version, err := l.env.runCommand(command.executable, command.args...)
if exitErr, ok := err.(*commandError); ok {
l.exitCode = exitErr.exitCode
return fmt.Errorf("err executing %s with %s", command.executable, command.args)
}
err = command.parse(version)
if err != nil {
return fmt.Errorf("err parsing info from %s with %s", command.executable, version)
}
l.activeCommand = command
return nil
}
return true
return errors.New(l.props.getString(MissingCommandTextProperty, MissingCommandText))
}

func (l *language) loadLanguageContext() {
Expand All @@ -158,14 +168,3 @@ func (l *language) inLanguageContext() bool {
}
return l.inContext()
}

func TruncatingSprintf(str string, args ...interface{}) (string, error) {
n := strings.Count(str, "%s")
if n > len(args) {
return "", errors.New("Too many parameters")
}
if n == 0 {
return fmt.Sprintf(str, args...), nil
}
return fmt.Sprintf(str, args[:n]...), nil
}

0 comments on commit c43cbac

Please sign in to comment.