From d8fb18a66390c9e0b84610bb43bf5509a634b321 Mon Sep 17 00:00:00 2001 From: Snider Date: Tue, 24 Mar 2026 19:18:59 +0000 Subject: [PATCH] feat: App struct with New(Options) + Find() as method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- app.go | 52 +++++++++++++++++++++++++++++++++------------------- app_test.go | 41 +++++++++++++++++++++++++++++++++++------ 2 files changed, 68 insertions(+), 25 deletions(-) diff --git a/app.go b/app.go index 3a5aa02..17c3214 100644 --- a/app.go +++ b/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} diff --git a/app_test.go b/app_test.go index 2460fab..c3b5e44 100644 --- a/app_test.go +++ b/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) }