# План реалізації CLI утиліти `mos`

## Огляд
Створення повністю незалежної консольної утиліти `mos` мовою Rust. Утиліта не матиме жодних зовнішніх залежностей (пустий блок `dependencies` у `Cargo.toml`). Вона читатиме вивід стандартної утиліти системного `ping` (зі `stdin`), дублюватиме його в `stdout`, а кожні 300 пінгів розраховуватиме і виводитиме детальну мережеву статистику, аналогічну тій, що є в `egui_pinger`. Також утиліта відстежуватиме стан з'єднання ("інциденти") та додаватиме маркери про втрату або відновлення зв'язку.

## Зміни в архітектурі
- **Новий крейт `mos`**: Буде створено нову директорію `mos` поруч зі `src/` основного проєкту, яка міститиме власний `Cargo.toml` та вихідний код утиліти `mos`.
- **Ізоляція коду**: `mos` буде повністю самостійним. Частина математики для розрахунку Jitter, MOS, затримок та відсотків буде вручну скопійована з `egui_pinger` (дублювання в цьому випадку виправдане заради нульових зовнішніх залежностей).

## Етапи реалізації

- [ ] **Крок 1: Ініціалізація проєкту**
  - [ ] Створити теку `mos/` в корені робочого простору.
  - [ ] Ініціалізувати в ній новий бінарний Rust проєкт: `cargo new mos`.
  - [ ] Переконатися, що у `mos/Cargo.toml` немає жодних зовнішніх залежностей.
  - [ ] Додати `mos` до списку `members` у `workspace` основного `Cargo.toml` (якщо є секція workspace) або залишити повністю самостійним крейтом.

- [ ] **Крок 2: Розробка парсера виводу `ping` (`mos/src/parser.rs` або всередині `main.rs`)**
  - [ ] Написати ефективну логіку порядкового читання `stdin` через `std::io::stdin().lock().lines()`.
  - [ ] Створити універсальний та швидкий парсер рядків без використання пакету `regex` (через `str::split`, `str::contains`, `str::find` тощо).
  - [ ] Парсер має підтримувати розпізнавання:
    - Успішних результатів `ping` для платформ: Linux, Windows, Mac OS, BSD (як локалізованих, так і не локалізованих виводів).
    - Різноманітних повідомлень про помилки замість успішного пакету ("Request timeout", "Destination Host Unreachable", "Заданий інтервал", "Time to live exceeded" тощо).
  - [ ] Усі рядки, що не є корисним навантаженням (заголовки, підсумки самого ping), мають ігноруватися для цілей статистики, але дублюватися у `stdout`.

- [ ] **Крок 2.5: Обробка аргументів командного рядка**
  - [ ] Реалізувати розбір аргументів командного рядка у `src/main.rs`.
  - [ ] Використати ручний розбір через цикл `while` і конструкцію `match`, розбираючи масив аргументів, наприклад:
    ```rust
    let mut args: Vec<String> = std::env::args().collect();
    let mut args_slice = args.as_slice();
    while let [tail @ ..] = args_slice {
        match tail {
            ["--help" | "-h", rest @ ..] => { /* вивести довідку */ args_slice = rest; },
            // ... інші аргументи
            [_unknown, rest @ ..] => { args_slice = rest; }, // або помилка
            [] => break,
        }
    }
    ```
  - [ ] Обробляти параметри такі як -n | --number-of-lines NUM, -e | --enable STAT, -d | --disable STAT.

- [ ] **Крок 3: Копіювання та адаптація математичного ядра (`mos/src/stats.rs` або `main.rs`)**
  - [ ] Перенести необхідні функції зі `src/model/status.rs`.
  - [ ] Видалити використання `#[derive(Serialize, Deserialize)]` та виклики макросу `tr!()`.
  - [ ] Створити структуру `MosStatus`, яка міститиме:
    - `VecDeque<f64>` на 300 елементів (або кастомний кільцевий буфер заради оптимізації) для історії RTT (втрата = `f64::NAN`).
    - Алгоритми обчислення: `mean`, `median`, `p95`, `rtp_jitter`, `rtp_jitter_mean`, `rtp_jitter_median`, `availability`, `loss_percent`, `mos`.
  - [ ] Підтвердити сумісність формул (особливо MOS та RFC 3550 Jitter).

- [ ] **Крок 4: Реалізація основної логіки та виводу (`mos/src/main.rs`)**
  - [ ] На початку виконання: вивести маркер про початок логування із поточним часом.
  - [ ] У циклі читання `stdin`:
    - Транслювати оригінальний рядок у `stdout`.
    - Розпізнати результат і оновити `MosStatus`.
  - [ ] **Інциденти**: Зберігати Streak. При 3 втратах підряд вивести повідомлення 🟥 `! "Зв'язок втрачено"`. При поверненні пінгів — 🟩 `! "Зв'язок відновлено"`.
  - [ ] **Скидання статистики**: Кожні 300 розпарасених пінгів друкувати в `stdout` блок статистики, такий самий як в egui_pinger, для прикладу: `# Статистика: J=Xms MOS=X`. (Англійською мовою)

- [ ] **Крок 5: Обробка фіналу програми**
  - [ ] Обробляти кінець потоку (EOF, коли оригінальний `ping` завершується). Коли `stdin` закриється, вивести маркер про кінець логування та фінальний блок добової/сесійної статистики.

- [ ] **Крок 6: Тестування та перевірка**
  - [ ] Запуск вручну у пайплайні: `ping -c 310 8.8.8.8 | . /mos/target/debug/mos`.
  - [ ] Читання різних сесій ping з файлів та перевірка правильності згенерованої статистики.
  - [ ] Перевірити коректність роботи при втраті пакетів.
  - [ ] Перевірити опції командного рядка.
  - [ ] Пересвідчитися, що бінарник дійсно компілюється фактично миттєво і не тягне залежностей з `crates.io`.

## Критерії успіху
- [ ] Папка `mos/` і її `Cargo.toml` не містять блоку `[dependencies]`.
- [ ] Утиліта коректно розпізнає вивід системного `ping` з платформ Linux, Windows, Mac OS та BSD (включаючи локалізовані версії та повідомлення про помилки).
- [ ] Аргументи командного рядка обробляються коректно через патерн матчинг `while match`.
- [ ] Розрахунки повністю тотожні показникам `egui_pinger`.
- [ ] При відсутності зв'язку виводяться коректні маркери інцидентів.
- [ ] Простий запуск у пайпі `ping ... | mos` не порушує вивід основного пінгу, а акуратно доповнює його.
- [ ] Створено тести, які тестують інтерфейс утиліти файлами із записами сесій ping, як для happy path так і для fail path, як англійською, так і локазіловані на українську версії ping.