1// Copyright 2024 The BoringSSL Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package main 16 17import ( 18 "fmt" 19 "io" 20 "net/http" 21 "strings" 22) 23 24func step(desc string, f func(*stepPrinter) error) error { 25 fmt.Printf("%s...", desc) 26 if *pipe { 27 fmt.Printf("\n") 28 } else { 29 fmt.Printf(" ") 30 } 31 s := stepPrinter{lastPercent: -1} 32 err := f(&s) 33 s.erasePercent() 34 if err != nil { 35 fmt.Printf("ERROR\n") 36 } else { 37 fmt.Printf("OK\n") 38 } 39 return err 40} 41 42type stepPrinter struct { 43 lastPercent int 44 percentLen int 45 progress, total int 46} 47 48func (s *stepPrinter) erasePercent() { 49 if !*pipe && s.percentLen > 0 { 50 var erase strings.Builder 51 for i := 0; i < s.percentLen; i++ { 52 erase.WriteString("\b \b") 53 } 54 fmt.Printf("%s", erase.String()) 55 s.percentLen = 0 56 } 57} 58 59func (s *stepPrinter) setTotal(total int) { 60 s.progress = 0 61 s.total = total 62 s.printPercent() 63} 64 65func (s *stepPrinter) addProgress(delta int) { 66 s.progress += delta 67 s.printPercent() 68} 69 70func (s *stepPrinter) printPercent() { 71 if s.total <= 0 { 72 return 73 } 74 75 percent := 100 76 if s.progress < s.total { 77 percent = 100 * s.progress / s.total 78 } 79 if *pipe { 80 percent -= percent % 10 81 } 82 if percent == s.lastPercent { 83 return 84 } 85 86 s.erasePercent() 87 88 s.lastPercent = percent 89 str := fmt.Sprintf("%d%%", percent) 90 s.percentLen = len(str) 91 fmt.Printf("%s", str) 92 if *pipe { 93 fmt.Printf("\n") 94 } 95} 96 97func (s *stepPrinter) progressWriter(total int) io.Writer { 98 s.setTotal(total) 99 return &progressWriter{step: s} 100} 101 102func (s *stepPrinter) httpBodyWithProgress(r *http.Response) io.Reader { 103 // This does not always give any progress. It seems GitHub will sometimes 104 // provide a Content-Length header and sometimes not, for the same URL. 105 return io.TeeReader(r.Body, s.progressWriter(int(r.ContentLength))) 106} 107 108type progressWriter struct { 109 step *stepPrinter 110} 111 112func (p *progressWriter) Write(b []byte) (int, error) { 113 p.step.addProgress(len(b)) 114 return len(b), nil 115} 116