123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- package sizer
- import (
- "bufio"
- "fmt"
- "io"
- "os"
- )
- type dummy struct{}
- func (d dummy) Write(p []byte) (n int, err error) {
- return 0, nil
- }
- // Trivella provides an io.Writer with a notion of depth in the
- // path graph
- type Trivella struct {
- Level uint
- Writer io.Writer
- }
- // Scendi increases the depth in the graph
- func (t *Trivella) Scendi() *Trivella {
- var v *Trivella
- if t.Level > 0 {
- v = TrInit(t.Writer, t.Level-1)
- } else {
- var d dummy
- v = TrInit(d, 0)
- }
- return v
- }
- // TrInit assigns the writer to a Trivella
- func TrInit(writer io.Writer, level uint) *Trivella {
- return &Trivella{Writer: writer, Level: level}
- }
- // 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
- ReportIn chan StatusReport
- ReportOut chan StatusReport
- DoneIn chan StatusReport
- DoneOut chan StatusReport
- Opened map[string]*Ruspa
- }
- // NewRuspa inits a new Ruspa
- func NewRuspa(inode *INode, reportOut, doneOut chan StatusReport) *Ruspa {
- var r Ruspa
- r.INode = inode
- r.ReportOut = reportOut
- r.DoneOut = doneOut
- r.ReportIn = make(chan StatusReport)
- r.DoneIn = make(chan StatusReport)
- r.Opened = make(map[string]*Ruspa)
- return &r
- }
- // Ammucchia gathers the size of the underlying INode(s) from the Ruspa worker
- // and adds to the INode size.
- func (w *Ruspa) Ammucchia(t *Trivella) {
- Console.Debugln(Gray("Ammucchia:", w.INode.Path))
- for len(w.Opened) > 0 {
- select {
- case report := <-w.ReportIn:
- Console.Debugln(Gray("ReportIn:", report))
- if report.err == nil {
- w.INode.Size += report.size
- fmt.Fprintf(t.Writer, "%s: %d\n", w.INode.Path, w.INode.Size)
- } else {
- Console.Debugln(Red(report.err))
- }
- case leaf := <-w.DoneIn:
- Console.Debugln(Gray("Done:", leaf.path))
- delete(w.Opened, leaf.path)
- }
- }
- Console.Debugln(Gray(w.INode.Path, ": Completed. Exiting..."))
- w.DoneOut <- StatusReport{path: w.INode.Path, size: w.INode.Size}
- }
- // Scava starts a worker on the given INode.
- func (w *Ruspa) Scava(t *Trivella) {
- Console.Debugln(Gray("Scava: ", w.INode.Path))
- children, err := ls(w.INode.Path)
- if err != nil {
- w.ReportOut <- StatusReport{path: w.INode.Path, err: err}
- }
- for _, childPath := range children {
- kind, size, err := IdentifyType(childPath)
- if err != nil {
- w.ReportOut <- 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.ReportOut <- StatusReport{path: childPath, size: size}
- case kind == DirType:
- Console.Debugln(Gray("[dir]", w.INode.Path, " ~> ", childINode.Path))
- w.INode.Size += size
- cw := NewRuspa(childINode, w.ReportIn, w.DoneIn)
- w.Opened[childPath] = cw
- go cw.Scava(t.Scendi())
- }
- }
- if len(w.Opened) != 0 {
- Console.Debugln(Gray("Spawning Ammucchia:", w.INode.Path))
- go w.Ammucchia(t)
- } else {
- w.DoneOut <- 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(t *Trivella, report, done chan StatusReport) {
- w := NewRuspa(i, report, done)
- go w.Scava(t)
- }
- // NastroConvogliatore starts a Pesa on a given path and displays
- // the current value of the size.
- func NastroConvogliatore(path string, depth uint) {
- finished := false
- pathINode, err := INodeFromPath(path)
- if err != nil {
- Console.Fatalln("Failed creating inode:", err)
- }
- Console.Debugln(Gray("Starting Pesa on path:", path))
- report := make(chan StatusReport)
- done := make(chan StatusReport)
- writer := bufio.NewWriter(os.Stdout)
- defer writer.Flush()
- t := TrInit(writer, depth)
- fmt.Fprintf(writer, "%s\n", Green("Starting..."))
- pathINode.Pesa(t, report, done)
- for !finished {
- select {
- case res := <-report:
- Console.Debugln(Gray("MAIN: result =>", res))
- case res := <-done:
- Console.Debugln(Gray("MAIN: done =>", res))
- finished = true
- }
- }
- fmt.Fprintf(writer, "%s: %d\n", path, pathINode.Size)
- return
- }
|