worker.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. package sizer
  2. import (
  3. "fmt"
  4. "github.com/gosuri/uilive"
  5. )
  6. // Ruspa is the asynchronous worker that is in charge of collecting
  7. // the underlying sizes and relay the total to the eventual parent.
  8. type Ruspa struct {
  9. INode *INode
  10. Send chan StatusReport
  11. Recv chan StatusReport
  12. Done chan StatusReport
  13. Opened map[string]*Ruspa
  14. }
  15. // NewRuspa inits a new Ruspa
  16. func NewRuspa(inode *INode, collect chan StatusReport) *Ruspa {
  17. var r Ruspa
  18. r.INode = inode
  19. r.Recv = make(chan StatusReport)
  20. if collect == nil {
  21. r.Send = make(chan StatusReport)
  22. } else {
  23. r.Send = collect
  24. }
  25. r.Done = make(chan StatusReport)
  26. r.Opened = make(map[string]*Ruspa)
  27. return &r
  28. }
  29. // Scava starts a worker on the given INode.
  30. func (w *Ruspa) Scava(done chan StatusReport) {
  31. Console.Debugln("Scava: ", w.INode.Path)
  32. children, err := ls(w.INode.Path)
  33. if err != nil {
  34. w.Send <- StatusReport{path: w.INode.Path, err: err}
  35. }
  36. for _, childPath := range children {
  37. kind, size, err := IdentifyType(childPath)
  38. if err != nil {
  39. w.Send <- StatusReport{path: childPath, err: err}
  40. }
  41. childINode := NewINode(kind, size, childPath)
  42. w.INode.Children.Append(childINode)
  43. switch {
  44. case kind == FileType:
  45. Console.Debugln("[file]", w.INode.Path, " ~> ", childINode.Path)
  46. w.Send <- StatusReport{path: childPath, size: size}
  47. case kind == DirType:
  48. Console.Debugln("[dir]", w.INode.Path, " ~> ", childINode.Path)
  49. cw := NewRuspa(childINode, w.Recv)
  50. w.Opened[childPath] = cw
  51. go cw.Scava(w.Done)
  52. // go cw.Ammucchia(w.Done)
  53. }
  54. }
  55. if len(w.Opened) == 0 {
  56. done <- StatusReport{path: w.INode.Path}
  57. } else {
  58. go w.Ammucchia(done)
  59. }
  60. }
  61. // Ammucchia gathers the size of the underlying Ruspa workers
  62. // and adds to the INode size.
  63. func (w *Ruspa) Ammucchia(done chan StatusReport) {
  64. for len(w.Opened) > 0 {
  65. select {
  66. case report := <-w.Recv:
  67. if report.err != nil {
  68. Console.Debugln(Red(report.err))
  69. }
  70. if report.err == nil {
  71. w.INode.Size += report.size
  72. }
  73. case leaf := <-w.Done:
  74. Console.Debugln("Done:", leaf.path)
  75. delete(w.Opened, leaf.path)
  76. }
  77. }
  78. Console.Debugln(w.INode.Path, ": Completed. Exiting...")
  79. done <- StatusReport{path: w.INode.Path, size: w.INode.Size}
  80. }
  81. // Pesa starts the worker for current inode and returns the channel
  82. // to listen to get the size.
  83. func (i *INode) Pesa(done chan StatusReport) chan StatusReport {
  84. result := make(chan StatusReport)
  85. w := NewRuspa(i, result)
  86. go w.Scava(done)
  87. // go w.Ammucchia(done)
  88. return result
  89. }
  90. // NastroConvogliatore starts a Pesa on a given path and displays
  91. // the current value of the size.
  92. func NastroConvogliatore(path string) {
  93. pathINode, err := INodeFromPath(path)
  94. if err != nil {
  95. Console.Fatalln("Failed creating inode:", err)
  96. }
  97. Console.Debugln("Starting Pesa on path:", path)
  98. done := make(chan StatusReport)
  99. result := pathINode.Pesa(done)
  100. writer := uilive.New()
  101. writer.Start()
  102. fmt.Fprintf(writer, "%s\n", Green("Starting..."))
  103. for {
  104. select {
  105. case res := <-result:
  106. fmt.Fprintf(writer, "%s: %d\n", res.path, res.size)
  107. case res := <-done:
  108. fmt.Fprintf(writer, "%s: %d\n", res.path, res.size)
  109. writer.Stop()
  110. return
  111. }
  112. }
  113. }