Compare commits

...

2 commits

Author SHA1 Message Date
d00a7fa2c0
Make the command stop when finished 2020-03-07 18:42:29 +01:00
6ccb16e7bd
Package the logic. Create cli entrypoint. 2020-03-05 14:32:50 +01:00
7 changed files with 260 additions and 155 deletions

View file

@ -4,6 +4,8 @@ import (
"flag" "flag"
"fmt" "fmt"
"time" "time"
"git.lattuga.net/blallo/ruspa/tree"
) )
type UnitValue struct { type UnitValue struct {
@ -35,28 +37,36 @@ func (u *UnitValue) Set(value string) error {
u.unit = "PB" u.unit = "PB"
return nil return nil
default: default:
return ErrUnknownUnit return tree.ErrUnknownUnit
} }
} }
func main() { func main() {
var path string var path string
var depth int var depth int
var root tree.Node
var unit = &UnitValue{unit: "KB"} var unit = &UnitValue{unit: "KB"}
flag.StringVar(&path, "path", ".", "Path from where to start the walk from") flag.StringVar(&path, "path", ".", "Path from where to start the walk from")
flag.IntVar(&depth, "depth", 0, "Depth to display") flag.IntVar(&depth, "depth", 0, "Depth to display")
flag.Var(unit, "unit", "Unit in which to report size") flag.Var(unit, "unit", "Unit in which to report size")
flag.Parse() flag.Parse()
t := NewTop(path) if depth == 0 || !tree.AnyDirectoryDownThere(path) {
t.SetUnit(unit.String()) root = tree.NewSingle(path)
go t.Spawn(depth) } else {
go t.Collect() root = tree.NewTop(path)
}
root.SetUnit(unit.String())
go root.Spawn(depth)
for { for {
select { select {
case <-time.After(500 * time.Millisecond): case <-time.After(500 * time.Millisecond):
fmt.Printf("\033[H\033[2J") fmt.Printf("\033[H\033[2J")
fmt.Println(t) fmt.Println(root)
if root.Complete() {
return
}
} }
} }
} }

View file

