chore: clean up — remove core.go re-export, pkg/mnt, go-io/go-log deps

Removed:
- core.go (top-level re-export layer, no longer needed)
- pkg/mnt/ (absorbed into pkg/core/mnt.go)
- pkg/log/ (absorbed into pkg/core/log.go)
- go-io dependency (absorbed into pkg/core/io.go)
- go-log dependency (absorbed into pkg/core/error.go + log.go)

Remaining: single package pkg/core/ with 14 source files.
Only dependency: testify (test-only).
Production code: zero external dependencies.

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-03-18 01:28:14 +00:00
parent 16a985ad5c
commit 8f2e3d9457
11 changed files with 10 additions and 508 deletions

108
core.go
View file

@ -1,108 +0,0 @@
// SPDX-License-Identifier: EUPL-1.2
// Package core is the Core framework for Go.
//
// Single import, single struct, everything accessible:
//
// import core "forge.lthn.ai/core/go"
//
// c, _ := core.New(
// core.WithAssets(myEmbed),
// core.WithService(myFactory),
// )
//
// // DI
// svc, _ := core.ServiceFor[*MyService](c, "name")
//
// // Mount
// content, _ := c.Mnt().ReadString("persona/secops/developer.md")
// c.Mnt().Extract(targetDir, data)
//
// // IPC
// c.ACTION(msg)
package core
import (
di "forge.lthn.ai/core/go/pkg/core"
)
// --- Types ---
// Core is the central application container.
type Core = di.Core
// Option configures a Core instance.
type Option = di.Option
// Message is the IPC message type.
type Message = di.Message
// Sub is a scoped view of an embedded filesystem.
type Sub = di.Sub
// ExtractOptions configures template extraction.
type ExtractOptions = di.ExtractOptions
// Startable is implemented by services with startup logic.
type Startable = di.Startable
// Stoppable is implemented by services with shutdown logic.
type Stoppable = di.Stoppable
// LocaleProvider provides locale filesystems for i18n.
type LocaleProvider = di.LocaleProvider
// ServiceRuntime is the base for services with typed options.
type ServiceRuntime[T any] = di.ServiceRuntime[T]
// --- Constructor + Options ---
// New creates a new Core instance.
var New = di.New
// WithService registers a service factory.
var WithService = di.WithService
// WithName registers a named service factory.
var WithName = di.WithName
// WithAssets mounts an embedded filesystem.
var WithAssets = di.WithAssets
// WithMount mounts an embedded filesystem at a subdirectory.
var WithMount = di.WithMount
// WithServiceLock prevents late service registration.
var WithServiceLock = di.WithServiceLock
// WithApp sets the GUI runtime.
var WithApp = di.WithApp
// Mount creates a scoped view of an embed.FS at basedir.
var Mount = di.Mount
// --- Generic Functions ---
// ServiceFor retrieves a typed service by name.
func ServiceFor[T any](c *Core, name string) (T, error) {
return di.ServiceFor[T](c, name)
}
// E creates a structured error.
var E = di.E
// --- Configuration (core.Etc) ---
// Etc is the configuration and feature flags store.
type Etc = di.Etc
// NewEtc creates a standalone configuration store.
var NewEtc = di.NewEtc
// Var is a typed optional variable (set/unset/get).
type Var[T any] = di.Var[T]
// NewVar creates a Var with the given value.
func NewVar[T any](val T) Var[T] {
return di.NewVar(val)
}

9
go.mod
View file

@ -2,14 +2,13 @@ module forge.lthn.ai/core/go
go 1.26.0
require (
forge.lthn.ai/core/go-io v0.1.6
forge.lthn.ai/core/go-log v0.0.4
github.com/stretchr/testify v1.11.1
)
require github.com/stretchr/testify v1.11.1
require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

10
go.sum
View file

@ -1,15 +1,17 @@
forge.lthn.ai/core/go-io v0.1.6 h1:RByYeP829HFqR2yLg5iBM5dGHKzPFYc+udl/Y1DZIRs=
forge.lthn.ai/core/go-io v0.1.6/go.mod h1:3MSuQZuzhCi6aefECQ/LxhM8ooVLam1KgEvgeEjYZVc=
forge.lthn.ai/core/go-log v0.0.4 h1:KTuCEPgFmuM8KJfnyQ8vPOU1Jg654W74h8IJvfQMfv0=
forge.lthn.ai/core/go-log v0.0.4/go.mod h1:r14MXKOD3LF/sI8XUJQhRk/SZHBE7jAFVuCfgkXoZPw=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=

View file

