Skip to content

Commit

Permalink
initial commit, migrating from gravestench/runtime-examples, updating…
Browse files Browse the repository at this point in the history
… to use the new implementation gravestench/servicemesh
  • Loading branch information
gravestench committed Dec 16, 2023
0 parents commit c716312
Show file tree
Hide file tree
Showing 94 changed files with 4,254 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea
go.sum
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## About

go see https://github.com/gravestench/runtime

There are example runtime services in the `services` directory. Some of
these services depend on each other, namely the config file manager.

There are example build targets in the `cmd` directory. There may be one-off
service declarations inside of these directories.
13 changes: 13 additions & 0 deletions cmd/bare_minimum/example_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package main

type example struct {
name string
}

func (e *example) Init(r servicemesh.R) {
return
}

func (e *example) Name() string {
return e.name
}
9 changes: 9 additions & 0 deletions cmd/bare_minimum/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package main

func main() {
rt := servicemesh.New("my runtime")

rt.Add(&example{name: "foo"})

rt.Run()
}
105 changes: 105 additions & 0 deletions cmd/charm/simple_tui/bubbletea_wrapper_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package main

import (
"sync"
"time"

tea "github.com/charmbracelet/bubbletea"
"github.com/gravestench/runtime/pkg/events"
)

type serviceWithModel interface {
Model() tea.Model
}

type bubbleteaService struct {
*tea.Program
mux sync.Mutex
}

func (b *bubbleteaService) Init(mesh servicemesh.M) {
go b.runLoop()

b.bindExisting(rt)

rt.Events().On(events.EventServiceAdded, func(...any) {
b.bindExisting(rt)
})
}

func (b *bubbleteaService) runLoop() {
for {
time.Sleep(time.Second)

if b.Program == nil {
continue
}

b.Program.Run()
}
}

func (b *bubbleteaService) bindExisting(mesh servicemesh.M) {
var models []tea.Model

for _, service := range rt.Services() {
if candidate, ok := service.(serviceWithModel); ok {
models = append(models, candidate.Model())
}
}

if len(models) < 1 {
return
}

if b.Program != nil {
b.Program.Kill()
}

b.Program = tea.NewProgram(b.newMainModel(models))
}

func (b *bubbleteaService) newMainModel(models []tea.Model) tea.Model {
return &mainModel{models: models}
}

func (b *bubbleteaService) Name() string {
return "Bubbletea"
}

type mainModel struct {
models []tea.Model
currentModelIndex int
}

func (m mainModel) Init() (cmd tea.Cmd) {
for _, model := range m.models {
cmd = model.Init()
}

return cmd
}

func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if len(m.models) < 1 {
return m, nil
}

if m.currentModelIndex < 0 || m.currentModelIndex >= len(m.models) {
m.currentModelIndex = 0
}

return m.models[m.currentModelIndex].Update(msg)
}

func (m mainModel) View() string {
if len(m.models) < 1 {
return ""
}

if m.currentModelIndex < 0 || m.currentModelIndex >= len(m.models) {
m.currentModelIndex = 0
}

return m.models[m.currentModelIndex].View()
}
99 changes: 99 additions & 0 deletions cmd/charm/simple_tui/filepicker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package main

import (
"errors"
"os"
"path/filepath"
"strings"
"time"

"github.com/charmbracelet/bubbles/filepicker"
tea "github.com/charmbracelet/bubbletea"
)

type filePickerService struct {
model *filePickerModel
}

func (t *filePickerService) Init(mesh servicemesh.M) {
t.model = &filePickerModel{}
}

func (t *filePickerService) Name() string {
return "Filepicker TUI"
}

func (t *filePickerService) Model() tea.Model {
return t.model
}

type filePickerModel struct {
filepicker filepicker.Model
selectedFile string
quitting bool
err error
}

type clearErrorMsg struct{}

func clearErrorAfter(t time.Duration) tea.Cmd {
return tea.Tick(t, func(_ time.Time) tea.Msg {
return clearErrorMsg{}
})
}

func (m filePickerModel) Init() tea.Cmd {
m.filepicker.CurrentDirectory, _ = os.UserHomeDir()
m.filepicker.CurrentDirectory = filepath.Join(m.filepicker.CurrentDirectory, "Downloads")
return m.filepicker.Init()
}

