gitlab.com/SkynetLabs/skyd@v1.6.9/cmd/skyc/helpers.go (about)

     1  package main
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"io"
     7  	"log"
     8  	"os"
     9  	"path/filepath"
    10  	"sort"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/vbauerster/mpb/v5"
    15  	"github.com/vbauerster/mpb/v5/decor"
    16  	"gitlab.com/SkynetLabs/skyd/skykey"
    17  	"gitlab.com/SkynetLabs/skyd/skymodules"
    18  	"go.sia.tech/siad/node/api"
    19  	"go.sia.tech/siad/types"
    20  )
    21  
    22  // abs returns the absolute representation of a path.
    23  // TODO: bad things can happen if you run siac from a non-existent directory.
    24  // Implement some checks to catch this problem.
    25  func abs(path string) string {
    26  	abspath, err := filepath.Abs(path)
    27  	if err != nil {
    28  		return path
    29  	}
    30  	return abspath
    31  }
    32  
    33  // absDuration is a small helper function that sanitizes the output for the
    34  // given time duration. If the duration is less than 0 it will return 0,
    35  // otherwise it will return the duration rounded to the nearest second.
    36  func absDuration(t time.Duration) time.Duration {
    37  	if t <= 0 {
    38  		return 0
    39  	}
    40  	return t.Round(time.Second)
    41  }
    42  
    43  // askForConfirmation prints a question and waits for confirmation until the
    44  // user gives a valid answer ("y", "yes", "n", "no" with any capitalization).
    45  func askForConfirmation(s string) bool {
    46  	r := bufio.NewReader(os.Stdin)
    47  	for {
    48  		fmt.Printf("%s [y/n]: ", s)
    49  		answer, err := r.ReadString('\n')
    50  		if err != nil {
    51  			log.Fatal(err)
    52  		}
    53  		answer = strings.ToLower(strings.TrimSpace(answer))
    54  		if answer == "y" || answer == "yes" {
    55  			return true
    56  		} else if answer == "n" || answer == "no" {
    57  			return false
    58  		}
    59  	}
    60  }
    61  
    62  // calculateAverageUint64 calculates the average of a uint64 slice and returns the average as a uint64
    63  func calculateAverageUint64(input []uint64) uint64 {
    64  	total := uint64(0)
    65  	if len(input) == 0 {
    66  		return 0
    67  	}
    68  	for _, v := range input {
    69  		total += v
    70  	}
    71  	return total / uint64(len(input))
    72  }
    73  
    74  // calculateMedianUint64 calculates the median of a uint64 slice and returns the median as a uint64
    75  func calculateMedianUint64(mm []uint64) uint64 {
    76  	sort.Slice(mm, func(i, j int) bool { return mm[i] < mm[j] }) // sort the numbers
    77  
    78  	mNumber := len(mm) / 2
    79  
    80  	if len(mm)%2 == 0 {
    81  		return mm[mNumber]
    82  	}
    83  
    84  	return (mm[mNumber-1] + mm[mNumber]) / 2
    85  }
    86  
    87  func fileExists(filename string) bool {
    88  	info, err := os.Stat(filename)
    89  	if os.IsNotExist(err) {
    90  		return false
    91  	}
    92  	return !info.IsDir()
    93  }
    94  
    95  // estimatedHeightAt returns the estimated block height for the given time.
    96  // Block height is estimated by calculating the minutes since a known block in
    97  // the past and dividing by 10 minutes (the block time).
    98  func estimatedHeightAt(t time.Time, cg api.ConsensusGET) types.BlockHeight {
    99  	gt := cg.GenesisTimestamp
   100  	bf := cg.BlockFrequency
   101  	return types.BlockHeight(types.Timestamp(t.Unix())-gt) / bf
   102  }
   103  
   104  // newProgressReader is a helper method for adding a new progress bar to an
   105  // existing *mpb.Progress object.
   106  func newProgressReader(pbs *mpb.Progress, size int64, filename string, file io.Reader) (*mpb.Bar, io.ReadCloser) {
   107  	bar := pbs.AddBar(
   108  		size,
   109  		mpb.PrependDecorators(
   110  			decor.Name(pBarJobUpload, decor.WC{W: 10}),
   111  			decor.Percentage(decor.WC{W: 6}),
   112  		),
   113  		mpb.AppendDecorators(
   114  			decor.Name(filename, decor.WC{W: len(filename) + 1, C: decor.DidentRight}),
   115  		),
   116  	)
   117  	return bar, bar.ProxyReader(file)
   118  }
   119  
   120  // newProgressSpinner creates a spinner that is queued after `afterBar` is
   121  // complete.
   122  func newProgressSpinner(pbs *mpb.Progress, afterBar *mpb.Bar, filename string) *mpb.Bar {
   123  	return pbs.AddSpinner(
   124  		1,
   125  		mpb.SpinnerOnMiddle,
   126  		mpb.SpinnerStyle([]string{"∙∙∙", "●∙∙", "∙●∙", "∙∙●", "∙∙∙"}),
   127  		mpb.BarQueueAfter(afterBar),
   128  		mpb.BarFillerClearOnComplete(),
   129  		mpb.PrependDecorators(
   130  			decor.OnComplete(decor.Name(pBarJobProcess, decor.WC{W: 10}), pBarJobDone),
   131  			decor.Name("", decor.WC{W: 6, C: decor.DidentRight}),
   132  		),
   133  		mpb.AppendDecorators(
   134  			decor.Name(filename, decor.WC{W: len(filename) + 1, C: decor.DidentRight}),
   135  		),
   136  	)
   137  }
   138  
   139  // parseAndAddSkykey is a helper that parses any supplied skykey and adds it to
   140  // the SkyfileUploadParameters
   141  func parseAndAddSkykey(sup skymodules.SkyfileUploadParameters) skymodules.SkyfileUploadParameters {
   142  	if skykeyName != "" && skykeyID != "" {
   143  		die("Can only use either skykeyname or skykeyid flag, not both.")
   144  	}
   145  	// Set Encrypt param to true if a skykey ID or name is set.
   146  	if skykeyName != "" {
   147  		sup.SkykeyName = skykeyName
   148  	} else if skykeyID != "" {
   149  		var ID skykey.SkykeyID
   150  		err := ID.FromString(skykeyID)
   151  		if err != nil {
   152  			die("Unable to parse skykey ID")
   153  		}
   154  		sup.SkykeyID = ID
   155  	}
   156  	return sup
   157  }
   158  
   159  // sanitizeErr is a small helper function that sanitizes the output for the
   160  // given error string. It will print "-", if the error string is the equivalent
   161  // of a nil error.
   162  func sanitizeErr(errStr string) string {
   163  	if errStr == "" {
   164  		return "-"
   165  	}
   166  	if !verbose && len(errStr) > truncateErrLength {
   167  		errStr = errStr[:truncateErrLength] + "..."
   168  	}
   169  	return errStr
   170  }
   171  
   172  // sanitizeTime is a small helper function that sanitizes the output for the
   173  // given time. If the given 'cond' value is false, it will print "-", if it is
   174  // true it will print the time in a predefined format.
   175  func sanitizeTime(t time.Time, cond bool) string {
   176  	if !cond {
   177  		return "-"
   178  	}
   179  	return fmt.Sprintf("%v", t.Format(time.RFC3339))
   180  }
   181  
   182  // validateSkyKeyNameAndIDUsage validates the usage of name and ID, ensuring
   183  // that only one is used.
   184  func validateSkyKeyNameAndIDUsage(name, id string) error {
   185  	if name == "" && id == "" {
   186  		return errNeitherNameNorIDUsed
   187  	}
   188  	if name != "" && id != "" {
   189  		return errBothNameAndIDUsed
   190  	}
   191  	return nil
   192  }