|
@@ -0,0 +1,121 @@
|
|
|
+package sizer
|
|
|
+
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+
|
|
|
+ "github.com/gosuri/uilive"
|
|
|
+)
|
|
|
+
|
|
|
+// Ruspa is the asynchronous worker that is in charge of collecting
|
|
|
+// the underlying sizes and relay the total to the eventual parent.
|
|
|
+type Ruspa struct {
|
|
|
+ INode *INode
|
|
|
+ Send chan StatusReport
|
|
|
+ Recv chan StatusReport
|
|
|
+ Done chan StatusReport
|
|
|
+ Opened map[string]*Ruspa
|
|
|
+}
|
|
|
+
|
|
|
+// NewRuspa inits a new Ruspa
|
|
|
+func NewRuspa(inode *INode, collect chan StatusReport) *Ruspa {
|
|
|
+ var r Ruspa
|
|
|
+ r.INode = inode
|
|
|
+ r.Recv = make(chan StatusReport)
|
|
|
+ if collect == nil {
|
|
|
+ r.Send = make(chan StatusReport)
|
|
|
+ } else {
|
|
|
+ r.Send = collect
|
|
|
+ }
|
|
|
+ r.Done = make(chan StatusReport)
|
|
|
+ r.Opened = make(map[string]*Ruspa)
|
|
|
+ return &r
|
|
|
+}
|
|
|
+
|
|
|
+// Scava starts a worker on the given INode.
|
|
|
+func (w *Ruspa) Scava(done chan StatusReport) {
|
|
|
+ Console.Debugln("Scava: ", w.INode.Path)
|
|
|
+ children, err := ls(w.INode.Path)
|
|
|
+ if err != nil {
|
|
|
+ w.Send <- StatusReport{path: w.INode.Path, err: err}
|
|
|
+ }
|
|
|
+ for _, childPath := range children {
|
|
|
+ kind, size, err := IdentifyType(childPath)
|
|
|
+ if err != nil {
|
|
|
+ w.Send <- StatusReport{path: childPath, err: err}
|
|
|
+ }
|
|
|
+ childINode := NewINode(kind, size, childPath)
|
|
|
+ w.INode.Children.Append(childINode)
|
|
|
+ switch {
|
|
|
+ case kind == FileType:
|
|
|
+ Console.Debugln("[file]", w.INode.Path, " ~> ", childINode.Path)
|
|
|
+ w.Send <- StatusReport{path: childPath, size: size}
|
|
|
+ case kind == DirType:
|
|
|
+ Console.Debugln("[dir]", w.INode.Path, " ~> ", childINode.Path)
|
|
|
+ cw := NewRuspa(childINode, w.Recv)
|
|
|
+ w.Opened[childPath] = cw
|
|
|
+ go cw.Scava(w.Done)
|
|
|
+ // go cw.Ammucchia(w.Done)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if len(w.Opened) == 0 {
|
|
|
+ done <- StatusReport{path: w.INode.Path}
|
|
|
+ } else {
|
|
|
+ go w.Ammucchia(done)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Ammucchia gathers the size of the underlying Ruspa workers
|
|
|
+// and adds to the INode size.
|
|
|
+func (w *Ruspa) Ammucchia(done chan StatusReport) {
|
|
|
+ for len(w.Opened) > 0 {
|
|
|
+ select {
|
|
|
+ case report := <-w.Recv:
|
|
|
+ if report.err != nil {
|
|
|
+ Console.Debugln(Red(report.err))
|
|
|
+ }
|
|
|
+ if report.err == nil {
|
|
|
+ w.INode.Size += report.size
|
|
|
+ }
|
|
|
+ case leaf := <-w.Done:
|
|
|
+ Console.Debugln("Done:", leaf.path)
|
|
|
+ delete(w.Opened, leaf.path)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Console.Debugln(w.INode.Path, ": Completed. Exiting...")
|
|
|
+ done <- StatusReport{path: w.INode.Path, size: w.INode.Size}
|
|
|
+}
|
|
|
+
|
|
|
+// Pesa starts the worker for current inode and returns the channel
|
|
|
+// to listen to get the size.
|
|
|
+func (i *INode) Pesa(done chan StatusReport) chan StatusReport {
|
|
|
+ result := make(chan StatusReport)
|
|
|
+ w := NewRuspa(i, result)
|
|
|
+ go w.Scava(done)
|
|
|
+ // go w.Ammucchia(done)
|
|
|
+ return result
|
|
|
+}
|
|
|
+
|
|
|
+// NastroConvogliatore starts a Pesa on a given path and displays
|
|
|
+// the current value of the size.
|
|
|
+func NastroConvogliatore(path string) {
|
|
|
+ pathINode, err := INodeFromPath(path)
|
|
|
+ if err != nil {
|
|
|
+ Console.Fatalln("Failed creating inode:", err)
|
|
|
+ }
|
|
|
+ Console.Debugln("Starting Pesa on path:", path)
|
|
|
+ done := make(chan StatusReport)
|
|
|
+ result := pathINode.Pesa(done)
|
|
|
+ writer := uilive.New()
|
|
|
+ writer.Start()
|
|
|
+ fmt.Fprintf(writer, "%s\n", Green("Starting..."))
|
|
|
+ for {
|
|
|
+ select {
|
|
|
+ case res := <-result:
|
|
|
+ fmt.Fprintf(writer, "%s: %d\n", res.path, res.size)
|
|
|
+ case res := <-done:
|
|
|
+ fmt.Fprintf(writer, "%s: %d\n", res.path, res.size)
|
|
|
+ writer.Stop()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|