//go:build windows package main import ( "fmt" "sort" "strconv" "strings" "time" "unicode" "unicode/utf8" ) func cleanLogText(s string, limit int) string { var out []rune for _, r := range s { if unicode.IsControl(r) || unicode.In(r, unicode.Cf) || r == '|' { r = ' ' } if len(out) >= limit { if limit >= 3 { out = append(out[:limit-3], '.', '.', '.') } break } out = append(out, r) } return string(out) } func priorityRank(p uint32) int { switch p { case PriorityClassIdle: return 0 case PriorityClassBelowNormal: return 1 case PriorityClassNormal: return 2 case PriorityClassAboveNormal: return 3 case PriorityClassHigh: return 4 case PriorityClassRealtime: return 5 } return -1 } func logMemory(b uint64) string { if b >= 1<<30 { return fmt.Sprintf("%.1f ГБ", float64(b)/(1<<30)) } if b >= 1<<20 { return fmt.Sprintf("%d МБ", b>>20) } if b >= 1<<10 { return fmt.Sprintf("%d КБ", b>>10) } return fmt.Sprintf("%d Б", b) } func writeLogTable(b *strings.Builder, cells [][]string) { widths := make([]int, len(cells[0])) for _, row := range cells { for i, value := range row { widths[i] = max(widths[i], utf8.RuneCountInString(value)) } } var border strings.Builder border.WriteByte('+') for _, width := range widths { border.WriteString(strings.Repeat("-", width+2)) border.WriteByte('+') } border.WriteByte('\n') b.WriteString(border.String()) for n, row := range cells { b.WriteByte('|') for i, value := range row { fmt.Fprintf(b, " %-*s |", widths[i], value) } b.WriteByte('\n') if n == 0 { b.WriteString(border.String()) } } b.WriteString(border.String()) } func formatReport(r scanReport, when time.Time) string { if r.Title == "" && len(r.Rows) == 0 && len(r.Errors) == 0 { return "" } var b strings.Builder header := func(title string) { fmt.Fprintf(&b, "[%s] %s", when.Format("02.01.2006 15:04:05"), cleanLogText(title, 200)) if r.DryRun { b.WriteString(" [DRY-RUN: ПЛАН, БЕЗ ИЗМЕНЕНИЙ]") } b.WriteByte('\n') } if r.Title != "" || len(r.Rows) > 0 { title := r.Title if title == "" { title = "ИЗМЕНЕНИЕ ПРИОРИТЕТОВ" } header(title) if len(r.Rows) > 0 { rows := append([]priorityChange(nil), r.Rows...) sort.SliceStable(rows, func(i, j int) bool { if rows[i].Heavy != rows[j].Heavy { return rows[i].Heavy } return rows[i].PID < rows[j].PID }) cells := [][]string{{"Процесс", "PID", "Память", "Повышено", "Понижено"}} for _, row := range rows { columns := []string{cleanLogText(row.Name, 64), strconv.FormatUint(uint64(row.PID), 10), logMemory(row.RSS), "", ""} if row.Blocked { columns[3] = "в блоклисте" } else if row.Unchanged { columns[3] = "уже " + prioName(row.After) } else { change := prioName(row.Before) + " -> " + prioName(row.After) if priorityRank(row.After) > priorityRank(row.Before) { columns[3] = change } else { columns[4] = change } } cells = append(cells, columns) } writeLogTable(&b, cells) } b.WriteByte('\n') } if len(r.Errors) > 0 { header("ОШИБКИ") rows := append([]processError(nil), r.Errors...) sort.SliceStable(rows, func(i, j int) bool { return rows[i].PID < rows[j].PID }) cells := [][]string{{"Процесс", "PID", "Описание ошибки"}} for _, problem := range rows { name, pid := problem.Name, "-" if name == "" { name = "autoPriority" } if problem.PID != 0 { pid = strconv.FormatUint(uint64(problem.PID), 10) } cells = append(cells, []string{cleanLogText(name, 64), pid, cleanLogText(problem.Description, 1500)}) } writeLogTable(&b, cells) b.WriteByte('\n') } return b.String() }