mirror of
https://github.com/samuelncui/acp.git
synced 2025-12-23 05:05:15 +00:00
146 lines
2.6 KiB
Go
146 lines
2.6 KiB
Go
package acp
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"sync"
|
|
|
|
"github.com/schollz/progressbar/v3"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
const (
|
|
StageIndex = iota
|
|
StageCopy
|
|
StageFinished
|
|
)
|
|
|
|
type Copyer struct {
|
|
*option
|
|
|
|
createFlag int
|
|
stage int64
|
|
copyedBytes int64
|
|
totalBytes int64
|
|
copyedFiles int64
|
|
totalFiles int64
|
|
|
|
updateProgressBar func(func(bar *progressbar.ProgressBar))
|
|
updateCopying func(func(set map[int64]struct{}))
|
|
logf func(l logrus.Level, format string, args ...any)
|
|
|
|
jobsLock sync.Mutex
|
|
jobs []*baseJob
|
|
noSpaceSource []*source
|
|
|
|
errsLock sync.Mutex
|
|
errors []*Error
|
|
|
|
badDstsLock sync.Mutex
|
|
badDsts map[string]error
|
|
|
|
readingFiles chan struct{}
|
|
writePipe chan *writeJob
|
|
postPipe chan *baseJob
|
|
|
|
running sync.WaitGroup
|
|
}
|
|
|
|
func New(ctx context.Context, opts ...Option) (*Copyer, error) {
|
|
opt := newOption()
|
|
for _, o := range opts {
|
|
if o == nil {
|
|
continue
|
|
}
|
|
opt = o(opt)
|
|
}
|
|
if err := opt.check(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
c := &Copyer{
|
|
option: opt,
|
|
stage: StageIndex,
|
|
|
|
updateProgressBar: func(f func(bar *progressbar.ProgressBar)) {},
|
|
updateCopying: func(f func(set map[int64]struct{})) {},
|
|
logf: func(l logrus.Level, format string, args ...any) {
|
|
logrus.StandardLogger().Logf(l, format, args...)
|
|
},
|
|
|
|
badDsts: make(map[string]error),
|
|
writePipe: make(chan *writeJob, 32),
|
|
postPipe: make(chan *baseJob, 8),
|
|
}
|
|
|
|
c.createFlag = os.O_WRONLY | os.O_CREATE
|
|
if c.overwrite {
|
|
c.createFlag |= os.O_TRUNC
|
|
} else {
|
|
c.createFlag |= os.O_EXCL
|
|
}
|
|
|
|
if c.fromDevice.linear {
|
|
c.readingFiles = make(chan struct{}, 1)
|
|
}
|
|
|
|
c.running.Add(1)
|
|
go wrap(ctx, func() { c.run(ctx) })
|
|
return c, nil
|
|
}
|
|
|
|
func (c *Copyer) Wait() *Report {
|
|
c.running.Wait()
|
|
return c.Report()
|
|
}
|
|
|
|
func (c *Copyer) run(ctx context.Context) {
|
|
defer c.running.Done()
|
|
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
defer cancel()
|
|
|
|
if c.withProgressBar {
|
|
c.startProgressBar(ctx)
|
|
}
|
|
|
|
c.index(ctx)
|
|
if !c.checkJobs() {
|
|
return
|
|
}
|
|
|
|
if err := c.applyAutoFillLimit(); err != nil {
|
|
c.reportError("_autofill", err)
|
|
return
|
|
}
|
|
|
|
c.copy(ctx)
|
|
}
|
|
|
|
func (c *Copyer) reportError(file string, err error) {
|
|
e := &Error{Path: file, Err: err}
|
|
c.logf(logrus.ErrorLevel, e.Error())
|
|
|
|
c.errsLock.Lock()
|
|
defer c.errsLock.Unlock()
|
|
c.errors = append(c.errors, e)
|
|
}
|
|
|
|
func (c *Copyer) getErrors() []*Error {
|
|
c.errsLock.Lock()
|
|
defer c.errsLock.Unlock()
|
|
return c.errors
|
|
}
|
|
|
|
func (c *Copyer) addBadDsts(dst string, err error) {
|
|
c.badDstsLock.Lock()
|
|
defer c.badDstsLock.Unlock()
|
|
c.badDsts[dst] = err
|
|
}
|
|
|
|
func (c *Copyer) getBadDsts() map[string]error {
|
|
c.errsLock.Lock()
|
|
defer c.errsLock.Unlock()
|
|
return c.badDsts
|
|
}
|