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(Gray("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(Gray("[file]", w.INode.Path, " ~> ", childINode.Path)) w.Send <- StatusReport{path: childPath, size: size} case kind == DirType: Console.Debugln(Gray("[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(Gray(Red(report.err))) } if report.err == nil { w.INode.Size += report.size } case leaf := <-w.Done: Console.Debugln(Gray("Done:", leaf.path)) delete(w.Opened, leaf.path) } } Console.Debugln(Gray(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) { var size int64 pathINode, err := INodeFromPath(path) if err != nil { Console.Fatalln("Failed creating inode:", err) } Console.Debugln(Gray("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...")) size = 0 for { select { case res := <-result: size += res.size fmt.Fprintf(writer, "%s: %d\n%s: %d\n", res.path, res.size, path, size) case <-done: fmt.Fprintf(writer, "%s: %d\n", path, pathINode.Size) writer.Stop() return } } }