refactor(ax): add memory medium aliases
This commit is contained in:
parent
c0ee58201b
commit
25b12a22a4
7 changed files with 93 additions and 87 deletions
|
|
@ -4,31 +4,31 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkMockMedium_Write(b *testing.B) {
|
||||
m := NewMockMedium()
|
||||
func BenchmarkMemoryMedium_Write(b *testing.B) {
|
||||
medium := NewMemoryMedium()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = m.Write("test.txt", "some content")
|
||||
_ = medium.Write("test.txt", "some content")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMockMedium_Read(b *testing.B) {
|
||||
m := NewMockMedium()
|
||||
_ = m.Write("test.txt", "some content")
|
||||
func BenchmarkMemoryMedium_Read(b *testing.B) {
|
||||
medium := NewMemoryMedium()
|
||||
_ = medium.Write("test.txt", "some content")
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = m.Read("test.txt")
|
||||
_, _ = medium.Read("test.txt")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMockMedium_List(b *testing.B) {
|
||||
m := NewMockMedium()
|
||||
_ = m.EnsureDir("dir")
|
||||
func BenchmarkMemoryMedium_List(b *testing.B) {
|
||||
medium := NewMemoryMedium()
|
||||
_ = medium.EnsureDir("dir")
|
||||
for i := 0; i < 100; i++ {
|
||||
_ = m.Write("dir/file"+string(rune(i))+".txt", "content")
|
||||
_ = medium.Write("dir/file"+string(rune(i))+".txt", "content")
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = m.List("dir")
|
||||
_, _ = medium.List("dir")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,15 +9,15 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// --- MockMedium Tests ---
|
||||
// --- MemoryMedium Compatibility Tests ---
|
||||
|
||||
func TestClient_NewMockMedium_Good(t *testing.T) {
|
||||
m := NewMockMedium()
|
||||
assert.NotNil(t, m)
|
||||
assert.NotNil(t, m.Files)
|
||||
assert.NotNil(t, m.Dirs)
|
||||
assert.Empty(t, m.Files)
|
||||
assert.Empty(t, m.Dirs)
|
||||
func TestClient_NewMemoryMedium_Good(t *testing.T) {
|
||||
medium := NewMemoryMedium()
|
||||
assert.NotNil(t, medium)
|
||||
assert.NotNil(t, medium.Files)
|
||||
assert.NotNil(t, medium.Dirs)
|
||||
assert.Empty(t, medium.Files)
|
||||
assert.Empty(t, medium.Dirs)
|
||||
}
|
||||
|
||||
func TestClient_MockMedium_Read_Good(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ var (
|
|||
type Medium struct {
|
||||
dataNode *borgdatanode.DataNode
|
||||
directorySet map[string]bool // explicit directories that exist without file contents
|
||||
mu sync.RWMutex
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func New() *Medium {
|
||||
|
|
@ -63,8 +63,8 @@ func FromTar(data []byte) (*Medium, error) {
|
|||
|
||||
// Example: snapshot, _ := medium.Snapshot()
|
||||
func (medium *Medium) Snapshot() ([]byte, error) {
|
||||
medium.mu.RLock()
|
||||
defer medium.mu.RUnlock()
|
||||
medium.lock.RLock()
|
||||
defer medium.lock.RUnlock()
|
||||
data, err := medium.dataNode.ToTar()
|
||||
if err != nil {
|
||||
return nil, core.E("datanode.Snapshot", "tar failed", err)
|
||||
|
|
@ -78,8 +78,8 @@ func (medium *Medium) Restore(data []byte) error {
|
|||
if err != nil {
|
||||
return core.E("datanode.Restore", "tar failed", err)
|
||||
}
|
||||
medium.mu.Lock()
|
||||
defer medium.mu.Unlock()
|
||||
medium.lock.Lock()
|
||||
defer medium.lock.Unlock()
|
||||
medium.dataNode = dataNode
|
||||
medium.directorySet = make(map[string]bool)
|
||||
return nil
|
||||
|
|
@ -87,8 +87,8 @@ func (medium *Medium) Restore(data []byte) error {
|
|||
|
||||
// Example: dataNode := medium.DataNode()
|
||||
func (medium *Medium) DataNode() *borgdatanode.DataNode {
|
||||
medium.mu.RLock()
|
||||
defer medium.mu.RUnlock()
|
||||
medium.lock.RLock()
|
||||
defer medium.lock.RUnlock()
|
||||
return medium.dataNode
|
||||
}
|
||||
|
||||
|
|
@ -105,8 +105,8 @@ func normaliseEntryPath(filePath string) string {
|
|||
// --- io.Medium interface ---
|
||||
|
||||
func (medium *Medium) Read(filePath string) (string, error) {
|
||||
medium.mu.RLock()
|
||||
defer medium.mu.RUnlock()
|
||||
medium.lock.RLock()
|
||||
defer medium.lock.RUnlock()
|
||||
|
||||
filePath = normaliseEntryPath(filePath)
|
||||
file, err := medium.dataNode.Open(filePath)
|
||||
|
|
@ -131,8 +131,8 @@ func (medium *Medium) Read(filePath string) (string, error) {
|
|||
}
|
||||
|
||||
func (medium *Medium) Write(filePath, content string) error {
|
||||
medium.mu.Lock()
|
||||
defer medium.mu.Unlock()
|
||||
medium.lock.Lock()
|
||||
defer medium.lock.Unlock()
|
||||
|
||||
filePath = normaliseEntryPath(filePath)
|
||||
if filePath == "" {
|
||||
|
|
@ -150,8 +150,8 @@ func (medium *Medium) WriteMode(filePath, content string, mode fs.FileMode) erro
|
|||
}
|
||||
|
||||
func (medium *Medium) EnsureDir(filePath string) error {
|
||||
medium.mu.Lock()
|
||||
defer medium.mu.Unlock()
|
||||
medium.lock.Lock()
|
||||
defer medium.lock.Unlock()
|
||||
|
||||
filePath = normaliseEntryPath(filePath)
|
||||
if filePath == "" {
|
||||
|
|
@ -162,7 +162,7 @@ func (medium *Medium) EnsureDir(filePath string) error {
|
|||
}
|
||||
|
||||
// ensureDirsLocked marks a directory and all ancestors as existing.
|
||||
// Caller must hold medium.mu.
|
||||
// Caller must hold medium.lock.
|
||||
func (medium *Medium) ensureDirsLocked(directoryPath string) {
|
||||
for directoryPath != "" && directoryPath != "." {
|
||||
medium.directorySet[directoryPath] = true
|
||||
|
|
@ -174,8 +174,8 @@ func (medium *Medium) ensureDirsLocked(directoryPath string) {
|
|||
}
|
||||
|
||||
func (medium *Medium) IsFile(filePath string) bool {
|
||||
medium.mu.RLock()
|
||||
defer medium.mu.RUnlock()
|
||||
medium.lock.RLock()
|
||||
defer medium.lock.RUnlock()
|
||||
|
||||
filePath = normaliseEntryPath(filePath)
|
||||
info, err := medium.dataNode.Stat(filePath)
|
||||
|
|
@ -191,20 +191,17 @@ func (medium *Medium) FileSet(filePath, content string) error {
|
|||
}
|
||||
|
||||
func (medium *Medium) Delete(filePath string) error {
|
||||
medium.mu.Lock()
|
||||
defer medium.mu.Unlock()
|
||||
medium.lock.Lock()
|
||||
defer medium.lock.Unlock()
|
||||
|
||||
filePath = normaliseEntryPath(filePath)
|
||||
if filePath == "" {
|
||||
return core.E("datanode.Delete", "cannot delete root", fs.ErrPermission)
|
||||
}
|
||||
|
||||
// Check if it's a file in the DataNode
|
||||
info, err := medium.dataNode.Stat(filePath)
|
||||
if err != nil {
|
||||
// Check explicit directories
|
||||
if medium.directorySet[filePath] {
|
||||
// Check if dir is empty
|
||||
hasChildren, err := medium.hasPrefixLocked(filePath + "/")
|
||||
if err != nil {
|
||||
return core.E("datanode.Delete", core.Concat("failed to inspect directory: ", filePath), err)
|
||||
|
|
@ -238,8 +235,8 @@ func (medium *Medium) Delete(filePath string) error {
|
|||
}
|
||||
|
||||
func (medium *Medium) DeleteAll(filePath string) error {
|
||||
medium.mu.Lock()
|
||||
defer medium.mu.Unlock()
|
||||
medium.lock.Lock()
|
||||
defer medium.lock.Unlock()
|
||||
|
||||
filePath = normaliseEntryPath(filePath)
|
||||
if filePath == "" {
|
||||
|
|
@ -249,7 +246,6 @@ func (medium *Medium) DeleteAll(filePath string) error {
|
|||
prefix := filePath + "/"
|
||||
found := false
|
||||
|
||||
// Check if filePath itself is a file
|
||||
info, err := medium.dataNode.Stat(filePath)
|
||||
if err == nil && !info.IsDir() {
|
||||
if err := medium.removeFileLocked(filePath); err != nil {
|
||||
|
|
@ -287,20 +283,18 @@ func (medium *Medium) DeleteAll(filePath string) error {
|
|||
}
|
||||
|
||||
func (medium *Medium) Rename(oldPath, newPath string) error {
|
||||
medium.mu.Lock()
|
||||
defer medium.mu.Unlock()
|
||||
medium.lock.Lock()
|
||||
defer medium.lock.Unlock()
|
||||
|
||||
oldPath = normaliseEntryPath(oldPath)
|
||||
newPath = normaliseEntryPath(newPath)
|
||||
|
||||
// Check if source is a file
|
||||
info, err := medium.dataNode.Stat(oldPath)
|
||||
if err != nil {
|
||||
return core.E("datanode.Rename", core.Concat("not found: ", oldPath), fs.ErrNotExist)
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
// Read old, write new, delete old
|
||||
data, err := medium.readFileLocked(oldPath)
|
||||
if err != nil {
|
||||
return core.E("datanode.Rename", core.Concat("failed to read source file: ", oldPath), err)
|
||||
|
|
@ -313,7 +307,6 @@ func (medium *Medium) Rename(oldPath, newPath string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Directory rename: move all files under oldPath to newPath
|
||||
oldPrefix := oldPath + "/"
|
||||
newPrefix := newPath + "/"
|
||||
|
||||
|
|
@ -335,7 +328,6 @@ func (medium *Medium) Rename(oldPath, newPath string) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Move explicit directories
|
||||
dirsToMove := make(map[string]string)
|
||||
for directoryPath := range medium.directorySet {
|
||||
if directoryPath == oldPath || core.HasPrefix(directoryPath, oldPrefix) {
|
||||
|
|
@ -352,14 +344,13 @@ func (medium *Medium) Rename(oldPath, newPath string) error {
|
|||
}
|
||||
|
||||
func (medium *Medium) List(filePath string) ([]fs.DirEntry, error) {
|
||||
medium.mu.RLock()
|
||||
defer medium.mu.RUnlock()
|
||||
medium.lock.RLock()
|
||||
defer medium.lock.RUnlock()
|
||||
|
||||
filePath = normaliseEntryPath(filePath)
|
||||
|
||||
entries, err := medium.dataNode.ReadDir(filePath)
|
||||
if err != nil {
|
||||
// Check explicit directories
|
||||
if filePath == "" || medium.directorySet[filePath] {
|
||||
return []fs.DirEntry{}, nil
|
||||
}
|
||||
|
|
@ -399,8 +390,8 @@ func (medium *Medium) List(filePath string) ([]fs.DirEntry, error) {
|
|||
}
|
||||
|
||||
func (medium *Medium) Stat(filePath string) (fs.FileInfo, error) {
|
||||
medium.mu.RLock()
|
||||
defer medium.mu.RUnlock()
|
||||
medium.lock.RLock()
|
||||
defer medium.lock.RUnlock()
|
||||
|
||||
filePath = normaliseEntryPath(filePath)
|
||||
if filePath == "" {
|
||||
|
|
@ -419,8 +410,8 @@ func (medium *Medium) Stat(filePath string) (fs.FileInfo, error) {
|
|||
}
|
||||
|
||||
func (medium *Medium) Open(filePath string) (fs.File, error) {
|
||||
medium.mu.RLock()
|
||||
defer medium.mu.RUnlock()
|
||||
medium.lock.RLock()
|
||||
defer medium.lock.RUnlock()
|
||||
|
||||
filePath = normaliseEntryPath(filePath)
|
||||
return medium.dataNode.Open(filePath)
|
||||
|
|
@ -440,25 +431,24 @@ func (medium *Medium) Append(filePath string) (goio.WriteCloser, error) {
|
|||
return nil, core.E("datanode.Append", "empty path", fs.ErrInvalid)
|
||||
}
|
||||
|
||||
// Read existing content
|
||||
var existing []byte
|
||||
medium.mu.RLock()
|
||||
medium.lock.RLock()
|
||||
if medium.IsFile(filePath) {
|
||||
data, err := medium.readFileLocked(filePath)
|
||||
if err != nil {
|
||||
medium.mu.RUnlock()
|
||||
medium.lock.RUnlock()
|
||||
return nil, core.E("datanode.Append", core.Concat("failed to read existing content: ", filePath), err)
|
||||
}
|
||||
existing = data
|
||||
}
|
||||
medium.mu.RUnlock()
|
||||
medium.lock.RUnlock()
|
||||
|
||||
return &writeCloser{medium: medium, path: filePath, buf: existing}, nil
|
||||
}
|
||||
|
||||
func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) {
|
||||
medium.mu.RLock()
|
||||
defer medium.mu.RUnlock()
|
||||
medium.lock.RLock()
|
||||
defer medium.lock.RUnlock()
|
||||
|
||||
filePath = normaliseEntryPath(filePath)
|
||||
file, err := medium.dataNode.Open(filePath)
|
||||
|
|
@ -473,8 +463,8 @@ func (medium *Medium) WriteStream(filePath string) (goio.WriteCloser, error) {
|
|||
}
|
||||
|
||||
func (medium *Medium) Exists(filePath string) bool {
|
||||
medium.mu.RLock()
|
||||
defer medium.mu.RUnlock()
|
||||
medium.lock.RLock()
|
||||
defer medium.lock.RUnlock()
|
||||
|
||||
filePath = normaliseEntryPath(filePath)
|
||||
if filePath == "" {
|
||||
|
|
@ -488,8 +478,8 @@ func (medium *Medium) Exists(filePath string) bool {
|
|||
}
|
||||
|
||||
func (medium *Medium) IsDir(filePath string) bool {
|
||||
medium.mu.RLock()
|
||||
defer medium.mu.RUnlock()
|
||||
medium.lock.RLock()
|
||||
defer medium.lock.RUnlock()
|
||||
|
||||
filePath = normaliseEntryPath(filePath)
|
||||
if filePath == "" {
|
||||
|
|
@ -556,7 +546,7 @@ func (medium *Medium) readFileLocked(filePath string) ([]byte, error) {
|
|||
|
||||
// removeFileLocked removes a single file by rebuilding the DataNode.
|
||||
// This is necessary because Borg's DataNode doesn't expose a Remove method.
|
||||
// Caller must hold medium.mu write lock.
|
||||
// Caller must hold medium.lock write lock.
|
||||
func (medium *Medium) removeFileLocked(target string) error {
|
||||
entries, err := medium.collectAllLocked()
|
||||
if err != nil {
|
||||
|
|
@ -591,8 +581,8 @@ func (writer *writeCloser) Write(data []byte) (int, error) {
|
|||
}
|
||||
|
||||
func (writer *writeCloser) Close() error {
|
||||
writer.medium.mu.Lock()
|
||||
defer writer.medium.mu.Unlock()
|
||||
writer.medium.lock.Lock()
|
||||
defer writer.medium.lock.Unlock()
|
||||
|
||||
writer.medium.dataNode.AddData(writer.path, writer.buf)
|
||||
writer.medium.ensureDirsLocked(path.Dir(writer.path))
|
||||
|
|
|
|||
22
io.go
22
io.go
|
|
@ -165,11 +165,17 @@ type MockMedium struct {
|
|||
ModTimes map[string]time.Time
|
||||
}
|
||||
|
||||
var _ Medium = (*MockMedium)(nil)
|
||||
// Example: medium := io.NewMemoryMedium()
|
||||
// _ = medium.Write("config/app.yaml", "port: 8080")
|
||||
type MemoryMedium = MockMedium
|
||||
|
||||
var _ Medium = (*MemoryMedium)(nil)
|
||||
|
||||
// NewMockMedium returns MemoryMedium for compatibility.
|
||||
//
|
||||
// Example: medium := io.NewMockMedium()
|
||||
// _ = medium.Write("config/app.yaml", "port: 8080")
|
||||
func NewMockMedium() *MockMedium {
|
||||
func NewMockMedium() *MemoryMedium {
|
||||
return &MockMedium{
|
||||
Files: make(map[string]string),
|
||||
Dirs: make(map[string]bool),
|
||||
|
|
@ -177,6 +183,12 @@ func NewMockMedium() *MockMedium {
|
|||
}
|
||||
}
|
||||
|
||||
// Example: medium := io.NewMemoryMedium()
|
||||
// _ = medium.Write("config/app.yaml", "port: 8080")
|
||||
func NewMemoryMedium() *MemoryMedium {
|
||||
return NewMockMedium()
|
||||
}
|
||||
|
||||
func (medium *MockMedium) Read(path string) (string, error) {
|
||||
content, ok := medium.Files[path]
|
||||
if !ok {
|
||||
|
|
@ -369,6 +381,9 @@ type MockFile struct {
|
|||
offset int64
|
||||
}
|
||||
|
||||
// MemoryFile is the preferred alias for MockFile.
|
||||
type MemoryFile = MockFile
|
||||
|
||||
func (file *MockFile) Stat() (fs.FileInfo, error) {
|
||||
return FileInfo{
|
||||
name: file.name,
|
||||
|
|
@ -396,6 +411,9 @@ type MockWriteCloser struct {
|
|||
data []byte
|
||||
}
|
||||
|
||||
// MemoryWriteCloser is the preferred alias for MockWriteCloser.
|
||||
type MemoryWriteCloser = MockWriteCloser
|
||||
|
||||
func (writeCloser *MockWriteCloser) Write(data []byte) (int, error) {
|
||||
writeCloser.data = append(writeCloser.data, data...)
|
||||
return len(data), nil
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ func (node *Node) CopyFile(sourcePath, destinationPath string, perm fs.FileMode)
|
|||
return coreio.Local.WriteMode(destinationPath, string(file.content), perm)
|
||||
}
|
||||
|
||||
// Example: _ = nodeTree.CopyTo(io.NewMockMedium(), "config", "backup/config")
|
||||
// Example: _ = nodeTree.CopyTo(io.NewMemoryMedium(), "config", "backup/config")
|
||||
func (node *Node) CopyTo(target coreio.Medium, sourcePath, destPath string) error {
|
||||
sourcePath = core.TrimPrefix(sourcePath, "/")
|
||||
info, err := node.Stat(sourcePath)
|
||||
|
|
|
|||
|
|
@ -420,12 +420,12 @@ func TestNode_CopyTo_Good(t *testing.T) {
|
|||
n.AddData("config/app.yaml", []byte("port: 8080"))
|
||||
n.AddData("config/env/app.env", []byte("MODE=test"))
|
||||
|
||||
fileTarget := coreio.NewMockMedium()
|
||||
fileTarget := coreio.NewMemoryMedium()
|
||||
err := n.CopyTo(fileTarget, "config/app.yaml", "backup/app.yaml")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "port: 8080", fileTarget.Files["backup/app.yaml"])
|
||||
|
||||
dirTarget := coreio.NewMockMedium()
|
||||
dirTarget := coreio.NewMemoryMedium()
|
||||
err = n.CopyTo(dirTarget, "config", "backup/config")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "port: 8080", dirTarget.Files["backup/config/app.yaml"])
|
||||
|
|
@ -434,7 +434,7 @@ func TestNode_CopyTo_Good(t *testing.T) {
|
|||
|
||||
func TestNode_CopyTo_Bad(t *testing.T) {
|
||||
n := New()
|
||||
err := n.CopyTo(coreio.NewMockMedium(), "missing", "backup/missing")
|
||||
err := n.CopyTo(coreio.NewMemoryMedium(), "missing", "backup/missing")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,12 +34,11 @@ type Options struct {
|
|||
|
||||
// Example: service, _ := workspace.New(workspace.Options{Core: core.New(), Crypt: cryptProvider})
|
||||
type Service struct {
|
||||
core *core.Core
|
||||
crypt CryptProvider
|
||||
activeWorkspaceID string
|
||||
rootPath string
|
||||
medium io.Medium
|
||||
mu sync.RWMutex
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
var _ Workspace = (*Service)(nil)
|
||||
|
|
@ -58,7 +57,6 @@ func New(options Options) (*Service, error) {
|
|||
}
|
||||
|
||||
service := &Service{
|
||||
core: options.Core,
|
||||
rootPath: rootPath,
|
||||
medium: io.Local,
|
||||
}
|
||||
|
|
@ -76,8 +74,8 @@ func New(options Options) (*Service, error) {
|
|||
|
||||
// Example: workspaceID, _ := service.CreateWorkspace("alice", "pass123")
|
||||
func (service *Service) CreateWorkspace(identifier, password string) (string, error) {
|
||||
service.mu.Lock()
|
||||
defer service.mu.Unlock()
|
||||
service.lock.Lock()
|
||||
defer service.lock.Unlock()
|
||||
|
||||
if service.crypt == nil {
|
||||
return "", core.E("workspace.CreateWorkspace", "crypt service not available", nil)
|
||||
|
|
@ -114,8 +112,8 @@ func (service *Service) CreateWorkspace(identifier, password string) (string, er
|
|||
|
||||
// Example: _ = service.SwitchWorkspace(workspaceID)
|
||||
func (service *Service) SwitchWorkspace(workspaceID string) error {
|
||||
service.mu.Lock()
|
||||
defer service.mu.Unlock()
|
||||
service.lock.Lock()
|
||||
defer service.lock.Unlock()
|
||||
|
||||
workspaceDirectory, err := service.resolveWorkspaceDirectory("workspace.SwitchWorkspace", workspaceID)
|
||||
if err != nil {
|
||||
|
|
@ -148,8 +146,8 @@ func (service *Service) resolveActiveWorkspaceFilePath(operation, workspaceFileP
|
|||
|
||||
// Example: content, _ := service.WorkspaceFileGet("notes/todo.txt")
|
||||
func (service *Service) WorkspaceFileGet(workspaceFilePath string) (string, error) {
|
||||
service.mu.RLock()
|
||||
defer service.mu.RUnlock()
|
||||
service.lock.RLock()
|
||||
defer service.lock.RUnlock()
|
||||
|
||||
filePath, err := service.resolveActiveWorkspaceFilePath("workspace.WorkspaceFileGet", workspaceFilePath)
|
||||
if err != nil {
|
||||
|
|
@ -160,8 +158,8 @@ func (service *Service) WorkspaceFileGet(workspaceFilePath string) (string, erro
|
|||
|
||||
// Example: _ = service.WorkspaceFileSet("notes/todo.txt", "ship it")
|
||||
func (service *Service) WorkspaceFileSet(workspaceFilePath, content string) error {
|
||||
service.mu.Lock()
|
||||
defer service.mu.Unlock()
|
||||
service.lock.Lock()
|
||||
defer service.lock.Unlock()
|
||||
|
||||
filePath, err := service.resolveActiveWorkspaceFilePath("workspace.WorkspaceFileSet", workspaceFilePath)
|
||||
if err != nil {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue