2020-06-13 20:37:42 +02:00
|
|
|
package accounting
|
|
|
|
|
|
|
|
import (
|
2020-11-05 17:59:59 +01:00
|
|
|
"context"
|
2020-06-13 20:37:42 +02:00
|
|
|
"fmt"
|
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/rclone/rclone/fs"
|
2020-08-05 17:59:44 +02:00
|
|
|
"github.com/rclone/rclone/fs/rc"
|
2020-06-13 20:37:42 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// transferMap holds name to transfer map
|
|
|
|
type transferMap struct {
|
|
|
|
mu sync.RWMutex
|
|
|
|
items map[string]*Transfer
|
|
|
|
name string
|
|
|
|
}
|
|
|
|
|
|
|
|
// newTransferMap creates a new empty transfer map of capacity size
|
|
|
|
func newTransferMap(size int, name string) *transferMap {
|
|
|
|
return &transferMap{
|
|
|
|
items: make(map[string]*Transfer, size),
|
|
|
|
name: name,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// add adds a new transfer to the map
|
|
|
|
func (tm *transferMap) add(tr *Transfer) {
|
|
|
|
tm.mu.Lock()
|
|
|
|
tm.items[tr.remote] = tr
|
|
|
|
tm.mu.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// del removes a transfer from the map by name
|
2022-06-27 18:56:03 +02:00
|
|
|
func (tm *transferMap) del(remote string) bool {
|
2020-06-13 20:37:42 +02:00
|
|
|
tm.mu.Lock()
|
2022-06-27 18:56:03 +02:00
|
|
|
_, exists := tm.items[remote]
|
2020-06-13 20:37:42 +02:00
|
|
|
delete(tm.items, remote)
|
|
|
|
tm.mu.Unlock()
|
2022-06-27 18:56:03 +02:00
|
|
|
|
|
|
|
return exists
|
2020-06-13 20:37:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// merge adds items from another map
|
|
|
|
func (tm *transferMap) merge(m *transferMap) {
|
|
|
|
tm.mu.Lock()
|
|
|
|
m.mu.Lock()
|
|
|
|
for name, tr := range m.items {
|
|
|
|
tm.items[name] = tr
|
|
|
|
}
|
|
|
|
m.mu.Unlock()
|
|
|
|
tm.mu.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// empty returns whether the map has any items
|
|
|
|
func (tm *transferMap) empty() bool {
|
|
|
|
tm.mu.RLock()
|
|
|
|
defer tm.mu.RUnlock()
|
|
|
|
return len(tm.items) == 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// count returns the number of items in the map
|
|
|
|
func (tm *transferMap) count() int {
|
|
|
|
tm.mu.RLock()
|
|
|
|
defer tm.mu.RUnlock()
|
|
|
|
return len(tm.items)
|
|
|
|
}
|
|
|
|
|
2020-08-05 17:59:44 +02:00
|
|
|
// _sortedSlice returns all transfers sorted by start time
|
|
|
|
//
|
|
|
|
// Call with mu.Rlock held
|
|
|
|
func (tm *transferMap) _sortedSlice() []*Transfer {
|
2020-06-16 17:18:32 +02:00
|
|
|
s := make([]*Transfer, 0, len(tm.items))
|
|
|
|
for _, tr := range tm.items {
|
|
|
|
s = append(s, tr)
|
|
|
|
}
|
2020-09-18 13:30:01 +02:00
|
|
|
// sort by time first and if equal by name. Note that the relatively
|
|
|
|
// low time resolution on Windows can cause equal times.
|
2020-06-16 17:18:32 +02:00
|
|
|
sort.Slice(s, func(i, j int) bool {
|
2020-09-18 13:30:01 +02:00
|
|
|
a, b := s[i], s[j]
|
|
|
|
if a.startedAt.Before(b.startedAt) {
|
|
|
|
return true
|
|
|
|
} else if !a.startedAt.Equal(b.startedAt) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return a.remote < b.remote
|
2020-06-16 17:18:32 +02:00
|
|
|
})
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
2020-06-13 20:37:42 +02:00
|
|
|
// String returns string representation of map items excluding any in
|
|
|
|
// exclude (if set).
|
2020-11-05 17:59:59 +01:00
|
|
|
func (tm *transferMap) String(ctx context.Context, progress *inProgress, exclude *transferMap) string {
|
2020-06-13 20:37:42 +02:00
|
|
|
tm.mu.RLock()
|
|
|
|
defer tm.mu.RUnlock()
|
2020-11-05 12:33:32 +01:00
|
|
|
ci := fs.GetConfig(ctx)
|
Spelling fixes
Fix spelling of: above, already, anonymous, associated,
authentication, bandwidth, because, between, blocks, calculate,
candidates, cautious, changelog, cleaner, clipboard, command,
completely, concurrently, considered, constructs, corrupt, current,
daemon, dependencies, deprecated, directory, dispatcher, download,
eligible, ellipsis, encrypter, endpoint, entrieslist, essentially,
existing writers, existing, expires, filesystem, flushing, frequently,
hierarchy, however, implementation, implements, inaccurate,
individually, insensitive, longer, maximum, metadata, modified,
multipart, namedirfirst, nextcloud, obscured, opened, optional,
owncloud, pacific, passphrase, password, permanently, persimmon,
positive, potato, protocol, quota, receiving, recommends, referring,
requires, revisited, satisfied, satisfies, satisfy, semver,
serialized, session, storage, strategies, stringlist, successful,
supported, surprise, temporarily, temporary, transactions, unneeded,
update, uploads, wrapped
Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-10-09 02:17:24 +02:00
|
|
|
stringList := make([]string, 0, len(tm.items))
|
2020-08-05 17:59:44 +02:00
|
|
|
for _, tr := range tm._sortedSlice() {
|
2023-02-27 17:46:12 +01:00
|
|
|
var what = tr.what
|
2020-06-13 20:37:42 +02:00
|
|
|
if exclude != nil {
|
|
|
|
exclude.mu.RLock()
|
2020-06-16 17:18:32 +02:00
|
|
|
_, found := exclude.items[tr.remote]
|
2020-06-13 20:37:42 +02:00
|
|
|
exclude.mu.RUnlock()
|
|
|
|
if found {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var out string
|
2020-06-16 17:18:32 +02:00
|
|
|
if acc := progress.get(tr.remote); acc != nil {
|
2020-06-13 20:37:42 +02:00
|
|
|
out = acc.String()
|
2023-02-27 17:46:12 +01:00
|
|
|
if what != "" {
|
|
|
|
out += ", " + what
|
|
|
|
}
|
2020-06-13 20:37:42 +02:00
|
|
|
} else {
|
2023-02-06 11:30:22 +01:00
|
|
|
if what == "" {
|
|
|
|
what = tm.name
|
|
|
|
}
|
2020-06-13 20:37:42 +02:00
|
|
|
out = fmt.Sprintf("%*s: %s",
|
2020-11-05 12:33:32 +01:00
|
|
|
ci.StatsFileNameLength,
|
|
|
|
shortenName(tr.remote, ci.StatsFileNameLength),
|
2023-02-06 11:30:22 +01:00
|
|
|
what,
|
2020-06-13 20:37:42 +02:00
|
|
|
)
|
|
|
|
}
|
Spelling fixes
Fix spelling of: above, already, anonymous, associated,
authentication, bandwidth, because, between, blocks, calculate,
candidates, cautious, changelog, cleaner, clipboard, command,
completely, concurrently, considered, constructs, corrupt, current,
daemon, dependencies, deprecated, directory, dispatcher, download,
eligible, ellipsis, encrypter, endpoint, entrieslist, essentially,
existing writers, existing, expires, filesystem, flushing, frequently,
hierarchy, however, implementation, implements, inaccurate,
individually, insensitive, longer, maximum, metadata, modified,
multipart, namedirfirst, nextcloud, obscured, opened, optional,
owncloud, pacific, passphrase, password, permanently, persimmon,
positive, potato, protocol, quota, receiving, recommends, referring,
requires, revisited, satisfied, satisfies, satisfy, semver,
serialized, session, storage, strategies, stringlist, successful,
supported, surprise, temporarily, temporary, transactions, unneeded,
update, uploads, wrapped
Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-10-09 02:17:24 +02:00
|
|
|
stringList = append(stringList, " * "+out)
|
2020-06-13 20:37:42 +02:00
|
|
|
}
|
Spelling fixes
Fix spelling of: above, already, anonymous, associated,
authentication, bandwidth, because, between, blocks, calculate,
candidates, cautious, changelog, cleaner, clipboard, command,
completely, concurrently, considered, constructs, corrupt, current,
daemon, dependencies, deprecated, directory, dispatcher, download,
eligible, ellipsis, encrypter, endpoint, entrieslist, essentially,
existing writers, existing, expires, filesystem, flushing, frequently,
hierarchy, however, implementation, implements, inaccurate,
individually, insensitive, longer, maximum, metadata, modified,
multipart, namedirfirst, nextcloud, obscured, opened, optional,
owncloud, pacific, passphrase, password, permanently, persimmon,
positive, potato, protocol, quota, receiving, recommends, referring,
requires, revisited, satisfied, satisfies, satisfy, semver,
serialized, session, storage, strategies, stringlist, successful,
supported, surprise, temporarily, temporary, transactions, unneeded,
update, uploads, wrapped
Signed-off-by: Josh Soref <jsoref@users.noreply.github.com>
2020-10-09 02:17:24 +02:00
|
|
|
return strings.Join(stringList, "\n")
|
2020-06-13 20:37:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// progress returns total bytes read as well as the size.
|
|
|
|
func (tm *transferMap) progress(stats *StatsInfo) (totalBytes, totalSize int64) {
|
|
|
|
tm.mu.RLock()
|
|
|
|
defer tm.mu.RUnlock()
|
|
|
|
for name := range tm.items {
|
|
|
|
if acc := stats.inProgress.get(name); acc != nil {
|
|
|
|
bytes, size := acc.progress()
|
|
|
|
if size >= 0 && bytes >= 0 {
|
|
|
|
totalBytes += bytes
|
|
|
|
totalSize += size
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return totalBytes, totalSize
|
|
|
|
}
|
2020-08-05 17:59:44 +02:00
|
|
|
|
|
|
|
// remotes returns a []string of the remote names for the transferMap
|
|
|
|
func (tm *transferMap) remotes() (c []string) {
|
|
|
|
tm.mu.RLock()
|
|
|
|
defer tm.mu.RUnlock()
|
|
|
|
for _, tr := range tm._sortedSlice() {
|
|
|
|
c = append(c, tr.remote)
|
|
|
|
}
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
// rcStats returns a []rc.Params of the stats for the transferMap
|
|
|
|
func (tm *transferMap) rcStats(progress *inProgress) (t []rc.Params) {
|
|
|
|
tm.mu.RLock()
|
|
|
|
defer tm.mu.RUnlock()
|
|
|
|
for _, tr := range tm._sortedSlice() {
|
2024-01-18 17:44:13 +01:00
|
|
|
out := tr.rcStats() // basic stats
|
2020-08-05 17:59:44 +02:00
|
|
|
if acc := progress.get(tr.remote); acc != nil {
|
2024-01-18 17:44:13 +01:00
|
|
|
acc.rcStats(out) // add extended stats if have acc
|
2020-08-05 17:59:44 +02:00
|
|
|
}
|
2024-01-18 17:44:13 +01:00
|
|
|
t = append(t, out)
|
2020-08-05 17:59:44 +02:00
|
|
|
}
|
|
|
|
return t
|
|
|
|
}
|