@ -1,194 +0,0 @@
// SPDX-License-Identifier: EUPL-1.2
package mnt
import (
"bytes"
"io"
"io/fs"
"os"
"path/filepath"
"strings"
"text/template"
)
// ExtractOptions configures template extraction.
type ExtractOptions struct {
// TemplateFilters identifies template files by substring match.
// Default: [".tmpl"]
TemplateFilters []string
// IgnoreFiles is a set of filenames to skip during extraction.
IgnoreFiles map[string]struct{}
// RenameFiles maps original filenames to new names.
RenameFiles map[string]string
}
// Extract copies a template directory from an fs.FS to targetDir,
// processing Go text/template in filenames and file contents.
//
// Files containing a template filter substring (default: ".tmpl") have
// their contents processed through text/template with the given data.
// The filter is stripped from the output filename.
//
// Directory and file names can contain Go template expressions:
// {{.Name}}/main.go → myproject/main.go
//
// Data can be any struct or map[string]string for template substitution.
func Extract(fsys fs.FS, targetDir string, data any, opts ...ExtractOptions) error {
opt := ExtractOptions{
TemplateFilters: []string{".tmpl"},
IgnoreFiles: make(map[string]struct{}),
RenameFiles: make(map[string]string),
}
if len(opts) > 0 {
if len(opts[0].TemplateFilters) > 0 {
opt.TemplateFilters = opts[0].TemplateFilters
}
if opts[0].IgnoreFiles != nil {
opt.IgnoreFiles = opts[0].IgnoreFiles
}
if opts[0].RenameFiles != nil {
opt.RenameFiles = opts[0].RenameFiles
}
}
// Ensure target directory exists
targetDir, err := filepath.Abs(targetDir)
if err != nil {
return err
}
if err := os.MkdirAll(targetDir, 0755); err != nil {
return err
}
// Categorise files
var dirs []string
var templateFiles []string
var standardFiles []string
err = fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if path == "." {
return nil
}
if d.IsDir() {
dirs = append(dirs, path)
return nil
}
filename := filepath.Base(path)
if _, ignored := opt.IgnoreFiles[filename]; ignored {
return nil
}
if isTemplate(filename, opt.TemplateFilters) {
templateFiles = append(templateFiles, path)
} else {
standardFiles = append(standardFiles, path)
}
return nil
})
if err != nil {
return err
}
// Create directories (names may contain templates)
for _, dir := range dirs {
target := renderPath(filepath.Join(targetDir, dir), data)
if err := os.MkdirAll(target, 0755); err != nil {
return err
}
}
// Process template files
for _, path := range templateFiles {
tmpl, err := template.ParseFS(fsys, path)
if err != nil {
return err
}
targetFile := renderPath(filepath.Join(targetDir, path), data)
// Strip template filters from filename
dir := filepath.Dir(targetFile)
name := filepath.Base(targetFile)
for _, filter := range opt.TemplateFilters {
name = strings.ReplaceAll(name, filter, "")
}
if renamed := opt.RenameFiles[name]; renamed != "" {
name = renamed
}
targetFile = filepath.Join(dir, name)
f, err := os.Create(targetFile)
if err != nil {
return err
}
if err := tmpl.Execute(f, data); err != nil {
f.Close()
return err
}
f.Close()
}
// Copy standard files
for _, path := range standardFiles {
name := filepath.Base(path)
if renamed := opt.RenameFiles[name]; renamed != "" {
path = filepath.Join(filepath.Dir(path), renamed)
}
target := renderPath(filepath.Join(targetDir, path), data)
if err := copyFile(fsys, path, target); err != nil {
return err
}
}
return nil
}
func isTemplate(filename string, filters []string) bool {
for _, f := range filters {
if strings.Contains(filename, f) {
return true
}
}
return false
}
func renderPath(path string, data any) string {
if data == nil {
return path
}
tmpl, err := template.New("path").Parse(path)
if err != nil {
return path
}
var buf bytes.Buffer
if err := tmpl.Execute(&buf, data); err != nil {
return path
}
return buf.String()
}
func copyFile(fsys fs.FS, source, target string) error {
s, err := fsys.Open(source)
if err != nil {
return err
}
defer s.Close()
if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
return err
}
d, err := os.Create(target)
if err != nil {
return err
}
defer d.Close()
_, err = io.Copy(d, s)
return err
}

View file

