feat: App struct with New(Options) + Find() as method
App.New() creates from Options. App.Find() locates programs on PATH. Both are struct methods — no package-level functions. 8 tests passing. Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
a49bc46bc7
commit
2a81b4f576
2 changed files with 68 additions and 25 deletions
52
app.go
52
app.go
|
|
@ -1,7 +1,6 @@
|
|||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
// Application identity for the Core framework.
|
||||
// Based on leaanthony/sail — Name, Filename, Path.
|
||||
|
||||
package core
|
||||
|
||||
|
|
@ -11,32 +10,47 @@ import (
|
|||
)
|
||||
|
||||
// App holds the application identity and optional GUI runtime.
|
||||
//
|
||||
// app := core.App{}.New(core.NewOptions(
|
||||
// core.Option{Key: "name", Value: "Core CLI"},
|
||||
// core.Option{Key: "version", Value: "1.0.0"},
|
||||
// ))
|
||||
type App struct {
|
||||
// Name is the human-readable application name (e.g., "Core CLI").
|
||||
Name string
|
||||
|
||||
// Version is the application version string (e.g., "1.2.3").
|
||||
Version string
|
||||
|
||||
// Description is a short description of the application.
|
||||
Name string
|
||||
Version string
|
||||
Description string
|
||||
Filename string
|
||||
Path string
|
||||
Runtime any // GUI runtime (e.g., Wails App). Nil for CLI-only.
|
||||
}
|
||||
|
||||
// Filename is the executable filename (e.g., "core").
|
||||
Filename string
|
||||
|
||||
// Path is the absolute path to the executable.
|
||||
Path string
|
||||
|
||||
// Runtime is the GUI runtime (e.g., Wails App).
|
||||
// Nil for CLI-only applications.
|
||||
Runtime any
|
||||
// New creates an App from Options.
|
||||
//
|
||||
// app := core.App{}.New(core.NewOptions(
|
||||
// core.Option{Key: "name", Value: "myapp"},
|
||||
// core.Option{Key: "version", Value: "1.0.0"},
|
||||
// ))
|
||||
func (a App) New(opts Options) App {
|
||||
if name := opts.String("name"); name != "" {
|
||||
a.Name = name
|
||||
}
|
||||
if version := opts.String("version"); version != "" {
|
||||
a.Version = version
|
||||
}
|
||||
if desc := opts.String("description"); desc != "" {
|
||||
a.Description = desc
|
||||
}
|
||||
if filename := opts.String("filename"); filename != "" {
|
||||
a.Filename = filename
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Find locates a program on PATH and returns a Result containing the App.
|
||||
//
|
||||
// r := core.Find("node", "Node.js")
|
||||
// r := core.App{}.Find("node", "Node.js")
|
||||
// if r.OK { app := r.Value.(*App) }
|
||||
func Find(filename, name string) Result {
|
||||
func (a App) Find(filename, name string) Result {
|
||||
path, err := exec.LookPath(filename)
|
||||
if err != nil {
|
||||
return Result{err, false}
|
||||
|
|
|
|||
41
app_test.go
41
app_test.go
|
|
@ -7,14 +7,41 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// --- App ---
|
||||
// --- App.New ---
|
||||
|
||||
func TestApp_Good(t *testing.T) {
|
||||
c := New(WithOptions(NewOptions(Option{Key: "name", Value: "myapp"}))).Value.(*Core)
|
||||
func TestApp_New_Good(t *testing.T) {
|
||||
app := App{}.New(NewOptions(
|
||||
Option{Key: "name", Value: "myapp"},
|
||||
Option{Key: "version", Value: "1.0.0"},
|
||||
Option{Key: "description", Value: "test app"},
|
||||
))
|
||||
assert.Equal(t, "myapp", app.Name)
|
||||
assert.Equal(t, "1.0.0", app.Version)
|
||||
assert.Equal(t, "test app", app.Description)
|
||||
}
|
||||
|
||||
func TestApp_New_Empty_Good(t *testing.T) {
|
||||
app := App{}.New(NewOptions())
|
||||
assert.Equal(t, "", app.Name)
|
||||
assert.Equal(t, "", app.Version)
|
||||
}
|
||||
|
||||
func TestApp_New_Partial_Good(t *testing.T) {
|
||||
app := App{}.New(NewOptions(
|
||||
Option{Key: "name", Value: "myapp"},
|
||||
))
|
||||
assert.Equal(t, "myapp", app.Name)
|
||||
assert.Equal(t, "", app.Version)
|
||||
}
|
||||
|
||||
// --- App via Core ---
|
||||
|
||||
func TestApp_Core_Good(t *testing.T) {
|
||||
c := New(WithOption("name", "myapp")).Value.(*Core)
|
||||
assert.Equal(t, "myapp", c.App().Name)
|
||||
}
|
||||
|
||||
func TestApp_Empty_Good(t *testing.T) {
|
||||
func TestApp_Core_Empty_Good(t *testing.T) {
|
||||
c := New().Value.(*Core)
|
||||
assert.NotNil(t, c.App())
|
||||
assert.Equal(t, "", c.App().Name)
|
||||
|
|
@ -26,14 +53,16 @@ func TestApp_Runtime_Good(t *testing.T) {
|
|||
assert.NotNil(t, c.App().Runtime)
|
||||
}
|
||||
|
||||
// --- App.Find ---
|
||||
|
||||
func TestApp_Find_Good(t *testing.T) {
|
||||
r := Find("go", "go")
|
||||
r := App{}.Find("go", "go")
|
||||
assert.True(t, r.OK)
|
||||
app := r.Value.(*App)
|
||||
assert.NotEmpty(t, app.Path)
|
||||
}
|
||||
|
||||
func TestApp_Find_Bad(t *testing.T) {
|
||||
r := Find("nonexistent-binary-xyz", "test")
|
||||
r := App{}.Find("nonexistent-binary-xyz", "test")
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue