clone

an offline map explorer where each world, from a game map to a city's open data, ships as one .atlas file

feat/atlas-next-horizon
atlas / internal / app / hostenv / oshost / oshost.go
oshost.go 2.8K
1// Package oshost is the hostenv contract implemented against an operating2// system: a library that is a directory of .atlas files, session records that3// are files, and a file picker the host either has or does not.4//5// It is separate from hostenv proper so that the interfaces stay portable.6// The handler imports hostenv; only a host that runs on a machine with a7// filesystem imports this. A WASM service worker host would sit beside it8// with OPFS-backed stores and the same handler above it (issue #5 ยง3.3).9package oshost1011import (12 "context"13 "fmt"14 "io"1516 "github.com/FelineStateMachine/atlas/internal/app/hostenv"17)1819// Host is an OS-backed hostenv.20type Host struct {21 volumes *Volumes22 sessions hostenv.SessionStore23 picker Picker24}2526// Picker puts a file picker in front of the reader. A host with a window27// supplies one -- the Wails host's native dialog is exactly this function --28// and a headless host supplies nothing, which answers hostenv.ErrNotAvailable.29type Picker func(ctx context.Context) (io.ReadCloser, string, error)3031// Options is what an OS host needs to know: where the library is, where32// session records go, and whether there is anyone to show a dialog to.33type Options struct {34 // BundlesDir is the library of .atlas files. It need not exist yet.35 BundlesDir string3637 // SessionsDir is where session records are written. Empty means the38 // records live in memory for the length of the process, which is what a39 // dev server pointed at somebody else's library wants: it reads the40 // library and writes nothing near it.41 SessionsDir string4243 // Pick is the host's file picker, or nil for a host with no window.44 Pick Picker45}4647// New opens a host over the given directories, scanning the library once.48func New(opts Options) (*Host, error) {49 volumes, err := NewVolumes(opts.BundlesDir)50 if err != nil {51 return nil, err52 }53 var sessions hostenv.SessionStore = hostenv.NewMemorySessions()54 if opts.SessionsDir != "" {55 sessions, err = NewSessions(opts.SessionsDir)56 if err != nil {57 return nil, err58 }59 }60 return &Host{volumes: volumes, sessions: sessions, picker: opts.Pick}, nil61}6263// Volumes is the library.64func (h *Host) Volumes() hostenv.VolumeStore { return h.volumes }6566// Sessions is where session records are kept.67func (h *Host) Sessions() hostenv.SessionStore { return h.sessions }6869// PickFile asks the host's picker for a file. A host without one refuses70// rather than pretending the reader cancelled: an import that cannot happen71// and an import nobody wanted are different answers, and the application72// tells the reader which it got.73func (h *Host) PickFile(ctx context.Context) (io.ReadCloser, string, error) {74 if h.picker == nil {75 return nil, "", fmt.Errorf("choosing a bundle: %w", hostenv.ErrNotAvailable)76 }77 return h.picker(ctx)78}