github.com/sagernet/sing-box@v1.2.7/experimental/clashapi/cachefile/cache.go (about) 1 package cachefile 2 3 import ( 4 "os" 5 "time" 6 7 "github.com/sagernet/sing-box/adapter" 8 9 "go.etcd.io/bbolt" 10 ) 11 12 var bucketSelected = []byte("selected") 13 14 var _ adapter.ClashCacheFile = (*CacheFile)(nil) 15 16 type CacheFile struct { 17 DB *bbolt.DB 18 } 19 20 func Open(path string) (*CacheFile, error) { 21 const fileMode = 0o666 22 options := bbolt.Options{Timeout: time.Second} 23 db, err := bbolt.Open(path, fileMode, &options) 24 switch err { 25 case bbolt.ErrInvalid, bbolt.ErrChecksum, bbolt.ErrVersionMismatch: 26 if err = os.Remove(path); err != nil { 27 break 28 } 29 db, err = bbolt.Open(path, 0o666, &options) 30 } 31 if err != nil { 32 return nil, err 33 } 34 return &CacheFile{db}, nil 35 } 36 37 func (c *CacheFile) LoadSelected(group string) string { 38 var selected string 39 c.DB.View(func(t *bbolt.Tx) error { 40 bucket := t.Bucket(bucketSelected) 41 if bucket == nil { 42 return nil 43 } 44 selectedBytes := bucket.Get([]byte(group)) 45 if len(selectedBytes) > 0 { 46 selected = string(selectedBytes) 47 } 48 return nil 49 }) 50 return selected 51 } 52 53 func (c *CacheFile) StoreSelected(group, selected string) error { 54 return c.DB.Batch(func(t *bbolt.Tx) error { 55 bucket, err := t.CreateBucketIfNotExists(bucketSelected) 56 if err != nil { 57 return err 58 } 59 return bucket.Put([]byte(group), []byte(selected)) 60 }) 61 } 62 63 func (c *CacheFile) Close() error { 64 return c.DB.Close() 65 }