Files
OpenIntegrations/AGENTS.md
T

196 lines
17 KiB
Markdown
Raw Normal View History

# Руководство для 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_`.
- Каждый тестовый модуль должен содержать:
- область `ЗапускаемыеТесты` с процедурами-обертками
- область `АтомарныеТесты` с атомарными проверками
- Запускаемые тесты вызывают несколько атомарных.
- Количество атомарных тестов должно соответствовать количеству методов функционального модуля.
- Количество запускаемых тестов должно соответствовать количеству областей функционального модуля.
2026-04-12 16:01:05 +03:00
- Текст тестовых модулей используется для формирования примеров кода в документации
- Служебные комментарии `//SKIP` и `//END` используются для исключения частей кода теста при формировании примера для документации. `// SKIP` позволяет исключить конкретную строку внутри теста, `//END` - завершает запись примера. Весь служебный код, который идет после `//END` не попадает в пример кода для документации
2026-06-14 12:07:39 +03:00
- В каждом атомарном тесте должен быть **хотя бы один** вызов `OPI_ПолучениеДанныхТестов.Обработать` с **незаполненным** параметром `Вариант` (основной сценарий). Это **первый** такой вызов в процедуре, он размещается **до** `//END`; его результат попадает в онлайн-документацию как пример результата метода. Дополнительные варианты проверяются последующими вызовами `Обработать` с заполненным `Вариант` — они идут **после** `//END` и в пример документации не включаются
2026-04-12 16:01:05 +03:00
- При формировании атомарных тестов нельзя выносить повторяющийся код в служебные функции, так как это может привести к недопониманию при их использовании в качестве примеров для документации
- В спорных/неочевидных ситуациях ориентироваться на существующие тесты как на каноничные примеры.
## Реестр и функции проверок
- Каждый запускаемый тест должен быть указан в `ПолучитьТаблицуТестов` в модуле `OPI_ПолучениеДанныхТестов`.
- В `OPI_ПолучениеДанныхТестов` также находятся функции-проверки:
- по одной функции-проверке на каждый атомарный тест
- с возможным ветвлением внутри по параметру `Вариант`
2026-06-10 19:53:29 +03:00
- В `OPI_ПолучениеДанныхТестов` **нельзя** вызывать методы функциональных модулей библиотеки (`OPI_MessagePack`, `OPI_Lua`, `OPI_Telegram` и т.п.). Модуль проверок должен оставаться независимым от тестируемых API. Если для проверки нужны подготовленные данные (десериализация, round-trip, повторный вызов метода), это делается в атомарном тесте `OPIt_*` и передаётся в `Обработать` / `ОбработатьCLI` через дополнительные параметры (`Восстановленное`, `Исходное` и т.д.).
## Правила именования
- Формат имени атомарного теста:
- `<ИмяМодуляБезПрефикса>_<ИмяМетодаФункциональногоМодуля>`
- Формат имени запускаемого теста:
- `<ЛюбоеСокращениеИмениМодуляБезПрефикса><ИмяОбласти>`
- Формат имени функции-проверки:
- `Проверка_<ИмяАтомарногоТеста>`
2026-05-23 17:41:47 +03:00
## Схема нативных компонент (Rust add-in)
**Обязательный шаблон** для любой новой нативной компоненты и для переработки существующих. Не создавать add-in «с нуля» в монолитном `lib.rs` с `Arc<Mutex<…>>` — сразу закладывать структуру ниже.
Каноничные примеры: `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, маршрутизация) |
2026-05-23 17:41:47 +03:00
| **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<Mutex<State>>` — 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<Mutex<соединение/драйвер>>` на addin по-прежнему нельзя — драйвер только в worker/`Session`.
2026-05-23 17:41:47 +03:00
### Структура крейта (клиенты: БД, FTP, ZeroMQ и т.п.)
```
src/addins/<name>/src/
lib.rs — METHODS, get_params_amount, cal_func, impl_addin_exports
addin.rs — тонкий AddIn: FFI-методы, `Arc<Mutex<State>>` на оболочке, без Mutex вокруг драйвера
backend.rs — *Backend: настройки до connect, ленивый поток, SetLogger
2026-05-23 17:41:47 +03:00
worker.rs — WorkerCommand, Session, spawn_thread, вся работа с драйвером
query.rs — только если есть отдельная логика SQL/запросов (MSSQL, Postgres, …)
```
**Не использовать** в новом коде:
- `Arc<Mutex<Драйвер>>` на стороне 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` для бинарных полей и составных ответов.
2026-05-23 17:41:47 +03:00
- `common-tcp` — TLS, proxy, `create_tcp_connection` (FTP, БД с TLS).
- `common-dataset` — только где есть пакетные SQL/dataset (MSSQL).
### Паттерн `backend.rs`
- Поля: `thread: Option<…>`, настройки до connect (`tls`, `proxy`, строка подключения), `logger: Option<Arc<Logger>>`.
2026-05-23 17:41:47 +03:00
- `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<Result<…>>` или `Sender<String>` для готового JSON.
- `struct Session``client`/`connection`, `logger`.
2026-05-23 17:41:47 +03:00
- `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/<name>/` с `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<Mutex<драйвер>>`, заложить `Arc<Mutex<State>>` на FFI-оболочку (см. «Синхронизация FFI-оболочки»).
2026-05-23 17:41:47 +03:00
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` |