text table, telegram html message
This commit is contained in:
112
tools.go
Normal file
112
tools.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package notify
|
||||
|
||||
import (
|
||||
"html"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/mattn/go-runewidth"
|
||||
)
|
||||
|
||||
// Проверка, что строка — число (целое или с точкой, +,-, пробелы)
|
||||
var numberRe = regexp.MustCompile(`^[+-]?[\d\s_]*\.?\d+$`)
|
||||
|
||||
// Выравнивание текста по центру с учётом визуальной ширины
|
||||
func centerPad(s string, width int) string {
|
||||
w := runewidth.StringWidth(s)
|
||||
if w >= width {
|
||||
return s
|
||||
}
|
||||
left := (width - w) / 2
|
||||
right := width - w - left
|
||||
return strings.Repeat(" ", left) + s + strings.Repeat(" ", right)
|
||||
}
|
||||
|
||||
// Выравнивание текста по левому краю с учётом ширины
|
||||
func padRightVisual(s string, width int) string {
|
||||
w := runewidth.StringWidth(s)
|
||||
if w >= width {
|
||||
return s
|
||||
}
|
||||
return s + strings.Repeat(" ", width-w)
|
||||
}
|
||||
|
||||
// Выравнивание текста по правому краю с учётом ширины
|
||||
func padLeftVisual(s string, width int) string {
|
||||
w := runewidth.StringWidth(s)
|
||||
if w >= width {
|
||||
return s
|
||||
}
|
||||
return strings.Repeat(" ", width-w) + s
|
||||
}
|
||||
|
||||
// BuildTelegramBoxTable строит ASCII-таблицу с рамками для Telegram HTML <pre>
|
||||
func BuildTelegramHTMLTable(rows [][]string) string {
|
||||
if len(rows) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Экранируем HTML-символы
|
||||
for i := range rows {
|
||||
for j := range rows[i] {
|
||||
rows[i][j] = html.EscapeString(rows[i][j])
|
||||
}
|
||||
}
|
||||
|
||||
// Определяем визуальную ширину колонок
|
||||
colWidths := make([]int, len(rows[0]))
|
||||
for _, row := range rows {
|
||||
for i, col := range row {
|
||||
w := runewidth.StringWidth(col)
|
||||
if w > colWidths[i] {
|
||||
colWidths[i] = w
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Функция для линий-разделителей
|
||||
buildSep := func() string {
|
||||
var s strings.Builder
|
||||
s.WriteString("+")
|
||||
for _, w := range colWidths {
|
||||
s.WriteString(strings.Repeat("-", w+2))
|
||||
s.WriteString("+")
|
||||
}
|
||||
return s.String()
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteString(buildSep() + "\n")
|
||||
|
||||
for ri, row := range rows {
|
||||
b.WriteString("|")
|
||||
for i, col := range row {
|
||||
w := colWidths[i]
|
||||
var cell string
|
||||
|
||||
switch {
|
||||
case ri == 0:
|
||||
// Заголовок — по центру
|
||||
cell = centerPad(col, w)
|
||||
case numberRe.MatchString(col):
|
||||
// Число — по правому краю
|
||||
cell = padLeftVisual(col, w)
|
||||
default:
|
||||
// Текст — по левому краю
|
||||
cell = padRightVisual(col, w)
|
||||
}
|
||||
|
||||
b.WriteString(" " + cell + " |")
|
||||
}
|
||||
b.WriteString("\n")
|
||||
|
||||
// Разделитель после заголовка
|
||||
if ri == 0 {
|
||||
b.WriteString(buildSep() + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
b.WriteString(buildSep() + "\n")
|
||||
|
||||
return b.String()
|
||||
}
|
||||
Reference in New Issue
Block a user