# Руководство для AGENTS ## Каноничный исходный код - Основная реализация библиотеки находится в `src/ru/BSL/OpenIntegrations`. - Если поведение кода и тестов расходится, в первую очередь считать этот каталог источником истины. - Английская версия кода, находящаяся в `src/en` не требует доработки и синхронизируется с русской версией автоматическими пайплайнами ## Каноничные тесты - Основные BSL-тесты находятся в `src/ru/BSL/Tests`. - При разборе бизнес-логики анализировать эти тесты ## Зеркало OneScript - Зеркальные файлы OneScript находятся в `src/ru/OInt` (включая `src/ru/OInt/tests`). - Использовать этот каталог для проверки CLI/runtime-паритета, но не как первый источник при разборе BSL-регрессий. - Эти зеркала не требуют доработки. Все внесенные изменения будут затерты автоматическими процессами синхронизации 1С и OneScript версии (в пользу 1С версии из `src/ru/BSL`) ## Соглашения по тестовым модулям - У каждого функционального модуля есть два связанных тестовых модуля с тем же базовым именем: - основные тесты с префиксом `OPIt_` - тесты CLI с префиксом `OPItc_` - функциональные модули используют префикс `OPI_` - Не редактировать модули `OPItc_` вручную: CLI-тесты генерируются автоматически в CI-пайплайне из модулей `OPIt_`. - Каждый тестовый модуль должен содержать: - область `ЗапускаемыеТесты` с процедурами-обертками - область `АтомарныеТесты` с атомарными проверками - Запускаемые тесты вызывают несколько атомарных. - Количество атомарных тестов должно соответствовать количеству методов функционального модуля. - Количество запускаемых тестов должно соответствовать количеству областей функционального модуля. - Текст тестовых модулей используется для формирования примеров кода в документации - Служебные комментарии `//SKIP` и `//END` используются для исключения частей кода теста при формировании примера для документации. `// SKIP` позволяет исключить конкретную строку внутри теста, `//END` - завершает запись примера. Весь служебный код, который идет после `//END` не попадает в пример кода для документации - При формировании атомарных тестов нельзя выносить повторяющийся код в служебные функции, так как это может привести к недопониманию при их использовании в качестве примеров для документации - В спорных/неочевидных ситуациях ориентироваться на существующие тесты как на каноничные примеры. ## Реестр и функции проверок - Каждый запускаемый тест должен быть указан в `ПолучитьТаблицуТестов` в модуле `OPI_ПолучениеДанныхТестов`. - В `OPI_ПолучениеДанныхТестов` также находятся функции-проверки: - по одной функции-проверке на каждый атомарный тест - с возможным ветвлением внутри по параметру `Вариант` - В `OPI_ПолучениеДанныхТестов` **нельзя** вызывать методы функциональных модулей библиотеки (`OPI_MessagePack`, `OPI_Lua`, `OPI_Telegram` и т.п.). Модуль проверок должен оставаться независимым от тестируемых API. Если для проверки нужны подготовленные данные (десериализация, round-trip, повторный вызов метода), это делается в атомарном тесте `OPIt_*` и передаётся в `Обработать` / `ОбработатьCLI` через дополнительные параметры (`Восстановленное`, `Исходное` и т.д.). ## Правила именования - Формат имени атомарного теста: - `<ИмяМодуляБезПрефикса>_<ИмяМетодаФункциональногоМодуля>` - Формат имени запускаемого теста: - `<ЛюбоеСокращениеИмениМодуляБезПрефикса><ИмяОбласти>` - Формат имени функции-проверки: - `Проверка_<ИмяАтомарногоТеста>` ## Схема нативных компонент (Rust add-in) **Обязательный шаблон** для любой новой нативной компоненты и для переработки существующих. Не создавать add-in «с нуля» в монолитном `lib.rs` с `Arc>` — сразу закладывать структуру ниже. Каноничные примеры: `src/addins/postgres`, `mysql`, `sqlite`, `mssql`, `mongodb`, `ftp`, `zeromq`; серверы — `tcp_server`, `http_server`, `ws_server`. ### Два уровня защиты (не путать) | Уровень | Где | Что ловит | |--------|-----|-----------| | **FFI** | `common-core`: `call_as_func` → `catch_panic` | Паника в тонкой обёртке addin (JSON/Janx, маршрутизация) | | **Backend** | `common-backend`: поток worker + `catch_panic` на цикл handler | Паника в драйвере; после фатала — `health`, следующие `call`/`send` → `Err` | `catch_unwind` только на FFI **не заменяет** worker-поток: не решает `!Send` соединений (SQLite), гонки при параллельных вызовах из 1С, poisoned `Mutex` вокруг драйвера. **Не защищает** ни один уровень: segfault/abort в C-библиотеке драйвера — падает весь процесс 1С. Изоляция только отдельным процессом (вне этой схемы). ### Синхронизация FFI-оболочки Worker-поток сериализует работу с драйвером, но **не** защищает поля и флаги на стороне `AddIn`: `started`, `logger`, `datasets`, вызовы делегата в `backend`/`common-server`. Если платформа держит один экземпляр компоненты и вызывает методы **из разных потоков** (параллельные задания, внешний хостинг, общий кэш объекта), без mutex на оболочке возможны гонки — даже при корректном worker. **Обязательно** в `addin.rs` (и в server-`wrapper.rs`, где есть обёртка): - `Arc>` — backend-делегат, служебное состояние, logger; все FFI-методы через `common_utils::lock_unpoisoned`; - для SQL с dataset — `datasets` внутри того же `State`, не отдельное поле на `AddIn` (в `lib.rs` — тонкие `datasets_*` на `AddIn`). Свойства getset (`connection_string`, `server_address`, `address` и т.п.) допустимо оставить на `AddIn` **вне** mutex; методы, которые читают их вместе с `State`, берут lock и при необходимости клонируют строку до обращения к backend (см. `grpc` `connect`). Это **не** отменяет запрет ниже: `Arc>` на addin по-прежнему нельзя — драйвер только в worker/`Session`. ### Структура крейта (клиенты: БД, FTP, ZeroMQ и т.п.) ``` src/addins//src/ lib.rs — METHODS, get_params_amount, cal_func, impl_addin_exports addin.rs — тонкий AddIn: FFI-методы, `Arc>` на оболочке, без Mutex вокруг драйвера backend.rs — *Backend: настройки до connect, ленивый поток, SetLogger worker.rs — WorkerCommand, Session, spawn_thread, вся работа с драйвером query.rs — только если есть отдельная логика SQL/запросов (MSSQL, Postgres, …) ``` **Не использовать** в новом коде: - `Arc>` на стороне addin; - создание backend-потока в `new()` (только **лениво** при первом connect/операции — `ensure_thread`); - дублирование логики connect/execute в `lib.rs`. ### Выбор транспорта backend | Тип драйвера | Крейт | Поток | Примеры | |--------------|-------|-------|---------| | Синхронный API | `SyncBackendThread` | Обычный `std::thread`, без tokio | `postgres`, `mysql`, `sqlite`, `ftp` | | Async / tokio внутри драйвера | `BackendThread` | Поток + `Runtime::new()` в worker | `mssql`, `mongodb`, `zeromq` | | Долгоживущий сервер (listen/accept) | `common-server::Backend` | Внутри — `BackendThread` | `tcp_server`, `http_server`, `ws_server` | `common-server` — тот же канал команд, но свой API (`send_command`, `handle_async_command`); не смешивать с паттерном `addin/backend/worker` без необходимости. ### Общие зависимости (`Cargo.toml`) - `common-core` — макросы FFI, `catch_panic` на границе. - `common-backend` — `BackendThread` / `SyncBackendThread`. - `common-logs` — `Logger`, `log!`, `SetLogger` / `GetLogs` на FFI. - `common-janx` — `JanxValue`, `janx!`, `FromJanx` / `IntoJanx` для бинарных полей и составных ответов. - `common-tcp` — TLS, proxy, `create_tcp_connection` (FTP, БД с TLS). - `common-dataset` — только где есть пакетные SQL/dataset (MSSQL). ### Паттерн `backend.rs` - Поля: `thread: Option<…>`, настройки до connect (`tls`, `proxy`, строка подключения), `logger: Option>`. - `set_logger` / `set_tls` — **только до** установления соединения. - `connect` → `ensure_thread()` → `thread.call(WorkerCommand::Connect { … })`. - `close` / `Drop` → `shutdown(Some(WorkerCommand::Shutdown))`. - `get_logs` — читать из `Logger` на стороне addin/backend (не из worker), если logger хранится в backend. ### Паттерн `worker.rs` - `enum WorkerCommand` — одна варианта на операцию; ответы через `mpsc::Sender>` или `Sender` для готового JSON. - `struct Session` — `client`/`connection`, `logger`. - `fn log(&self, …)` — `common_logs::log!(logger, …)`. - `spawn_thread` — единственное место цикла `while let Ok(cmd) = rx.recv()`. - Вся работа с `FtpStream` / `Client` / `Connection` — **только** внутри этого потока. ### FFI: логирование Добавить в `METHODS` (перед `Version`): - `SetLogger` — JSON-конфиг (`Logger::from_json`); - `GetLogs` — `count`, ответ `{ result, logs, total, returned }`. Порядок на стороне BSL (`OPI_*`): настройки → `SetLogger` (если передано `Логирование`) → connect/open. ### BSL (`OPI_<Имя>`) В области основных методов: - `ПолучитьНастройкиЛогирования` → делегат в `OPI_Компоненты.ПолучитьНастройкиЛогирования`; - `ПолучитьЛог` → `OPI_Компоненты.ПолучитьЛог`; - в `ОткрытьСоединение` (или аналог) — необязательный параметр `Логирование`, вызов `Коннектор.SetLogger` до `Connect`. ### Тесты (новая и существующая компонента) 1. `OPIt_<Имя>`: области `ЗапускаемыеТесты` / `АтомарныеТесты` по правилам выше. 2. В запускаемый тест основных методов — `<Модуль>_ПолучитьНастройкиЛогирования`, `<Модуль>_ПолучитьЛог` (по образцу `OPIt_MSSQL`). 3. Атомарные тесты с `//END` для документации; без выноса повторов в служебные процедуры. 4. `OPI_ПолучениеДанныхТестов`: регистрация в `ПолучитьТаблицуТестов`, `Проверка_*` (для логирования — ветки `Файл`, `Память`, `КакСтрока`). 5. Не править `OPItc_*` вручную. 6. Количество атомарных тестов = количеству экспортных методов `OPI_*`. ### Серверы (отличие от клиентов) - Логирование часто включается **третьим аргументом `Start`**, а не отдельным `SetLogger` до connect (см. `tcp_server`, `http_server`, `ws_server`). - Состояние — accept/handle в `common-server`, не `Session { sql client }`. - BSL-тесты логирования — по тому же принципу, но вызов через `Запустить` / параметры старта. ### Чеклист: новая компонента 1. Создать `src/addins//` с `Cargo.toml` (зависимости из таблицы выше). 2. Сразу завести `lib.rs`, `addin.rs`, `backend.rs`, `worker.rs` (+ `query.rs` для SQL). 3. Выбрать `SyncBackendThread` или `BackendThread` (или `common-server` для listen-сервера). 4. Заложить `SetLogger` / `GetLogs` и `log!` в connect/операциях. 5. Параллельно: `OPI_<Имя>`, `OPIt_<Имя>`, проверки, шаблон в `CommonTemplates`, при необходимости — зеркало `OInt`. 6. `cargo check` в каталоге add-in. ### Чеклист: переработка старой компоненты Те же шаги, что для новой, плюс: 1. Разнести монолитный `lib.rs`; убрать `Arc>`, заложить `Arc>` на FFI-оболочку (см. «Синхронизация FFI-оболочки»). 2. Перенести клиент в `Session` worker-потока; поток — только ленивый `ensure_thread`. 3. **Сохранить** совместимость FFI: номера/имена методов, JSON-поля, намеренные побочные эффекты (например задержки в `cal_func`). ### Эталоны для копирования | Задача | Смотреть | |--------|----------| | Новая sync-клиент + SQL | `src/addins/postgres` | | Новая sync-клиент без SQL | `src/addins/ftp` | | Новая async-клиент | `src/addins/mssql`, `mongodb` | | BSL + тесты с нуля | `OPI_PostgreSQL`, `OPIt_MSSQL` | | Новый сервер | `src/addins/tcp_server` + `commons/common-server` |