Implement resource loading feature
This commit is contained in:
parent
4845a33a25
commit
986513750f
@ -13,6 +13,7 @@ var (
|
|||||||
ErrOffsetChanged = errors.New("offset has already been changed")
|
ErrOffsetChanged = errors.New("offset has already been changed")
|
||||||
ErrReadOpen = errors.New("only one file can be opened for reading at a time")
|
ErrReadOpen = errors.New("only one file can be opened for reading at a time")
|
||||||
ErrWriteOpen = errors.New("only one file can be opened for writing at a time")
|
ErrWriteOpen = errors.New("only one file can be opened for writing at a time")
|
||||||
|
ErrNoRemoveRoot = errors.New("refusing to remove root directory")
|
||||||
)
|
)
|
||||||
|
|
||||||
// FSError represents an error returned by BLE FS
|
// FSError represents an error returned by BLE FS
|
||||||
|
@ -12,4 +12,4 @@ func (iofs goFS) Open(path string) (fs.File, error) {
|
|||||||
|
|
||||||
func (blefs *FS) GoFS() fs.FS {
|
func (blefs *FS) GoFS() fs.FS {
|
||||||
return goFS{blefs}
|
return goFS{blefs}
|
||||||
}
|
}
|
||||||
|
2
go.sum
2
go.sum
@ -15,6 +15,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJ
|
|||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/muka/go-bluetooth v0.0.0-20220819140550-1d8857e3b268 h1:kOnq7TfaAO2Vc/MHxPqFIXe00y1qBxJAvhctXdko6vo=
|
||||||
|
github.com/muka/go-bluetooth v0.0.0-20220819140550-1d8857e3b268/go.mod h1:dMCjicU6vRBk34dqOmIZm0aod6gUwZXOXzBROqGous0=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/paypal/gatt v0.0.0-20151011220935-4ae819d591cf/go.mod h1:+AwQL2mK3Pd3S+TUwg0tYQjid0q1txyNUJuuSmz8Kdk=
|
github.com/paypal/gatt v0.0.0-20151011220935-4ae819d591cf/go.mod h1:+AwQL2mK3Pd3S+TUwg0tYQjid0q1txyNUJuuSmz8Kdk=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
180
resources.go
Normal file
180
resources.go
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
package infinitime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"go.arsenm.dev/infinitime/blefs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResourceOperation represents an operation performed during
|
||||||
|
// resource loading
|
||||||
|
type ResourceOperation uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ResourceOperationUpload represents the upload phase
|
||||||
|
// of resource loading
|
||||||
|
ResourceOperationUpload = iota
|
||||||
|
// ResourceOperationRemoveObsolete represents the obsolete
|
||||||
|
// file removal phase of resource loading
|
||||||
|
ResourceOperationRemoveObsolete
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResourceManifest is the structure of the resource manifest file
|
||||||
|
type ResourceManifest struct {
|
||||||
|
Resources []Resource `json:"resources"`
|
||||||
|
Obsolete []ObsoleteResource `json:"obsolete_files"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resource represents a resource entry in the manifest
|
||||||
|
type Resource struct {
|
||||||
|
Name string `json:"filename"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObsoleteResource represents an obsolete file entry in the manifest
|
||||||
|
type ObsoleteResource struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Since string `json:"since"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResourceLoadProgress contains information on the progress of
|
||||||
|
// a resource load
|
||||||
|
type ResourceLoadProgress struct {
|
||||||
|
Operation ResourceOperation
|
||||||
|
Name string
|
||||||
|
Total int64
|
||||||
|
Sent int64
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadResources accepts a resources zip file and a BLE FS.
|
||||||
|
// It loads the resources from the zip onto the FS.
|
||||||
|
func LoadResources(file *os.File, fs *blefs.FS) (<-chan ResourceLoadProgress, error) {
|
||||||
|
out := make(chan ResourceLoadProgress, 10)
|
||||||
|
|
||||||
|
fi, err := file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := zip.NewReader(file, fi.Size())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := r.Open("resources.json")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var manifest ResourceManifest
|
||||||
|
err = json.NewDecoder(m).Decode(&manifest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(out)
|
||||||
|
|
||||||
|
for _, file := range manifest.Obsolete {
|
||||||
|
name := filepath.Base(file.Path)
|
||||||
|
|
||||||
|
err := fs.RemoveAll(file.Path)
|
||||||
|
if err != nil {
|
||||||
|
out <- ResourceLoadProgress{
|
||||||
|
Name: name,
|
||||||
|
Operation: ResourceOperationRemoveObsolete,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
out <- ResourceLoadProgress{
|
||||||
|
Name: name,
|
||||||
|
Operation: ResourceOperationRemoveObsolete,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range manifest.Resources {
|
||||||
|
src, err := r.Open(file.Name)
|
||||||
|
if err != nil {
|
||||||
|
out <- ResourceLoadProgress{
|
||||||
|
Name: file.Name,
|
||||||
|
Operation: ResourceOperationUpload,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
srcFi, err := src.Stat()
|
||||||
|
if err != nil {
|
||||||
|
out <- ResourceLoadProgress{
|
||||||
|
Name: file.Name,
|
||||||
|
Operation: ResourceOperationUpload,
|
||||||
|
Total: srcFi.Size(),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = fs.MkdirAll(filepath.Dir(file.Path))
|
||||||
|
if err != nil {
|
||||||
|
out <- ResourceLoadProgress{
|
||||||
|
Name: file.Name,
|
||||||
|
Operation: ResourceOperationUpload,
|
||||||
|
Total: srcFi.Size(),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dst, err := fs.Create(file.Path, uint32(srcFi.Size()))
|
||||||
|
if err != nil {
|
||||||
|
out <- ResourceLoadProgress{
|
||||||
|
Name: file.Name,
|
||||||
|
Operation: ResourceOperationUpload,
|
||||||
|
Total: srcFi.Size(),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
progCh := dst.Progress()
|
||||||
|
go func() {
|
||||||
|
for sent := range progCh {
|
||||||
|
out <- ResourceLoadProgress{
|
||||||
|
Name: file.Name,
|
||||||
|
Operation: ResourceOperationUpload,
|
||||||
|
Total: srcFi.Size(),
|
||||||
|
Sent: int64(sent),
|
||||||
|
}
|
||||||
|
|
||||||
|
if sent == uint32(srcFi.Size()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
n, err := io.Copy(dst, src)
|
||||||
|
if err != nil {
|
||||||
|
out <- ResourceLoadProgress{
|
||||||
|
Name: file.Name,
|
||||||
|
Operation: ResourceOperationUpload,
|
||||||
|
Total: srcFi.Size(),
|
||||||
|
Sent: n,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
src.Close()
|
||||||
|
dst.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
Reference in New Issue
Block a user