feat: xmnt

This commit is contained in:
Yiyang Kang 2022-10-07 01:19:04 +08:00
parent 00b3e4b24f
commit 5a998b713c
16 changed files with 1481 additions and 0 deletions

68
mnt/mnt.go Normal file
View file

@ -0,0 +1,68 @@
package mnt
import (
"github.com/go-errors/errors"
"gensokyo.cafe/xmnt/msg"
)
type Mounter interface {
Mount() error
Unmount() error
}
type (
MounterFunc func(*Preset) (Mounter, error)
MatcherFunc func(string) ([]*Preset, error)
)
var mounters = map[string]MounterFunc{}
func RegisterMounter(typeName string, fn MounterFunc) {
mounters[typeName] = fn
}
func MounterFromPreset(preset *Preset) (Mounter, error) {
if fn, ok := mounters[preset.Type]; !ok {
return nil, errors.Errorf("unknown type %q", preset.Type)
} else {
return fn(preset)
}
}
var matchers []MatcherFunc
func RegisterMatcher(fn MatcherFunc) {
matchers = append(matchers, fn)
}
func MatchAll(s string) ([]*Preset, error) {
// Match against presets. If matched, return the preset (should be only one).
if matches, err := match(s); err != nil {
msg.Errorf("Failed to find match in presets: %v", err)
} else if len(matches) > 0 {
return matches[:1], nil
}
// Run other matchers. Might produce multiple matches.
var ret []*Preset
rCh := make(chan []*Preset, 1)
for _, fn := range matchers {
go func(fn MatcherFunc) {
matches, err := fn(s)
if err != nil {
msg.Errorf("%v", err)
rCh <- nil
} else {
rCh <- matches
}
}(fn)
}
for range matchers {
matches := <-rCh
ret = append(ret, matches...)
}
return ret, nil
}

97
mnt/preset.go Normal file
View file

@ -0,0 +1,97 @@
package mnt
import (
"os"
"path/filepath"
"strings"
"github.com/go-errors/errors"
"gopkg.in/yaml.v3"
"gensokyo.cafe/xmnt/msg"
"gensokyo.cafe/xmnt/util"
)
type Preset struct {
Name string `yaml:"name"`
Type string `yaml:"type"`
Path string `yaml:"path"`
MountPoint string `yaml:"mountpoint"`
AuthCmd string `yaml:"auth_cmd"` // e.g. for loading the encryption key
MountCmd string `yaml:"mount_cmd"`
CheckCmd string `yaml:"check_cmd"` // check if the mounting was successful
UnmountCmd string `yaml:"unmount_cmd"`
UnAuthCmd string `yaml:"unauth_cmd"` // e.g. for unloading the encryption key
UnmountCheckCmd string `yaml:"unmount_check_cmd"`
}
func (p *Preset) String() string {
return p.Type + ": " + p.Name + " (" + p.Path + ")"
}
func readPreset(path string) (*Preset, error) {
buf, err := os.ReadFile(path)
if err != nil {
return nil, errors.Wrap(err, 0)
}
var preset *Preset
if err = yaml.Unmarshal(buf, &preset); err != nil {
return nil, errors.Wrap(err, 0)
}
if preset.Name == "" {
preset.Name = filepath.Base(path)[:len(filepath.Base(path))-4] // remove .yml
}
return preset, nil
}
func ReadPresets() ([]*Preset, error) {
cfgDir, err := os.UserConfigDir()
if err != nil {
return nil, errors.WrapPrefix(err, "cannot obtain user config dir", 0)
}
presetsDir := filepath.Join(cfgDir, "xmnt", "presets")
dirExist, err := util.DirExists(presetsDir)
if err != nil {
return nil, errors.Wrap(err, 0)
}
if !dirExist {
return nil, nil
}
var ret []*Preset
entries, err := os.ReadDir(presetsDir)
if err != nil {
return nil, errors.Wrap(err, 0)
}
for _, entry := range entries {
if !entry.Type().IsRegular() || !strings.HasSuffix(entry.Name(), ".yml") {
continue
}
preset, err := readPreset(filepath.Join(presetsDir, entry.Name()))
if err != nil {
msg.Errorf("Failed to read preset %s: %v", entry.Name(), err)
continue
}
ret = append(ret, preset)
}
return ret, nil
}
func match(s string) ([]*Preset, error) {
presets, err := ReadPresets()
if err != nil {
return nil, errors.Wrap(err, 0)
}
for _, p := range presets {
// for presets, only return exact match.
if p.Name == s {
return []*Preset{p}, nil
}
}
return nil, nil
}