func (m filePickerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c", "q":
m.quitting = true
return m, tea.Quit
}
case clearErrorMsg:
m.err = nil
}

var cmd tea.Cmd
m.filepicker, cmd = m.filepicker.Update(msg)

// Did the user select a file?
if didSelect, path := m.filepicker.DidSelectFile(msg); didSelect {
// Get the path of the selected file.
m.selectedFile = path
}

// Did the user select a disabled file?
// This is only necessary to display an error to the user.
if didSelect, path := m.filepicker.DidSelectDisabledFile(msg); didSelect {
// Let's clear the selectedFile and display an error.
m.err = errors.New(path + " is not valid.")
m.selectedFile = ""
return m, tea.Batch(cmd, clearErrorAfter(2*time.Second))
}

return m, cmd
}

func (m filePickerModel) View() string {
if m.quitting {
return ""
}
var s strings.Builder
s.WriteString("\n ")
if m.err != nil {
s.WriteString(m.filepicker.Styles.DisabledFile.Render(m.err.Error()))
} else if m.selectedFile == "" {
s.WriteString("Pick a file:")
} else {
s.WriteString("Selected file: " + m.filepicker.Styles.Selected.Render(m.selectedFile))
}
s.WriteString("\n\n" + m.filepicker.View() + "\n")
return s.String()
}
21 changes: 21 additions & 0 deletions cmd/charm/simple_tui/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package main

import (
"os"
)

func main() {
rt := servicemesh.New()

logFile, err := os.OpenFile("/tmp/log.out", os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
panic(err)
}

rt.SetLogDestination(logFile)

rt.Add(&bubbleteaService{})
rt.Add(&filePickerService{})

rt.Run()
}
17 changes: 17 additions & 0 deletions cmd/config_file/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

import (
"github.com/gravestench/servicesmesh-examples/services/config_file"
)

func main() {
rt := servicemesh.New()
cfgManager := &config_file.Service{}

rt.Add(cfgManager)

// This service has a dependency on the config manager
rt.Add(&serviceThatUsesConfigManager{})

rt.Run()
}
55 changes: 55 additions & 0 deletions cmd/config_file/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package main

import (
"github.com/gravestench/servicesmesh-examples/services/config_file"
)

var _ interface {
servicemesh.S
servicemesh.HasLogger
servicemesh.HasDependencies
} = &serviceThatUsesConfigManager{}

type serviceThatUsesConfigManager struct {
configManager config_file.Manager // dependency on config file manager
log *slog.Logger
}

func (s *serviceThatUsesConfigManager) ResolveDependencies(mesh servicemesh.M) {
for _, service := range rt.Services() {
if instance, ok := service.(config_file.Manager); ok {
s.configManager = instance
}
}
}

func (s *serviceThatUsesConfigManager) DependenciesResolved() bool {
return s.configManager != nil
}

func (s *serviceThatUsesConfigManager) Init(r servicemesh.R) {
cfg, err := s.configManager.GetConfig("test.json")
if err != nil {
s.log.Fatal().Msgf("couldnt load example config file", "error", err)
}

group := cfg.Group("foo")

group.Set("a", 1)
group.Set("b", 2)
group.Set("c", 3)

s.configManager.SaveConfig("test.json")
}

func (s *serviceThatUsesConfigManager) Name() string {
return "Config User"
}

func (s *serviceThatUsesConfigManager) SetLogger(logger *slog.Logger) {
s.log = logger
}

func (s *serviceThatUsesConfigManager) Logger() *slog.Logger {
return s.log
}
16 changes: 16 additions & 0 deletions cmd/dependency_injection/non_service_dependencies/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package main

func main() {
rt := servicemesh.New()

// each service has a dependency that is not
// actually resolved through the runtime but by
// some other means (that part is up to you).
rt.Add(newServiceWithAsyncDependencyResolution())
rt.Add(newServiceWithAsyncDependencyResolution())
rt.Add(newServiceWithAsyncDependencyResolution())
rt.Add(newServiceWithAsyncDependencyResolution())
rt.Add(newServiceWithAsyncDependencyResolution())

rt.Run()
}
Loading

0 comments on commit c716312

Please sign in to comment.