Перейти к основному содержимому

Rust

Крейт читает тот же .tdc-конфиг и при том же сиде выдаёт тот же результат, что и реализации на TypeScript, Python, Java и C# — байт в байт, на всех трёх движках и в Parquet.

Один крейт: и библиотека, и командная строка.

Где взять

На crates.io — версия 0.1.7
cargo add tdcv2 # как зависимость
cargo install tdcv2 # как команда

У крейта нет зависимостей, а стартовые паки вшиты в бинарник — установленному крейту не нужно ничего на диске. Подробнее на странице Установка.

Как пользоваться

use tdcv2::Tdc;

let data = Tdc::from_file("users.tdc")?;
println!("{data}");

for row in data.rows() {
println!("{:?}", row.get("Gender"));
}

data.write_file("users.csv")?;

Строки записей, а не текст

Ради записей библиотеку и берут вместо командной строки. Тест, который проверяет row.get("Gender"), говорит то, что имеет в виду; тот же тест, разбирающий CSV обратно из строки, тратит бо́льшую часть строк кода на разбор.

Текстовый вывод и вывод записями читают одни и те же сгенерированные значения, так что разойтись они не могут. Вид записями полностью игнорирует <block> и обёртки — они описывают формат файла, а у записи формата нет.

use tdcv2::{Options, Tdc};

let data = Tdc::new(Options {
config_file: Some("users.tdc".into()),
count: Some(100), // перекрывает то, что объявлено в <env>
seed: Some("test".into()), // фиксирует прогон
..Options::default()
})?;

let first = data.row(0).unwrap();
println!("{:?}", first.get("Address.city")); // поле составной последовательности
println!("{:?}", first.nested()["Address"]); // или весь адрес целиком

Последовательность, которая к записи не относится, возвращает None, а не Some(""). У колонки с parent="Gender.Male" на женской записи значения нет, и пустая строка утверждала бы, что оно есть и просто пустое.

Параметры

config_file / config_stringРовно одно из двух
count, seed, localeПерекрывают то, что объявлено в <env>
engineПринудительно движок 1, 2 или 3 вместо выбора по конфигу
now_millisЗафиксировать часы, чтобы тест про даты не протух за ночь
packs_dir, data_pathsГде искать паки и источники @data/…
base_dirОтносительно чего считается относительный src=

Отклонённый конфиг возвращается как TdcError::Refused — он несёт и диагностики, и исходник, на который они указывают, так что вызывающий код может показать саму строку с ошибкой, а не только процитировать сообщение. diagnostics() на успешном прогоне несёт то, о чём предупредили, но из-за чего не отказали. seed() сообщает, был ли сид придуман: прогон без сида невоспроизводим, а этого почти никогда не хотят.

Одно значение без конфига

Quick вытягивает одно значение из тех же пакетов данных, которые читает конфиг: ни файла, ни <env>, один вызов.

use tdcv2::quick::Quick;

let mut tdc = Quick::new();

tdc.get("person.lastName")?; // Jones
tdc.get("usa.docs.ssn")?; // 699209702, с настоящими контрольными цифрами
tdc.many("person.lastName", 5)?; // сразу пять
Quick::seeded("demo").locale("ru").get("person.lastName")?; // закреплено и по-русски

Адрес здесь строка, тогда как TypeScript, Python и C# идут по нему через члены объекта. Запись через члены требует по одной сгенерированной функции на адрес, а сгенерированная поверхность покрыла бы только пакеты, вкомпилированные в бинарник, — при том что большинство пакетов приходит во время работы, а get("ru.person.lastName") заработает сразу после загрузки. Вся поверхность — на странице По одному значению.

Ни одной зависимости

У крейта нет зависимостей. Лексер и парсер написаны вручную по общей грамматике; так же написаны PRNG, распаковщик DEFLATE, SHA-256, кодировщики Thrift и Snappy под Parquet и декодер PNG, который читает рисунок в кривую.

Это не минимализм ради минимализма. Каждая из этих вещей обязана выдавать те же байты, что и остальные четыре реализации, а крейт, поменявший правило округления или хеш в минорном релизе, сломал бы главное обещание проекта, не тронув ни строчки TDC.

Единственное исключение — HTTPS: там нужен TLS-стек, который никто не должен писать руками. tdcv2 pack и <gen type="http"> запускают curl дочерним процессом; если его нет, команда так и скажет и напечатает строку установки для той системы, на которой запущена. Всё остальное — генерация данных, локальные паки, любой формат вывода — работает и без него.

Требования

Rust 1.74 или новее.