Как я писал генератор TypeScript-биндингов для Tauri
kirillinyakin 30 минут назад Как я писал генератор TypeScript-биндингов для Tauri Средний 5 мин 1.2K Rust * TypeScript * Open source * Программирование * Кейс Из песочницы Всё началось с бага, на который я убил вечер. В...
Anthropic — What company has the best second artificial intelligence model at the end of June?
Значимый прорыв формирует отрасль ИИ: kirillinyakin 30 минут назад Как я писал генератор TypeScript-биндингов для Tauri Средний 5 мин 1. 2K Rust * TypeScript * Open source * Программирование * Кейс Из песочницы Всё началось с бага, на который я убил вечер. В Tauri фронтенд вызывает Rust-команды так:const user = await invoke("get_user", { userId: 42 }); Строка с именем команды, объект с аргументами, никакой проверки типов.
У меня в Rust аргумент назывался user_id, и я честно передал user_id с фронта. В рантайме прилетело что-то вроде invalid args 'userId' for command 'get_user' — Tauri по умолчанию переименовывает snake_case-аргументы в camelCase. Это написано в документации, я это даже знал.
Технические детали
Но знание, которое не проверяет компилятор, — не знание, а примета. Дальше по классике: возвращаемый тип у invoke — any, а о переименованном поле в структуре фронт узнаёт уже от пользователей. Захотелось, чтобы всё это генерировалось из Rust-кода само.
Почему не готовые решенияts-rs умеет экспортировать типы, но требует # на каждой структуре и ничего не знает про команды. tauri-specta закрывает и команды, но просит зарегистрировать хендлеры через свои макросы и живёт внутри кода приложения. Оба варианта рабочие, и если они вам подходят — берите их.
Мне хотелось другого: CLI, который натравливаешь на существующий проект и получаешь готовые файлы. Без аннотаций и без единой правки в Rust-коде. Так появился tauri-ts-generator.
Отраслевые последствия
Вот что он делает:# # pub struct # async fn get_user(user_id: u64, state: State<'_, AppState>) -> превращается в// types. ts export interface // commands. ts export async function getUser(userId: number): Promise { return invoke("get_user", { userId }); } State исчез из сигнатуры — его инжектит сам Tauri, фронт его не передаёт.
Первый же баг-репорт в трекере был, кстати, ровно про это: если объявить State через type-алиас, он просачивался в TS-типы. Result стал Promise, ошибка и так прилетит как reject. А display_name стал displayName, потому что на структуре висит rename_all = "camelCase" — генератор смотрит на serde-атрибуты, а не на имена полей в коде.
Как это устроено внутриНикакой магии с компилятором: генератор парсит исходники через syn. Сканер обходит src-tauri, парсер вытаскивает функции с # и все структуры, енумы и алиасы, резолвер разруливает модули и use-алиасы, генератор пишет два файла. Примерно 12 тысяч строк Rust.
Этот прогресс даёт важные сигналы о будущем отрасли, и технологический мир внимательно наблюдает.





