progress.v

2.5 KB · raw

1//! The line a long action draws while it works (§4, "never leave the person2//! wondering"): one line on stderr, redrawn in place and cleared when the step3//! ends, and only when stderr is a terminal, so pipes, logs and `--json` never4//! see it.56module cli78import os9import src.fmt10import src.remote11import src.snapshot12import src.view1314fn working(text string) {15	if os.is_atty(2) == 0 {16		return17	}18	width := line_width()19	shown := fmt.fit('  ${text}', width)20	// Padded with spaces, not cleared with an escape code, so a plain terminal21	// and NO_COLOR stay free of control sequences.22	eprint('\r${fmt.dim(shown)}${' '.repeat(width - fmt.visible_width(shown))}')23	flush_stderr()24}2526fn done_working() {27	if os.is_atty(2) != 0 {28		eprint('\r${' '.repeat(line_width())}\r')29		flush_stderr()30	}31}3233// line_width stays one column short of the terminal, so the line never wraps.34fn line_width() int {35	_, columns := view.terminal_size()36	return if columns > 1 { columns - 1 } else { 79 }37}3839// reading is the walk's line: how many files it has had to read and hash.40fn reading() snapshot.Hashing {41	return snapshot.Hashing{42		on: fn (done int, total int) {43			if done % 100 == 1 {44				working('reading files · ${done}')45			}46		}47	}48}4950// Watched counts what an offer, steal or strip moves through a backend, so the51// line moves with the transfer. Exchange stays unaware of it.52struct Watched {53	label string54mut:55	inner    remote.Backend56	received int57	sent     int58}5960// watched_backend is this machine's backend, counted on the line under `label`.61fn watched_backend(label string) !remote.Backend {62	return remote.Backend(watch(label, this_machine_backend()!))63}6465fn watch(label string, backend remote.Backend) Watched {66	return Watched{67		label: label68		inner: backend69	}70}7172fn (mut w Watched) show() {73	mut text := w.label74	if w.sent > 0 {75		text += ' · ${w.sent} sent'76	}77	if w.received > 0 {78		text += ' · ${w.received} received'79	}80	working(text)81}8283fn (mut w Watched) get(key string) !remote.Object {84	object := w.inner.get(key)!85	w.received++86	w.show()87	return object88}8990fn (mut w Watched) list(prefix string) ![]string {91	return w.inner.list(prefix)!92}9394fn (mut w Watched) put_if_absent(key string, body []u8) !bool {95	put := w.inner.put_if_absent(key, body)!96	w.sent++97	w.show()98	return put99}100101fn (mut w Watched) put_if_match(key string, body []u8, tag string) !bool {102	put := w.inner.put_if_match(key, body, tag)!103	w.sent++104	w.show()105	return put106}107108fn (mut w Watched) delete(key string) ! {109	w.inner.delete(key)!110	w.sent++111	w.show()112}