@ -1,4 +1,4 @@
package main package tree
import ( import (
"os" "os"

View file

@ -1,4 +1,4 @@
package main package tree
import ( import (
"io/ioutil" "io/ioutil"

View file

@ -1,4 +1,4 @@
package main package tree
import ( import (
"errors" "errors"
@ -14,17 +14,6 @@ const (
SingleType = "single" SingleType = "single"
) )
type ByteSize uint
const (
B ByteSize = 1 << (10 * iota)
KB
MB
GB
TB
PB
)
var ErrUnknownType = errors.New("unknown type") var ErrUnknownType = errors.New("unknown type")
var ErrShouldBeBottom = errors.New("should be of type Bottom") var ErrShouldBeBottom = errors.New("should be of type Bottom")
var ErrChannelError = errors.New("channel errored") var ErrChannelError = errors.New("channel errored")
@ -42,58 +31,10 @@ type Node interface {
Size() int64 Size() int64
Spawn(int) error Spawn(int) error
Collect() error Collect() error
Collector() chan int64 AddCollector(chan int64)
Level() int Level() int
} Name() string
Complete() bool
func isLastLevel(node Node, maxLevel int) bool {
return maxLevel-1 == node.Level()
}
func setUnit(unit string) ByteSize {
switch unit {
case "B":
return B
case "KB":
return KB
case "MB":
return MB
case "GB":
return GB
case "TB":
return TB
case "PB":
// I reckon this will never happen, but you never know
return PB
default:
panic(ErrUnknownUnit)
}
}
func fmtUnit(unit ByteSize) string {
switch unit {
case B:
return "B"
case KB:
return "KB"
case MB:
return "MB"
case GB:
return "GB"
case TB:
return "TB"
case PB:
// I reckon this will never happen, but you never know
return "PB"
default:
panic(ErrUnknownUnit)
}
}
func fmtSize(size int64, unit ByteSize) string {
var dimension float64
dimension = float64(size) / float64(unit)
return fmt.Sprintf("%.2f %s", dimension, fmtUnit(unit))
} }
////////////////////////////// //////////////////////////////
@ -105,13 +46,13 @@ type Top struct {
size int64 size int64
unit ByteSize unit ByteSize
tree []Node tree []Node
collect chan int64 collect []<-chan int64
level int level int
complete bool
} }
func NewTop(path string) *Top { func NewTop(path string) *Top {
t := &Top{path: path, level: 0, unit: KB} t := &Top{path: path, level: 0, unit: KB}
t.collect = make(chan int64)
return t return t
} }
@ -140,32 +81,34 @@ func (t *Top) Spawn(maxLevel int) error {
switch mode := info.Mode(); { switch mode := info.Mode(); {
case mode.IsDir(): case mode.IsDir():
var child Node var child Node
var collect chan int64
path := filepath.Join(t.path, info.Name()) path := filepath.Join(t.path, info.Name())
if isLastLevel(t, maxLevel) || !AnyDirectoryDownThere(path) { if isLastLevel(t, maxLevel) || !AnyDirectoryDownThere(path) {
child = t.newBottom(path) child, collect = t.newBottom(path)
} else { } else {
child = t.newIntermediate(path) child, collect = t.newIntermediate(path)
} }
t.AddCollector(collect)
go child.Spawn(maxLevel) go child.Spawn(maxLevel)
defer func() {
go child.Collect()
}()
case !mode.IsDir() && mode.IsRegular(): case !mode.IsDir() && mode.IsRegular():
t.collect <- info.Size() t.size += info.Size()
} }
} }
go t.Collect()
return nil return nil
} }
func (t *Top) Collect() error { func (t *Top) Collect() error {
for s := range t.collect { for s := range merge(t.collect) {
t.size += s t.size += s
} }
t.complete = true
return nil return nil
} }
func (t *Top) Collector() chan int64 { func (t *Top) AddCollector(collect chan int64) {
return t.collect t.collect = append(t.collect, collect)
} }
func (t *Top) Level() int { func (t *Top) Level() int {
@ -176,6 +119,15 @@ func (t *Top) Name() string {
return filepath.Base(t.path) return filepath.Base(t.path)
} }
func (t *Top) Complete() bool {
for _, child := range t.tree {
if !child.Complete() {
return false
}
}
return true
}
func (t *Top) String() string { func (t *Top) String() string {
var out string var out string
var lines []string var lines []string
@ -203,27 +155,29 @@ func (t *Top) addToTree(child Node) {
t.tree = append(t.tree, child) t.tree = append(t.tree, child)
} }
func (t *Top) newChild(path, kind string) Node { func (t *Top) newChild(path, kind string) (Node, chan int64) {
switch { switch {
case kind == IntermediateType: case kind == IntermediateType:
i := NewIntermediate(path, t) i, collect := NewIntermediate(path, t)
t.addToTree(i) t.addToTree(i)
return i return i, collect
case kind == BottomType: case kind == BottomType:
b := NewBottom(path, t) b, collect := NewBottom(path, t)
t.addToTree(b) t.addToTree(b)
return b return b, collect
default: default:
panic(ErrUnknownType) panic(ErrUnknownType)
} }
} }
func (t *Top) newIntermediate(path string) *Intermediate { func (t *Top) newIntermediate(path string) (*Intermediate, chan int64) {
return t.newChild(path, IntermediateType).(*Intermediate) child, collect := t.newChild(path, IntermediateType)
return child.(*Intermediate), collect
} }
func (t *Top) newBottom(path string) *Bottom { func (t *Top) newBottom(path string) (*Bottom, chan int64) {
return t.newChild(path, BottomType).(*Bottom) child, collect := t.newChild(path, BottomType)
return child.(*Bottom), collect
} }
////////////////////////////// //////////////////////////////
@ -236,17 +190,19 @@ type Intermediate struct {
unit ByteSize unit ByteSize
parent Node parent Node
tree []Node tree []Node
collect chan int64 collect []<-chan int64
refer chan int64 refer chan int64
level int level int
complete bool
} }
func NewIntermediate(path string, parent Node) *Intermediate { func NewIntermediate(path string, parent Node) (*Intermediate, chan int64) {
i := &Intermediate{path: path, refer: parent.Collector(), parent: parent} refer := make(chan int64)
i.collect = make(chan int64) i := &Intermediate{path: path, refer: refer, parent: parent}
i.collect = make([]<-chan int64, 1)
i.level = parent.Level() + 1 i.level = parent.Level() + 1
i.unit = parent.GetUnit() i.unit = parent.GetUnit()
return i return i, refer
} }
func (i *Intermediate) SetUnit(unit string) { func (i *Intermediate) SetUnit(unit string) {
@ -275,35 +231,36 @@ func (i *Intermediate) Spawn(maxLevel int) error {
switch mode := info.Mode(); { switch mode := info.Mode(); {
case mode.IsDir(): case mode.IsDir():
var child Node var child Node
var collect chan int64
path := filepath.Join(i.path, info.Name()) path := filepath.Join(i.path, info.Name())
if isLastLevel(i, maxLevel) || !AnyDirectoryDownThere(path) { if isLastLevel(i, maxLevel) || !AnyDirectoryDownThere(path) {
child = i.newBottom(path) child, collect = i.newBottom(path)
} else { } else {
child = i.newIntermediate(path) child, collect = i.newIntermediate(path)
} }
i.AddCollector(collect)
go child.Spawn(maxLevel) go child.Spawn(maxLevel)
defer func() {
go child.Collect()
}()
case !mode.IsDir() && mode.IsRegular(): case !mode.IsDir() && mode.IsRegular():
i.collect <- info.Size() i.size += info.Size()
i.refer <- info.Size()
} }
} }
go i.Collect()
return nil return nil
} }
func (i *Intermediate) Collect() error { func (i *Intermediate) Collect() error {
for s := range i.collect { for s := range merge(i.collect) {
i.size += s i.size += s
i.refer <- s i.refer <- s
} }
close(i.refer) i.complete = true
return nil return nil
} }
func (i *Intermediate) Collector() chan int64 { func (i *Intermediate) AddCollector(collect chan int64) {
return i.collect i.collect = append(i.collect, collect)
} }
func (i *Intermediate) Level() int { func (i *Intermediate) Level() int {
@ -314,6 +271,15 @@ func (i *Intermediate) Name() string {
return filepath.Base(i.path) return filepath.Base(i.path)
} }
func (i *Intermediate) Complete() bool {
for _, child := range i.tree {
if !child.Complete() {
return false
}
}
return true
}
func (i *Intermediate) String() string { func (i *Intermediate) String() string {
var lines []string var lines []string
out := fmt.Sprintf("(%s) %s\n", fmtSize(i.Size(), i.GetUnit()), i.Name()) out := fmt.Sprintf("(%s) %s\n", fmtSize(i.Size(), i.GetUnit()), i.Name())
@ -348,27 +314,29 @@ func (i *Intermediate) addToTree(child Node) {
i.tree = append(i.tree, child) i.tree = append(i.tree, child)
} }
func (i *Intermediate) newChild(path, kind string) Node { func (i *Intermediate) newChild(path, kind string) (Node, chan int64) {
switch { switch {
case kind == IntermediateType: case kind == IntermediateType:
c := NewIntermediate(path, i) c, collect := NewIntermediate(path, i)
i.addToTree(c) i.addToTree(c)
return c return c, collect
case kind == BottomType: case kind == BottomType:
b := NewBottom(path, i) b, collect := NewBottom(path, i)
i.addToTree(b) i.addToTree(b)
return b return b, collect
default: default:
panic(ErrUnknownType) panic(ErrUnknownType)
} }
} }
func (i *Intermediate) newIntermediate(path string) *Intermediate { func (i *Intermediate) newIntermediate(path string) (*Intermediate, chan int64) {
return i.newChild(path, IntermediateType).(*Intermediate) child, collect := i.newChild(path, IntermediateType)
return child.(*Intermediate), collect
} }
func (i *Intermediate) newBottom(path string) *Bottom { func (i *Intermediate) newBottom(path string) (*Bottom, chan int64) {
return i.newChild(path, BottomType).(*Bottom) child, collect := i.newChild(path, BottomType)
return child.(*Bottom), collect
} }
////////////////////////////// //////////////////////////////
@ -384,14 +352,15 @@ type Bottom struct {
collect chan int64 collect chan int64
refer chan int64 refer chan int64
level int level int
complete bool
} }
func NewBottom(path string, parent Node) *Bottom { func NewBottom(path string, parent Node) (*Bottom, chan int64) {
b := &Bottom{path: path, refer: parent.Collector(), parent: parent, unit: parent.GetUnit()} refer := make(chan int64)
b.collect = make(chan int64) b := &Bottom{path: path, refer: refer, parent: parent, unit: parent.GetUnit()}
b.level = parent.Level() + 1 b.level = parent.Level() + 1
b.unit = parent.GetUnit() b.unit = parent.GetUnit()
return b return b, refer
} }
func (b *Bottom) SetUnit(unit string) { func (b *Bottom) SetUnit(unit string) {
@ -407,8 +376,11 @@ func (b *Bottom) Size() int64 {
} }
func (b *Bottom) Spawn(maxLevel int) error { func (b *Bottom) Spawn(maxLevel int) error {
b.walker = NewRealWalker(b.path, b.collect) collect := make(chan int64)
b.AddCollector(collect)
b.walker = NewRealWalker(b.path, collect)
go b.walker.Walk() go b.walker.Walk()
go b.Collect()
return nil return nil
} }
@ -417,12 +389,12 @@ func (b *Bottom) Collect() error {
b.size += s b.size += s
b.refer <- s b.refer <- s
} }
close(b.refer) b.complete = true
return nil return nil
} }
func (b *Bottom) Collector() chan int64 { func (b *Bottom) AddCollector(collect chan int64) {
return b.collect b.collect = collect
} }
func (b *Bottom) Level() int { func (b *Bottom) Level() int {
@ -433,6 +405,10 @@ func (b *Bottom) Name() string {
return filepath.Base(b.path) return filepath.Base(b.path)
} }
func (b *Bottom) Complete() bool {
return b.complete
}
func (b *Bottom) String() string { func (b *Bottom) String() string {
return fmt.Sprintf("(%s) %s", fmtSize(b.Size(), b.GetUnit()), b.Name()) return fmt.Sprintf("(%s) %s", fmtSize(b.Size(), b.GetUnit()), b.Name())
} }
@ -444,8 +420,25 @@ func (b *Bottom) String() string {
type Single struct { type Single struct {
path string path string
size int64 size int64
unit ByteSize
walker Walker walker Walker
collect chan int64 collect chan int64
complete bool
}
func NewSingle(path string) *Single {
s := &Single{path: path}
collect := make(chan int64)
s.AddCollector(collect)
return s
}
func (s *Single) SetUnit(unit string) {
s.unit = setUnit(unit)
}
func (s *Single) GetUnit() ByteSize {
return s.unit
} }
func (s *Single) Size() int64 { func (s *Single) Size() int64 {
@ -455,6 +448,7 @@ func (s *Single) Size() int64 {
func (s *Single) Spawn(maxLevel int) error { func (s *Single) Spawn(maxLevel int) error {
s.walker = NewRealWalker(s.path, s.collect) s.walker = NewRealWalker(s.path, s.collect)
go s.walker.Walk() go s.walker.Walk()
go s.Collect()
return nil return nil
} }
@ -462,21 +456,28 @@ func (s *Single) Collect() error {
for v := range s.collect { for v := range s.collect {
s.size += v s.size += v
} }
s.complete = true
return nil return nil
} }
func (s *Single) Collector() chan int64 { func (s *Single) AddCollector(collect chan int64) {
return s.collect s.collect = collect
} }
func (s *Single) Level() int { func (s *Single) Level() int {
return 0 return 0
} }
func NewSingle(path string) *Single { func (s *Single) Name() string {
s := &Single{path: path} return filepath.Base(s.path)
s.collect = make(chan int64) }
return s
func (s *Single) Complete() bool {
return s.complete
}
func (s *Single) String() string {
return fmt.Sprintf("(%s) %s\n", fmtSize(s.Size(), s.GetUnit()), s.Name())
} }
////////////////////////////// //////////////////////////////

View file

@ -1,4 +1,4 @@
package main package tree
import ( import (
"fmt" "fmt"

View file

@ -1,4 +1,4 @@
package main package tree
import ( import (
"os" "os"
@ -23,6 +23,7 @@ func (r *RealWalker) walkFunc(path string, info os.FileInfo, err error) error {
func (r *RealWalker) Walk() { func (r *RealWalker) Walk() {
filepath.Walk(r.path, r.walkFunc) filepath.Walk(r.path, r.walkFunc)
close(r.report)
} }
func NewRealWalker(path string, report chan int64) *RealWalker { func NewRealWalker(path string, report chan int64) *RealWalker {

93
tree/tree_utils.go Normal file
View file

@ -0,0 +1,93 @@
package tree
import (
"fmt"
"sync"
)
type ByteSize uint
const (
B ByteSize = 1 << (10 * iota)
KB
MB
GB
TB
PB
)
func isLastLevel(node Node, maxLevel int) bool {
return maxLevel-1 == node.Level()
}
func setUnit(unit string) ByteSize {
switch unit {
case "B":
return B
case "KB":
return KB
case "MB":
return MB
case "GB":
return GB
case "TB":
return TB
case "PB":
// I reckon this will never happen, but you never know
return PB
default:
panic(ErrUnknownUnit)
}
}
func fmtUnit(unit ByteSize) string {
switch unit {
case B:
return "B"
case KB:
return "KB"
case MB:
return "MB"
case GB:
return "GB"
case TB:
return "TB"
case PB:
// I reckon this will never happen, but you never know
return "PB"
default:
panic(ErrUnknownUnit)
}
}
func fmtSize(size int64, unit ByteSize) string {
var dimension float64
dimension = float64(size) / float64(unit)
return fmt.Sprintf("%.2f %s", dimension, fmtUnit(unit))
}
func merge(cs []<-chan int64) <-chan int64 {
var wg sync.WaitGroup
out := make(chan int64)
// Start an output goroutine for each input channel in cs. output
// copies values from c to out until c is closed, then calls wg.Done.
output := func(c <-chan int64) {
for n := range c {
out <- n
}
wg.Done()
}
wg.Add(len(cs))
for _, c := range cs {
go output(c)
}
// Start a goroutine to close out once all the output goroutines are
// done. This must start after the wg.Add call.
go func() {
wg.Wait()
close(out)
}()
return out
}