worker.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  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(Gray("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(Gray("[file]", w.INode.Path, " ~> ", childINode.Path))
  46. w.Send <- StatusReport{path: childPath, size: size}
  47. case kind == DirType:
  48. Console.Debugln(Gray("[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(Gray(Red(report.err)))
  69. }
  70. if report.err == nil {
  71. w.INode.Size += report.size
  72. }
  73. case leaf := <-w.Done:
  74. Console.Debugln(Gray("Done:", leaf.path))
  75. delete(w.Opened, leaf.path)
  76. }
  77. }
  78. Console.Debugln(Gray(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. var size int64
  94. pathINode, err := INodeFromPath(path)
  95. if err != nil {
  96. Console.Fatalln("Failed creating inode:", err)
  97. }
  98. Console.Debugln(Gray("Starting Pesa on path:", path))
  99. done := make(chan StatusReport)
  100. result := pathINode.Pesa(done)
  101. writer := uilive.New()
  102. writer.Start()
  103. fmt.Fprintf(writer, "%s\n", Green("Starting..."))
  104. size = 0
  105. for {
  106. select {
  107. case res := <-result:
  108. size += res.size
  109. fmt.Fprintf(writer, "%s: %d\n%s: %d\n", res.path, res.size, path, size)
  110. case <-done:
  111. fmt.Fprintf(writer, "%s: %d\n", path, pathINode.Size)
  112. writer.Stop()
  113. return
  114. }
  115. }
  116. }