@ -1,86 +0,0 @@
// SPDX-License-Identifier: EUPL-1.2
// Package mnt provides mount operations for the Core framework.
//
// Mount operations attach data to/from binaries and watch live filesystems:
//
// - FS: mount an embed.FS subdirectory for scoped access
// - Extract: extract a template directory with variable substitution
// - Watch: observe filesystem changes (file watcher)
//
// Zero external dependencies. All operations use stdlib only.
//
// Usage:
//
// sub, _ := mnt.FS(myEmbed, "lib/persona")
// content, _ := sub.ReadFile("secops/developer.md")
//
// mnt.Extract(sub, "/tmp/workspace", map[string]string{"Name": "myproject"})
package mnt
import (
"embed"
"io/fs"
"path/filepath"
)
// Sub wraps an embed.FS with a basedir for scoped access.
// All paths are relative to basedir.
type Sub struct {
basedir string
fs embed.FS
}
// FS creates a scoped view of an embed.FS anchored at basedir.
// Returns error if basedir doesn't exist in the embedded filesystem.
func FS(efs embed.FS, basedir string) (*Sub, error) {
s := &Sub{fs: efs, basedir: basedir}
// Verify the basedir exists
if _, err := s.ReadDir("."); err != nil {
return nil, err
}
return s, nil
}
func (s *Sub) path(name string) string {
return filepath.ToSlash(filepath.Join(s.basedir, name))
}
// Open opens the named file for reading.
func (s *Sub) Open(name string) (fs.File, error) {
return s.fs.Open(s.path(name))
}
// ReadDir reads the named directory.
func (s *Sub) ReadDir(name string) ([]fs.DirEntry, error) {
return s.fs.ReadDir(s.path(name))
}
// ReadFile reads the named file.
func (s *Sub) ReadFile(name string) ([]byte, error) {
return s.fs.ReadFile(s.path(name))
}
// ReadString reads the named file as a string.
func (s *Sub) ReadString(name string) (string, error) {
data, err := s.ReadFile(name)
if err != nil {
return "", err
}
return string(data), nil
}
// Sub returns a new Sub anchored at a subdirectory within this Sub.
func (s *Sub) Sub(subDir string) (*Sub, error) {
return FS(s.fs, s.path(subDir))
}
// Embed returns the underlying embed.FS.
func (s *Sub) Embed() embed.FS {
return s.fs
}
// BaseDir returns the basedir this Sub is anchored at.
func (s *Sub) BaseDir() string {
return s.basedir
}

View file

@ -1,106 +0,0 @@
// SPDX-License-Identifier: EUPL-1.2
package mnt_test
import (
"embed"
"os"
"testing"
"forge.lthn.ai/core/go/pkg/mnt"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
//go:embed testdata
var testFS embed.FS
func TestFS_Good(t *testing.T) {
sub, err := mnt.FS(testFS, "testdata")
require.NoError(t, err)
assert.Equal(t, "testdata", sub.BaseDir())
}
func TestFS_Bad_InvalidDir(t *testing.T) {
_, err := mnt.FS(testFS, "nonexistent")
assert.Error(t, err)
}
func TestSub_ReadFile_Good(t *testing.T) {
sub, err := mnt.FS(testFS, "testdata")
require.NoError(t, err)
data, err := sub.ReadFile("hello.txt")
require.NoError(t, err)
assert.Equal(t, "hello world\n", string(data))
}
func TestSub_ReadString_Good(t *testing.T) {
sub, err := mnt.FS(testFS, "testdata")
require.NoError(t, err)
content, err := sub.ReadString("hello.txt")
require.NoError(t, err)
assert.Equal(t, "hello world\n", content)
}
func TestSub_ReadDir_Good(t *testing.T) {
sub, err := mnt.FS(testFS, "testdata")
require.NoError(t, err)
entries, err := sub.ReadDir(".")
require.NoError(t, err)
assert.True(t, len(entries) >= 1)
}
func TestSub_Sub_Good(t *testing.T) {
sub, err := mnt.FS(testFS, "testdata")
require.NoError(t, err)
nested, err := sub.Sub("subdir")
require.NoError(t, err)
content, err := nested.ReadString("nested.txt")
require.NoError(t, err)
assert.Equal(t, "nested content\n", content)
}
func TestExtract_Good(t *testing.T) {
sub, err := mnt.FS(testFS, "testdata/template")
require.NoError(t, err)
targetDir := t.TempDir()
err = mnt.Extract(sub, targetDir, map[string]string{
"Name": "myproject",
"Module": "forge.lthn.ai/core/myproject",
})
require.NoError(t, err)
// Check template was processed
readme, err := os.ReadFile(targetDir + "/README.md")
require.NoError(t, err)
assert.Contains(t, string(readme), "myproject project")
// Check go.mod template was processed
gomod, err := os.ReadFile(targetDir + "/go.mod")
require.NoError(t, err)
assert.Contains(t, string(gomod), "module forge.lthn.ai/core/myproject")
// Check non-template was copied as-is
main, err := os.ReadFile(targetDir + "/main.go")
require.NoError(t, err)
assert.Equal(t, "package main\n", string(main))
}
func TestExtract_Good_NoData(t *testing.T) {
sub, err := mnt.FS(testFS, "testdata")
require.NoError(t, err)
targetDir := t.TempDir()
err = mnt.Extract(sub, targetDir, nil)
require.NoError(t, err)
data, err := os.ReadFile(targetDir + "/hello.txt")
require.NoError(t, err)
assert.Equal(t, "hello world\n", string(data))
}

View file

@ -1 +0,0 @@
hello world

View file

@ -1 +0,0 @@
nested content

View file

@ -1 +0,0 @@
{{.Name}} project

View file

@ -1 +0,0 @@
module {{.Module}}

View file

@ -1 +0,0 @@
package main