diff --git a/docs/docs/segment-project.md b/docs/docs/segment-project.md
index 456ebc5f..78095ff9 100644
--- a/docs/docs/segment-project.md
+++ b/docs/docs/segment-project.md
@@ -14,6 +14,7 @@ Supports:
- Cargo project (`Cargo.toml`)
- Poetry project (`pyproject.toml`)
- PHP project (`composer.json`)
+- Any nuspec based project (`*.nuspec`, first file match info is displayed)
## Sample Configuration
diff --git a/src/segments/project.go b/src/segments/project.go
index 23b0b162..2e177aae 100644
--- a/src/segments/project.go
+++ b/src/segments/project.go
@@ -2,8 +2,10 @@ package segments
import (
"encoding/json"
+ "encoding/xml"
"oh-my-posh/environment"
"oh-my-posh/properties"
+ "path/filepath"
"github.com/BurntSushi/toml"
)
@@ -33,6 +35,14 @@ type PyProjectToolTOML struct {
Poetry ProjectData
}
+type NuSpec struct {
+ XMLName xml.Name `xml:"package"`
+ MetaData struct {
+ Title string `xml:"title"`
+ Version string `xml:"version"`
+ } `xml:"metadata"`
+}
+
type Project struct {
props properties.Properties
env environment.Environment
@@ -44,14 +54,13 @@ type Project struct {
}
func (n *Project) Enabled() bool {
- var enabled = false
for _, item := range n.projects {
- if !enabled {
- enabled = n.hasProjectFile(item)
+ if n.hasProjectFile(item) {
+ n.Version, n.Name = item.Fetcher(*item)
+ return len(n.Version) > 0 || len(n.Name) > 0
}
}
-
- return enabled
+ return false
}
func (n *Project) Template() string {
@@ -83,15 +92,11 @@ func (n *Project) Init(props properties.Properties, env environment.Environment)
File: "composer.json",
Fetcher: n.getNodePackage,
},
- }
-
- n.Version = ""
- n.Name = ""
- for _, item := range n.projects {
- if n.hasProjectFile(item) {
- n.Version, n.Name = item.Fetcher(*item)
- break
- }
+ {
+ Name: "nuspec",
+ File: "*.nuspec",
+ Fetcher: n.getNuSpecPackage,
+ },
}
}
@@ -137,3 +142,24 @@ func (n *Project) getPoetryPackage(item ProjectItem) (string, string) {
return data.Tool.Poetry.Version, data.Tool.Poetry.Name
}
+
+func (n *Project) getNuSpecPackage(item ProjectItem) (string, string) {
+ files := n.env.LsDir(n.env.Pwd())
+ var content string
+ // get the first match only
+ for _, file := range files {
+ if filepath.Ext(file.Name()) == ".nuspec" {
+ content = n.env.FileContent(file.Name())
+ break
+ }
+ }
+
+ var data NuSpec
+ err := xml.Unmarshal([]byte(content), &data)
+ if err != nil {
+ n.Error = err.Error()
+ return "", ""
+ }
+
+ return data.MetaData.Version, data.MetaData.Title
+}
diff --git a/src/segments/project_test.go b/src/segments/project_test.go
index e83634b9..f6c0a69d 100644
--- a/src/segments/project_test.go
+++ b/src/segments/project_test.go
@@ -1,7 +1,8 @@
package segments
import (
- "fmt"
+ "io/fs"
+ "io/ioutil"
"oh-my-posh/mock"
"oh-my-posh/properties"
"testing"
@@ -11,55 +12,257 @@ import (
testify_mock "github.com/stretchr/testify/mock"
)
-type MockData struct {
- Name string
- Case string
- ExpectedString string
- PackageContents string
- File string
+type MockDirEntry struct {
+ name string
+ isDir bool
+ fileMode fs.FileMode
+ fileInfo fs.FileInfo
+ err error
}
-func getMockedPackageEnv(tc *MockData) (*mock.MockedEnvironment, properties.Map) {
- env := new(mock.MockedEnvironment)
- props := properties.Map{}
- env.On("HasFiles", testify_mock.Anything).Run(func(args testify_mock.Arguments) {
- for _, c := range env.ExpectedCalls {
- if c.Method == "HasFiles" {
- c.ReturnArguments = testify_mock.Arguments{args.Get(0).(string) == tc.File}
- }
- }
- })
- env.On("FileContent", tc.File).Return(tc.PackageContents)
- return env, props
+func (m *MockDirEntry) Name() string {
+ return m.name
+}
+
+func (m *MockDirEntry) IsDir() bool {
+ return m.isDir
+}
+
+func (m *MockDirEntry) Type() fs.FileMode {
+ return m.fileMode
+}
+
+func (m *MockDirEntry) Info() (fs.FileInfo, error) {
+ return m.fileInfo, m.err
}
func TestPackage(t *testing.T) {
- cases := []*MockData{
- {Case: "1.0.0 node.js", ExpectedString: "\uf487 1.0.0 test", Name: "node", File: "package.json", PackageContents: "{\"version\":\"1.0.0\",\"name\":\"test\"}"},
- {Case: "1.0.0 php", ExpectedString: "\uf487 1.0.0 test", Name: "php", File: "composer.json", PackageContents: "{\"version\":\"1.0.0\",\"name\":\"test\"}"},
- {Case: "3.2.1 node.js", ExpectedString: "\uf487 3.2.1 test", Name: "node", File: "package.json", PackageContents: "{\"version\":\"3.2.1\",\"name\":\"test\"}"},
- {Case: "1.0.0 cargo", ExpectedString: "\uf487 1.0.0 test", Name: "cargo", File: "Cargo.toml", PackageContents: "[package]\nname=\"test\"\nversion=\"1.0.0\"\n"},
- {Case: "3.2.1 cargo", ExpectedString: "\uf487 3.2.1 test", Name: "cargo", File: "Cargo.toml", PackageContents: "[package]\nname=\"test\"\nversion=\"3.2.1\"\n"},
- {Case: "1.0.0 poetry", ExpectedString: "\uf487 1.0.0 test", Name: "poetry", File: "pyproject.toml", PackageContents: "[tool.poetry]\nname=\"test\"\nversion=\"1.0.0\"\n"},
- {Case: "3.2.1 poetry", ExpectedString: "\uf487 3.2.1 test", Name: "poetry", File: "pyproject.toml", PackageContents: "[tool.poetry]\nname=\"test\"\nversion=\"3.2.1\"\n"},
- {Case: "No version present node.js", ExpectedString: "test", Name: "node", File: "package.json", PackageContents: "{\"name\":\"test\"}"},
- {Case: "No version present cargo", ExpectedString: "test", Name: "cargo", File: "Cargo.toml", PackageContents: "[package]\nname=\"test\"\n"},
- {Case: "No version present poetry", ExpectedString: "test", Name: "poetry", File: "pyproject.toml", PackageContents: "[tool.poetry]\nname=\"test\"\n"},
- {Case: "No name present node.js", ExpectedString: "\uf487 1.0.0", Name: "node", File: "package.json", PackageContents: "{\"version\":\"1.0.0\"}"},
- {Case: "No name present cargo", ExpectedString: "\uf487 1.0.0", Name: "cargo", File: "Cargo.toml", PackageContents: "[package]\nversion=\"1.0.0\"\n"},
- {Case: "No name present poetry", ExpectedString: "\uf487 1.0.0", Name: "poetry", File: "pyproject.toml", PackageContents: "[tool.poetry]\nversion=\"1.0.0\"\n"},
- {Case: "Empty project package node.js", ExpectedString: "", Name: "node", File: "package.json", PackageContents: "{}"},
- {Case: "Empty project package cargo", ExpectedString: "", Name: "cargo", File: "Cargo.toml", PackageContents: ""},
- {Case: "Empty project package poetry", ExpectedString: "", Name: "poetry", File: "pyproject.toml", PackageContents: ""},
- {Case: "Invalid json", ExpectedString: "invalid character '}' looking for beginning of value", Name: "node", File: "package.json", PackageContents: "}"},
- {Case: "Invalid toml", ExpectedString: "toml: line 1: unexpected end of table name (table names cannot be empty)", Name: "cargo", File: "Cargo.toml", PackageContents: "["},
+ cases := []struct {
+ Name string
+ Case string
+ File string
+ PackageContents string
+ ExpectedString string
+ ExpectedEnabled bool
+ }{
+ {
+ Case: "1.0.0 node.js",
+ ExpectedEnabled: true,
+ ExpectedString: "\uf487 1.0.0 test",
+ Name: "node",
+ File: "package.json",
+ PackageContents: "{\"version\":\"1.0.0\",\"name\":\"test\"}",
+ },
+ {
+ Case: "1.0.0 php",
+ ExpectedEnabled: true,
+ ExpectedString: "\uf487 1.0.0 test",
+ Name: "php",
+ File: "composer.json",
+ PackageContents: "{\"version\":\"1.0.0\",\"name\":\"test\"}",
+ },
+ {
+ Case: "3.2.1 node.js",
+ ExpectedEnabled: true,
+ ExpectedString: "\uf487 3.2.1 test",
+ Name: "node", File: "package.json",
+ PackageContents: "{\"version\":\"3.2.1\",\"name\":\"test\"}",
+ },
+ {
+ Case: "1.0.0 cargo",
+ ExpectedEnabled: true,
+ ExpectedString: "\uf487 1.0.0 test",
+ Name: "cargo",
+ File: "Cargo.toml",
+ PackageContents: "[package]\nname=\"test\"\nversion=\"1.0.0\"\n",
+ },
+ {
+ Case: "3.2.1 cargo",
+ ExpectedEnabled: true,
+ ExpectedString: "\uf487 3.2.1 test",
+ Name: "cargo",
+ File: "Cargo.toml",
+ PackageContents: "[package]\nname=\"test\"\nversion=\"3.2.1\"\n",
+ },
+ {
+ Case: "1.0.0 poetry",
+ ExpectedEnabled: true,
+ ExpectedString: "\uf487 1.0.0 test",
+ Name: "poetry",
+ File: "pyproject.toml",
+ PackageContents: "[tool.poetry]\nname=\"test\"\nversion=\"1.0.0\"\n",
+ },
+ {
+ Case: "3.2.1 poetry",
+ ExpectedEnabled: true,
+ ExpectedString: "\uf487 3.2.1 test",
+ Name: "poetry",
+ File: "pyproject.toml",
+ PackageContents: "[tool.poetry]\nname=\"test\"\nversion=\"3.2.1\"\n",
+ },
+ {
+ Case: "No version present node.js",
+ ExpectedEnabled: true,
+ ExpectedString: "test",
+ Name: "node",
+ File: "package.json",
+ PackageContents: "{\"name\":\"test\"}",
+ },
+ {
+ Case: "No version present cargo",
+ ExpectedEnabled: true,
+ ExpectedString: "test",
+ Name: "cargo",
+ File: "Cargo.toml",
+ PackageContents: "[package]\nname=\"test\"\n",
+ },
+ {
+ Case: "No version present poetry",
+ ExpectedEnabled: true,
+ ExpectedString: "test",
+ Name: "poetry",
+ File: "pyproject.toml",
+ PackageContents: "[tool.poetry]\nname=\"test\"\n",
+ },
+ {
+ Case: "No name present node.js",
+ ExpectedEnabled: true,
+ ExpectedString: "\uf487 1.0.0",
+ Name: "node",
+ File: "package.json",
+ PackageContents: "{\"version\":\"1.0.0\"}",
+ },
+ {
+ Case: "No name present cargo",
+ ExpectedEnabled: true,
+ ExpectedString: "\uf487 1.0.0",
+ Name: "cargo",
+ File: "Cargo.toml",
+ PackageContents: "[package]\nversion=\"1.0.0\"\n",
+ },
+ {
+ Case: "No name present poetry",
+ ExpectedEnabled: true,
+ ExpectedString: "\uf487 1.0.0",
+ Name: "poetry",
+ File: "pyproject.toml",
+ PackageContents: "[tool.poetry]\nversion=\"1.0.0\"\n",
+ },
+ {
+ Case: "Empty project package node.js",
+ ExpectedEnabled: false,
+ Name: "node",
+ File: "package.json",
+ PackageContents: "{}",
+ },
+ {
+ Case: "Empty project package cargo",
+ Name: "cargo",
+ File: "Cargo.toml",
+ PackageContents: "",
+ },
+ {
+ Case: "Empty project package poetry",
+ Name: "poetry",
+ File: "pyproject.toml",
+ PackageContents: "",
+ },
+ {
+ Case: "Invalid json",
+ ExpectedString: "invalid character '}' looking for beginning of value",
+ Name: "node",
+ File: "package.json",
+ PackageContents: "}",
+ },
+ {
+ Case: "Invalid toml",
+ ExpectedString: "toml: line 1: unexpected end of table name (table names cannot be empty)",
+ Name: "cargo",
+ File: "Cargo.toml",
+ PackageContents: "[",
+ },
}
for _, tc := range cases {
- env, props := getMockedPackageEnv(tc)
+ env := new(mock.MockedEnvironment)
+ env.On("HasFiles", testify_mock.Anything).Run(func(args testify_mock.Arguments) {
+ for _, c := range env.ExpectedCalls {
+ if c.Method == "HasFiles" {
+ c.ReturnArguments = testify_mock.Arguments{args.Get(0).(string) == tc.File}
+ }
+ }
+ })
+ env.On("FileContent", tc.File).Return(tc.PackageContents)
pkg := &Project{}
- pkg.Init(props, env)
- assert.True(t, pkg.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case))
- assert.Equal(t, tc.ExpectedString, renderTemplate(env, pkg.Template(), pkg), fmt.Sprintf("Failed in case: %s", tc.Case))
+ pkg.Init(properties.Map{}, env)
+ assert.Equal(t, tc.ExpectedEnabled, pkg.Enabled(), tc.Case)
+ if tc.ExpectedEnabled {
+ assert.Equal(t, tc.ExpectedString, renderTemplate(env, pkg.Template(), pkg), tc.Case)
+ }
+ }
+}
+
+func TestNuspecPackage(t *testing.T) {
+ cases := []struct {
+ Case string
+ HasFiles bool
+ FileName string
+ ExpectedString string
+ ExpectedEnabled bool
+ }{
+ {
+ Case: "valid file",
+ FileName: "../test/valid.nuspec",
+ HasFiles: true,
+ ExpectedEnabled: true,
+ ExpectedString: "\uf487 0.1.0 Az.Compute",
+ },
+ {
+ Case: "invalid file",
+ FileName: "../test/invalid.nuspec",
+ HasFiles: true,
+ ExpectedEnabled: false,
+ },
+ {
+ Case: "no info in file",
+ FileName: "../test/empty.nuspec",
+ HasFiles: true,
+ ExpectedEnabled: false,
+ },
+ {
+ Case: "no files",
+ HasFiles: false,
+ ExpectedEnabled: false,
+ },
+ }
+
+ for _, tc := range cases {
+ env := new(mock.MockedEnvironment)
+ env.On("HasFiles", testify_mock.Anything).Run(func(args testify_mock.Arguments) {
+ for _, c := range env.ExpectedCalls {
+ if c.Method != "HasFiles" {
+ continue
+ }
+ if args.Get(0).(string) == "*.nuspec" {
+ c.ReturnArguments = testify_mock.Arguments{tc.HasFiles}
+ continue
+ }
+ c.ReturnArguments = testify_mock.Arguments{false}
+ }
+ })
+ env.On("Pwd").Return("posh")
+ env.On("LsDir", "posh").Return([]fs.DirEntry{
+ &MockDirEntry{
+ name: tc.FileName,
+ },
+ })
+ content, _ := ioutil.ReadFile(tc.FileName)
+ env.On("FileContent", tc.FileName).Return(string(content))
+ pkg := &Project{}
+ pkg.Init(properties.Map{}, env)
+ assert.Equal(t, tc.ExpectedEnabled, pkg.Enabled(), tc.Case)
+ if tc.ExpectedEnabled {
+ assert.Equal(t, tc.ExpectedString, renderTemplate(env, pkg.Template(), pkg), tc.Case)
+ }
}
}
diff --git a/src/test/empty.nuspec b/src/test/empty.nuspec
new file mode 100644
index 00000000..aab6bbee
--- /dev/null
+++ b/src/test/empty.nuspec
@@ -0,0 +1,31 @@
+
+
+
+ Az.Compute
+ Microsoft Corporation
+ Microsoft Corporation
+ true
+ https://aka.ms/azps-license
+ https://github.com/Azure/azure-powershell
+ Microsoft Azure PowerShell: $(service-name) cmdlets
+
+ Microsoft Corporation. All rights reserved.
+ Azure ResourceManager ARM PSModule $(service-name)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/invalid.nuspec b/src/test/invalid.nuspec
new file mode 100644
index 00000000..0967ef42
--- /dev/null
+++ b/src/test/invalid.nuspec
@@ -0,0 +1 @@
+{}
diff --git a/src/test/valid.nuspec b/src/test/valid.nuspec
new file mode 100644
index 00000000..0bfe7a68
--- /dev/null
+++ b/src/test/valid.nuspec
@@ -0,0 +1,33 @@
+
+
+
+ Az.Compute
+ Az.Compute
+ 0.1.0
+ Microsoft Corporation
+ Microsoft Corporation
+ true
+ https://aka.ms/azps-license
+ https://github.com/Azure/azure-powershell
+ Microsoft Azure PowerShell: $(service-name) cmdlets
+
+ Microsoft Corporation. All rights reserved.
+ Azure ResourceManager ARM PSModule $(service-name)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+