Rust
Крейт читает тот же .tdc-конфиг и при том же сиде выдаёт тот же результат, что и
реализации на TypeScript, Python, Java и C# — байт в байт, на всех трёх движках и в
Parquet.
Один крейт: и библиотека, и командная строка.
Где взять
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 или новее.