cocurrency

This commit is contained in:
raf 2023-12-29 23:05:02 +03:00
parent a3ff066c29
commit 5fe48627b7
No known key found for this signature in database
GPG key ID: 02D1DD3FA08B6B29
2 changed files with 10063 additions and 35 deletions

98
main.go
View file

@ -8,6 +8,7 @@ import (
"os" "os"
"regexp" "regexp"
"strings" "strings"
"sync"
"time" "time"
"github.com/fatih/color" "github.com/fatih/color"
@ -19,26 +20,33 @@ type LinkCheckResult struct {
StatusCode int StatusCode int
} }
var wg sync.WaitGroup
var verboseMode bool
func logWithColor(level string, msg string, args ...interface{}) { func logWithColor(level string, msg string, args ...interface{}) {
timestamp := time.Now().Format("2006/01/02 15:04:05") if verboseMode {
colorFunc := color.New(color.FgWhite).SprintFunc() timestamp := time.Now().Format("2006/01/02 15:04:05")
switch level { colorFunc := color.New(color.FgWhite).SprintFunc()
case "ERROR": switch level {
colorFunc = color.New(color.FgRed).SprintFunc() case "ERROR":
case "WARN": colorFunc = color.New(color.FgRed).SprintFunc()
colorFunc = color.New(color.FgYellow).SprintFunc() case "WARN":
case "INFO": colorFunc = color.New(color.FgYellow).SprintFunc()
colorFunc = color.New(color.FgCyan).SprintFunc() case "INFO":
colorFunc = color.New(color.FgCyan).SprintFunc()
}
fmt.Printf("%s %s %s\n", timestamp, colorFunc(level), fmt.Sprintf(msg, args...))
} }
fmt.Printf("%s %s %s\n", timestamp, colorFunc(level), fmt.Sprintf(msg, args...))
} }
func main() { func main() {
filename := flag.String("file", "", "Markdown file to test") filename := flag.String("file", "", "Markdown file to test")
verbose := flag.Bool("verbose", false, "Enable verbose mode") verboseFlag := flag.Bool("verbose", false, "Enable verbose mode")
failedOnly := flag.Bool("failed-only", false, "Return only failed links") failedOnly := flag.Bool("failed-only", false, "Return only failed links")
flag.Parse() flag.Parse()
verboseMode = *verboseFlag
if *filename == "" { if *filename == "" {
logWithColor("INFO", "Please provide a markdown file.") logWithColor("INFO", "Please provide a markdown file.")
os.Exit(1) os.Exit(1)
@ -63,45 +71,56 @@ func main() {
re := regexp.MustCompile(`\[(.*?)\]\((.*?)\)`) re := regexp.MustCompile(`\[(.*?)\]\((.*?)\)`)
results := []LinkCheckResult{} results := []LinkCheckResult{}
resChan := make(chan LinkCheckResult, 1000) // buffered channel for storing responses
for scanner.Scan() { for scanner.Scan() {
line := scanner.Text() line := scanner.Text()
matches := re.FindAllStringSubmatch(line, -1) matches := re.FindAllStringSubmatch(line, -1)
for _, match := range matches { for _, match := range matches {
link := strings.TrimSpace(match[2]) wg.Add(1)
resp, err := http.Head(link) go func(link string, verboseFlag *bool) {
if err != nil || resp == nil { defer wg.Done()
logWithColor("ERROR", "Invalid link: %s", link) resp, err := http.Head(link)
results = append(results, LinkCheckResult{ if err != nil || resp == nil {
Link: link, logWithColor("ERROR", "Invalid link: %s", link)
IsValid: false, resChan <- LinkCheckResult{
StatusCode: http.StatusBadRequest, Link: link,
}) IsValid: false,
} else { StatusCode: http.StatusBadRequest,
isValid := resp.StatusCode == http.StatusOK }
result := LinkCheckResult{ } else {
Link: link, isValid := resp.StatusCode == http.StatusOK
IsValid: isValid, result := LinkCheckResult{
StatusCode: resp.StatusCode, Link: link,
} IsValid: isValid,
results = append(results, result) StatusCode: resp.StatusCode,
if *verbose || (!*failedOnly && !isValid) { }
statusColor := color.GreenString resChan <- result
if !isValid { if *verboseFlag || (!*failedOnly && !isValid) {
statusColor = color.RedString statusColor := color.GreenString
if !isValid {
statusColor = color.RedString
}
logWithColor("INFO", "%s: %s", link, statusColor("%d", resp.StatusCode))
} }
logWithColor("INFO", "%s: %s", link, statusColor("%d", resp.StatusCode))
} }
} }(strings.TrimSpace(match[2]), verboseFlag)
} }
} }
wg.Wait()
close(resChan)
for res := range resChan {
results = append(results, res)
}
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
logWithColor("ERROR", "Error scanning file: %v", err) logWithColor("ERROR", "Error scanning file: %v", err)
os.Exit(1) os.Exit(1)
} }
// Print summary // summary
validCount := 0 validCount := 0
invalidCount := 0 invalidCount := 0
for _, result := range results { for _, result := range results {
@ -115,6 +134,7 @@ func main() {
if invalidCount > 0 { if invalidCount > 0 {
summaryColor = color.RedString summaryColor = color.RedString
} }
logWithColor("INFO", summaryColor("Summary: %d valid links, %d invalid links"), validCount, invalidCount) logWithColor("INFO", summaryColor("Summary: %d valid links, %d invalid links"), validCount, invalidCount)
if *failedOnly { if *failedOnly {
@ -123,6 +143,14 @@ func main() {
logWithColor("ERROR", "Failed link: %s", result.Link) logWithColor("ERROR", "Failed link: %s", result.Link)
} }
} }
} else if *verboseFlag {
for _, result := range results {
statusColor := color.GreenString
if !result.IsValid {
statusColor = color.RedString
}
logWithColor("INFO", "%s: %s", result.Link, statusColor("%d", result.StatusCode))
}
} }
if invalidCount > 0 { if invalidCount > 0 {

10000
output.md Normal file

File diff suppressed because it is too large Load diff