Add public external file open API

This commit is contained in:
2026-06-27 13:30:31 +08:00
parent 6cc37972d1
commit 173cc93258
13 changed files with 367 additions and 10 deletions
+62
View File
@@ -0,0 +1,62 @@
package externalopen
import (
"os/exec"
"path/filepath"
"runtime"
)
type Runner func(name string, args ...string) error
type Service struct {
goos string
runner Runner
}
func NewService() *Service {
return NewServiceFor(runtime.GOOS, func(name string, args ...string) error {
return exec.Command(name, args...).Start()
})
}
func NewServiceFor(goos string, runner Runner) *Service {
return &Service{goos: goos, runner: runner}
}
func (s *Service) OpenPath(path string) error {
name, args := s.openCommand(path)
return s.runner(name, args...)
}
func (s *Service) ShowInFolder(path string, isDir bool) error {
name, args := s.showCommand(path, isDir)
return s.runner(name, args...)
}
func (s *Service) openCommand(path string) (string, []string) {
switch s.goos {
case "darwin":
return "open", []string{path}
case "windows":
return "rundll32", []string{"url.dll,FileProtocolHandler", path}
default:
return "xdg-open", []string{path}
}
}
func (s *Service) showCommand(path string, isDir bool) (string, []string) {
switch s.goos {
case "darwin":
return "open", []string{"-R", path}
case "windows":
if isDir {
return "explorer", []string{path}
}
return "explorer", []string{"/select," + path}
default:
if isDir {
return "xdg-open", []string{path}
}
return "xdg-open", []string{filepath.Dir(path)}
}
}
@@ -0,0 +1,71 @@
package externalopen
import (
"reflect"
"testing"
)
func TestOpenPathCommands(t *testing.T) {
cases := []struct {
goos string
path string
want commandCall
}{
{goos: "linux", path: "/vault/file.txt", want: commandCall{name: "xdg-open", args: []string{"/vault/file.txt"}}},
{goos: "darwin", path: "/vault/file.txt", want: commandCall{name: "open", args: []string{"/vault/file.txt"}}},
{goos: "windows", path: `C:\Vault\file.txt`, want: commandCall{name: "rundll32", args: []string{"url.dll,FileProtocolHandler", `C:\Vault\file.txt`}}},
}
for _, tc := range cases {
t.Run(tc.goos, func(t *testing.T) {
var got commandCall
svc := NewServiceFor(tc.goos, func(name string, args ...string) error {
got = commandCall{name: name, args: args}
return nil
})
if err := svc.OpenPath(tc.path); err != nil {
t.Fatalf("OpenPath: %v", err)
}
if !reflect.DeepEqual(got, tc.want) {
t.Fatalf("command = %#v, want %#v", got, tc.want)
}
})
}
}
func TestShowInFolderCommands(t *testing.T) {
cases := []struct {
name string
goos string
path string
isDir bool
want commandCall
}{
{name: "linux file opens parent", goos: "linux", path: "/vault/Docs/file.txt", want: commandCall{name: "xdg-open", args: []string{"/vault/Docs"}}},
{name: "linux dir opens dir", goos: "linux", path: "/vault/Docs", isDir: true, want: commandCall{name: "xdg-open", args: []string{"/vault/Docs"}}},
{name: "darwin reveal", goos: "darwin", path: "/vault/file.txt", want: commandCall{name: "open", args: []string{"-R", "/vault/file.txt"}}},
{name: "windows select file", goos: "windows", path: `C:\Vault\file.txt`, want: commandCall{name: "explorer", args: []string{`/select,C:\Vault\file.txt`}}},
{name: "windows open dir", goos: "windows", path: `C:\Vault\Docs`, isDir: true, want: commandCall{name: "explorer", args: []string{`C:\Vault\Docs`}}},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
var got commandCall
svc := NewServiceFor(tc.goos, func(name string, args ...string) error {
got = commandCall{name: name, args: args}
return nil
})
if err := svc.ShowInFolder(tc.path, tc.isDir); err != nil {
t.Fatalf("ShowInFolder: %v", err)
}
if !reflect.DeepEqual(got, tc.want) {
t.Fatalf("command = %#v, want %#v", got, tc.want)
}
})
}
}
type commandCall struct {
name string
args []string
}