/
codespawn
/
tgstream
Обзор
Документация
Войти
/
codespawn
/
tgstream
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
16
CI/CD
Аналитика
Безопасность
master
internal/dsl/parse.go
113 строк
4 KB
codespawn
Запрос на слияние 'feat/websource-edge-docs' (
#3
) из feat/websource-edge-docs в master
16 июл 2026, 07:24
Верифицирован
16 июл 2026, 07:24
fb8bdd6
Код
Авторство
О чём код?
package dsl import ( "fmt" "math" "strconv" "strings" "time" ) // parseValue применяет парсер mode к сырому значению и возвращает типизированный // результат. Используется extract.go после applyTransforms. // // mode: // - "" — без парсинга; возвращает строку как есть (как string). // - "date" — дата: пробует несколько общих layout'ов (RFC3339 + ряд // человекочитаемых форматов). Возвращает time.Time. // - "date_unix" — Unix-время в секундах. Возвращает time.Time. // - "count" — число с K/M/B-суффиксами ("3.36M" → 3360000). Возвращает int. // // Невалидное значение → ошибка. Пустая строка для date/count → zeroTime/0 // без ошибки (поле опциональное — отсутствие не должно валить весь пост). func parseValue(val, mode string) (any, error) { switch mode { case "", "text": // Строковые роли НЕ TrimSpace'ятся здесь: для attr:html trailing-пробелы // значимы (innerHTML сохраняется как есть). Для attr:text обрезка уже // сделана в extractField. Возвращает как есть. return val, nil case "date": val = strings.TrimSpace(val) if val == "" { return time.Time{}, nil } t, err := parseDate(val) if err != nil { return nil, err } return t, nil case "date_unix": val = strings.TrimSpace(val) if val == "" { return time.Time{}, nil } return parseDateUnix(val) case "count": return parseCount(val), nil default: return nil, fmt.Errorf("unknown parse mode %q", mode) } } // dateLayouts — форматы, перебираемые parseDate. Порядок важен: более // специфичные (с timezone) — раньше. RFC3339 — основной (t.me отдаёт его); // остальные покрывают распространённые человекочитаемые форматы сайтов. var dateLayouts = []string{ time.RFC3339, time.RFC3339Nano, "2006-01-02T15:04:05", "2006-01-02 15:04:05", "2006-01-02", "02.01.2006", "01/02/2006", "Jan 2, 2006", "January 2, 2006", "2 January 2006", time.RFC1123, time.RFC1123Z, } // parseDate перебирает dateLayouts и возвращает первый успешный результат. // Время приводится к UTC (консистентность вывода). func parseDate(raw string) (time.Time, error) { for _, layout := range dateLayouts { if t, err := time.Parse(layout, raw); err == nil { return t.UTC(), nil } } return time.Time{}, fmt.Errorf("cannot parse date %q", raw) } // parseDateUnix парсит Unix-время в секундах и возвращает UTC time.Time. func parseDateUnix(raw string) (time.Time, error) { n, err := strconv.ParseInt(strings.TrimSpace(raw), 10, 64) if err != nil { return time.Time{}, fmt.Errorf("cannot parse unix time %q: %w", raw, err) } return time.Unix(n, 0).UTC(), nil } // parseCount разбирает «чистый» токен счётчика вида "3.36M", "1.2K", "999". // Суффиксы: K=10^3, M=10^6, B=10^9 (регистронезависимо). Невалидная/пустая // строка → 0. Совпадает с tmsource.parseCountToken (для parity с Telegram). func parseCount(s string) int { s = strings.TrimSpace(s) if s == "" { return 0 } mult := 1.0 switch last := s[len(s)-1]; last { case 'k', 'K': mult, s = 1e3, s[:len(s)-1] case 'm', 'M': mult, s = 1e6, s[:len(s)-1] case 'b', 'B': mult, s = 1e9, s[:len(s)-1] } f, err := strconv.ParseFloat(s, 64) if err != nil { return 0 } return int(math.Round(f * mult)) }