github.com/Racer159/jackal@v0.32.7-0.20240401174413-0bd2339e4f2e/src/pkg/utils/bytes.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: 2021-Present The Jackal Authors 3 // forked from https://www.socketloop.com/tutorials/golang-byte-format-example 4 5 // Package utils provides generic utility functions. 6 package utils 7 8 import ( 9 "fmt" 10 "math" 11 "strconv" 12 "time" 13 14 "github.com/Racer159/jackal/src/pkg/message" 15 "github.com/defenseunicorns/pkg/helpers" 16 ) 17 18 // RoundUp rounds a float64 to the given number of decimal places. 19 func RoundUp(input float64, places int) (newVal float64) { 20 var round float64 21 pow := math.Pow(10, float64(places)) 22 digit := pow * input 23 round = math.Ceil(digit) 24 newVal = round / pow 25 return 26 } 27 28 // ByteFormat formats a number of bytes into a human readable string. 29 func ByteFormat(inputNum float64, precision int) string { 30 if precision <= 0 { 31 precision = 1 32 } 33 34 var unit string 35 var returnVal float64 36 37 // https://www.techtarget.com/searchstorage/definition/mebibyte-MiB 38 if inputNum >= 1000000000 { 39 returnVal = RoundUp(inputNum/1000000000, precision) 40 unit = " GB" // gigabyte 41 } else if inputNum >= 1000000 { 42 returnVal = RoundUp(inputNum/1000000, precision) 43 unit = " MB" // megabyte 44 } else if inputNum >= 1000 { 45 returnVal = RoundUp(inputNum/1000, precision) 46 unit = " KB" // kilobyte 47 } else { 48 returnVal = inputNum 49 unit = " Byte" // byte 50 } 51 52 if returnVal > 1 { 53 unit += "s" 54 } 55 56 return strconv.FormatFloat(returnVal, 'f', precision, 64) + unit 57 } 58 59 // RenderProgressBarForLocalDirWrite creates a progress bar that continuously tracks the progress of writing files to a local directory and all of its subdirectories. 60 // NOTE: This function runs infinitely until either completeChan or errChan is triggered, this function should be run in a goroutine while a different thread/process is writing to the directory. 61 func RenderProgressBarForLocalDirWrite(filepath string, expectedTotal int64, completeChan chan error, updateText string, successText string) { 62 63 // Create a progress bar 64 title := fmt.Sprintf("%s (%s of %s)", updateText, ByteFormat(float64(0), 2), ByteFormat(float64(expectedTotal), 2)) 65 progressBar := message.NewProgressBar(expectedTotal, title) 66 67 for { 68 select { 69 case err := <-completeChan: 70 if err == nil { 71 // Send success message 72 progressBar.Successf("%s (%s)", successText, ByteFormat(float64(expectedTotal), 2)) 73 completeChan <- nil 74 return 75 } else { 76 progressBar.Stop() 77 completeChan <- nil 78 return 79 } 80 default: 81 // Read the directory size 82 currentBytes, dirErr := helpers.GetDirSize(filepath) 83 if dirErr != nil { 84 message.Debugf("unable to get updated progress: %s", dirErr.Error()) 85 time.Sleep(200 * time.Millisecond) 86 continue 87 } 88 89 // Update the progress bar with the current size 90 title := fmt.Sprintf("%s (%s of %s)", updateText, ByteFormat(float64(currentBytes), 2), ByteFormat(float64(expectedTotal), 2)) 91 progressBar.Update(currentBytes, title) 92 time.Sleep(200 * time.Millisecond) 93 } 94 } 95 }