// Copyright 2024 The BoringSSL Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "fmt" "io" "net/http" "strings" ) func step(desc string, f func(*stepPrinter) error) error { fmt.Printf("%s...", desc) if *pipe { fmt.Printf("\n") } else { fmt.Printf(" ") } s := stepPrinter{lastPercent: -1} err := f(&s) s.erasePercent() if err != nil { fmt.Printf("ERROR\n") } else { fmt.Printf("OK\n") } return err } type stepPrinter struct { lastPercent int percentLen int progress, total int } func (s *stepPrinter) erasePercent() { if !*pipe && s.percentLen > 0 { var erase strings.Builder for i := 0; i < s.percentLen; i++ { erase.WriteString("\b \b") } fmt.Printf("%s", erase.String()) s.percentLen = 0 } } func (s *stepPrinter) setTotal(total int) { s.progress = 0 s.total = total s.printPercent() } func (s *stepPrinter) addProgress(delta int) { s.progress += delta s.printPercent() } func (s *stepPrinter) printPercent() { if s.total <= 0 { return } percent := 100 if s.progress < s.total { percent = 100 * s.progress / s.total } if *pipe { percent -= percent % 10 } if percent == s.lastPercent { return } s.erasePercent() s.lastPercent = percent str := fmt.Sprintf("%d%%", percent) s.percentLen = len(str) fmt.Printf("%s", str) if *pipe { fmt.Printf("\n") } } func (s *stepPrinter) progressWriter(total int) io.Writer { s.setTotal(total) return &progressWriter{step: s} } func (s *stepPrinter) httpBodyWithProgress(r *http.Response) io.Reader { // This does not always give any progress. It seems GitHub will sometimes // provide a Content-Length header and sometimes not, for the same URL. return io.TeeReader(r.Body, s.progressWriter(int(r.ContentLength))) } type progressWriter struct { step *stepPrinter } func (p *progressWriter) Write(b []byte) (int, error) { p.step.addProgress(len(b)) return len(b), nil }