git.sophuwu.com > byterate   
              162
            
             package main

import (
	"fmt"
	"os"
	"sophuwu.site/byterate"
	"strings"
	"time"
)

type inputs struct {
	nums []string
	help bool
	dur  bool
	time bool
}

func parseArgs(args []string) (i inputs, err error) {
	opts := ""
	for _, arg := range args {
		if strings.HasPrefix(arg, "--") {
			opts += arg[2:3]
		} else if strings.HasPrefix(arg, "-") {
			opts += arg[1:]
		} else {
			i.nums = append(i.nums, arg)
		}
	}
	for _, opt := range opts {
		switch opt {
		case 'h':
			i.help = true
		case 'd':
			i.dur = true
		case 't':
			i.time = true
		}
	}
	if !i.dur && !i.time {
		i.dur = true
	}
	if len(i.nums) != 2 {
		err = fmt.Errorf("expected 2 arguments, got %d", len(i.nums))
	}
	return
}

func tailS(n int) string {
	if n == 1 {
		return ""
	}
	return "s"
}

func main() {
	var (
		s, r byterate.Size
		t    time.Time
		d    time.Duration
		i    inputs
		err  error
		prnt string
	)

	i, err = parseArgs(os.Args[1:])

	if i.help {
		fmt.Println(strings.ReplaceAll(helpText, "{{ name }}", os.Args[0]))
		return
	}

	if err != nil {
		goto bad
	}

	s, err = byterate.ParseSize(i.nums[0])
	if err != nil {
		goto bad
	}

	r, err = byterate.ParseSize(i.nums[1])
	if err != nil {
		goto bad
	}

	t, d, err = byterate.Time(s, r)
	if err != nil {
		goto bad
	}

	if i.dur {
		if d.Hours() > 23 {
			days := int(d.Hours() / 24)
			if days > 365 {
				years := days / 365
				prnt = fmt.Sprintf("%d year%s, ", years, tailS(years))
				days %= 365
			}
			hrs := int(d.Hours()) % 24
			prnt += fmt.Sprintf("%d day%s, %02d hour%s, ", days, tailS(days), hrs, tailS(hrs))
		} else if d.Hours() >= 1 {
			prnt = fmt.Sprintf("%02d hour%s, ", int(d.Hours()), tailS(int(d.Hours())))
		}
		mins := int(d.Minutes()) % 60
		if prnt != "" || mins > 0 {
			prnt += fmt.Sprintf("%02d minute%s and ", mins, tailS(mins))
		}
		secs := int(d.Seconds()) % 60
		prnt += fmt.Sprintf("%02d second%s", secs, tailS(secs))
		fmt.Println(prnt)
	}
	if i.time {
		prnt = "Today "
		ndate := time.Now().Truncate(time.Hour * 24)
		tdate := t.Truncate(time.Hour * 24)
		if !tdate.Equal(ndate) {
			if tdate.Equal(ndate.AddDate(0, 0, 1)) {
				prnt = "Tomorrow "
			} else if t.Before(ndate.AddDate(0, 0, 6)) {
				prnt = t.Weekday().String() + " at " + prnt
			} else if t.Year() == ndate.Year() {
				prnt = "02 Jan "
			} else {
				prnt = "02 Jan 2006 "
			}
		}
		fmt.Println(t.Format(prnt + "15:04"))
	}
	return

bad:
	fmt.Printf("Usage: %s <size> <rate>\n", os.Args[0])
	fmt.Println("\t--help for more information")
}

const helpText = `{{ name }}:
	Print the time it takes to transfer a file at a given rate.

	Usage: {{ name }} [options] <size> <rate>

	Options:
		-h	--help
			Show this help message.
		-d	--duration
			Print the duration the transfer will take.
		-t	--time
			Print the time the transfer will finish if started now.

	if no options are given, duration will be printed.

	<size> is the size of the file to transfer.
	<rate> is the transfer rate as a size, always per second.

	SI prefixes are supported (e.g. 1k = 1000, 1ki = 1024)
	Supported units are: b for bytes, bit for bits.
	If no unit is given, bytes are assumed.

	Examples:
		10 GiB at 120 mbps : {{ name }} 10gib 120mbit
		16 MiB at 1.2 MiB/s: {{ name }} 16mib 1.2mib
		15 GB  at 1.5 MB/s : {{ name }} 1.5g 1.5m
`