140 lines
3.3 KiB
Go
140 lines
3.3 KiB
Go
package workspace
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/samber/mo"
|
|
)
|
|
|
|
type Workspace struct {
|
|
HostPath string `json:"host_path"`
|
|
Hash string `json:"hash"`
|
|
MountTag string `json:"mount_tag"`
|
|
GuestPath string `json:"guest_path"`
|
|
}
|
|
|
|
type Registry struct {
|
|
filePath string
|
|
workspaces map[string]Workspace
|
|
}
|
|
|
|
// Hash generates an 8-character hash from a path, matching bash behavior:
|
|
// echo -n "$path" | sha256sum | cut -c1-8
|
|
func Hash(path string) string {
|
|
h := sha256.Sum256([]byte(path))
|
|
return fmt.Sprintf("%x", h)[:8]
|
|
}
|
|
|
|
// NewRegistry creates a new empty registry
|
|
func NewRegistry(filePath string) *Registry {
|
|
return &Registry{
|
|
filePath: filePath,
|
|
workspaces: make(map[string]Workspace),
|
|
}
|
|
}
|
|
|
|
// Load reads the registry from a JSON file
|
|
func Load(filePath string) mo.Result[*Registry] {
|
|
registry := NewRegistry(filePath)
|
|
|
|
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
|
return mo.Ok(registry)
|
|
}
|
|
|
|
data, err := os.ReadFile(filePath)
|
|
if err != nil {
|
|
return mo.Err[*Registry](fmt.Errorf("failed to read workspaces file: %w", err))
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
return mo.Ok(registry)
|
|
}
|
|
|
|
var workspaceList []Workspace
|
|
if err := json.Unmarshal(data, &workspaceList); err != nil {
|
|
return mo.Err[*Registry](fmt.Errorf("failed to parse workspaces JSON: %w", err))
|
|
}
|
|
|
|
for _, ws := range workspaceList {
|
|
registry.workspaces[ws.HostPath] = ws
|
|
}
|
|
|
|
return mo.Ok(registry)
|
|
}
|
|
|
|
// Save writes the registry to the JSON file
|
|
func (r *Registry) Save() mo.Result[struct{}] {
|
|
dir := filepath.Dir(r.filePath)
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
return mo.Err[struct{}](fmt.Errorf("failed to create directory: %w", err))
|
|
}
|
|
|
|
workspaceList := make([]Workspace, 0, len(r.workspaces))
|
|
for _, ws := range r.workspaces {
|
|
workspaceList = append(workspaceList, ws)
|
|
}
|
|
|
|
data, err := json.MarshalIndent(workspaceList, "", " ")
|
|
if err != nil {
|
|
return mo.Err[struct{}](fmt.Errorf("failed to marshal JSON: %w", err))
|
|
}
|
|
|
|
if err := os.WriteFile(r.filePath, data, 0644); err != nil {
|
|
return mo.Err[struct{}](fmt.Errorf("failed to write workspaces file: %w", err))
|
|
}
|
|
|
|
return mo.Ok(struct{}{})
|
|
}
|
|
|
|
// Register adds a workspace to the registry if it doesn't already exist
|
|
func (r *Registry) Register(hostPath string) mo.Result[*Workspace] {
|
|
absPath, err := filepath.Abs(hostPath)
|
|
if err != nil {
|
|
return mo.Err[*Workspace](fmt.Errorf("failed to resolve absolute path: %w", err))
|
|
}
|
|
|
|
if existing, exists := r.workspaces[absPath]; exists {
|
|
return mo.Ok(&existing)
|
|
}
|
|
|
|
hash := Hash(absPath)
|
|
dirName := filepath.Base(absPath)
|
|
ws := Workspace{
|
|
HostPath: absPath,
|
|
Hash: hash,
|
|
MountTag: fmt.Sprintf("ws_%s", hash),
|
|
GuestPath: fmt.Sprintf("/workspace/%s-%s", hash, dirName),
|
|
}
|
|
|
|
r.workspaces[absPath] = ws
|
|
return mo.Ok(&ws)
|
|
}
|
|
|
|
// List returns all registered workspaces
|
|
func (r *Registry) List() []Workspace {
|
|
result := make([]Workspace, 0, len(r.workspaces))
|
|
for _, ws := range r.workspaces {
|
|
result = append(result, ws)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Find looks up a workspace by host path
|
|
func (r *Registry) Find(hostPath string) mo.Option[Workspace] {
|
|
absPath, err := filepath.Abs(hostPath)
|
|
if err != nil {
|
|
if ws, exists := r.workspaces[hostPath]; exists {
|
|
return mo.Some(ws)
|
|
}
|
|
return mo.None[Workspace]()
|
|
}
|
|
|
|
if ws, exists := r.workspaces[absPath]; exists {
|
|
return mo.Some(ws)
|
|
}
|
|
return mo.None[Workspace]()
|
|
}
|