Добро пожаловать во вторую часть моей серии статей Инженерия данных с помощью Rust и Apache Arrow DataFusion. Доступ к первой части здесь.
Во второй статье я описываю инициализацию проекта Rust с его зависимостями и настройку первого интерфейса командной строки с использованием библиотеки Clap.
Сначала я инициализирую проект с помощью Cargo и описываю его зависимости: библиотеку Clap, крейт Apache Arrow DataFusion и некоторые утилиты для логирования и управления ошибками.
С помощью Clap, Синтаксического анализатора аргументов командной строки для Rust, вы можете легко создавать приложения командной строки с пользовательскими правилами синтаксического анализа и проверки, используя Rust.
В этой статье я использую Clap для создания простого, но эффективного приложения командной строки.
Шаг 1 — Инициализация проекта и зависимости
Начнем с создания нового проекта с cargo new
.
$ cargo new mdata_app
Эта команда создает минимальный файл Cargo.toml
и исходный файл main.rs
.
$ tree mdata_app mdata_app ├── Cargo.toml └── src └── main.rs 1 directory, 2 files
Наш первоначальный файл Cargo.toml
прост и содержит только имя пакета и начальный номер версии.
→ toml «mdata_app/Cargo.toml» = [package] name = "mdata_app" version = "0.1.0" edition = "2021" (1) <<custom-options>> (2) <<dependencies>> (3) <<dev-dependencies>>
Заполняем три блока кода <<custom-options>>, <<dependencies>>
и <<dev-dependencies>>
.
<<custom-options>>
блок (1)
→ toml «custom-options» = publish = false [profile.release] strip = true
Указываем два варианта:
publish = false
— это упоминание о том, что этот пакет нельзя публиковать как крейт Rust. Полезно, чтобы убедиться, что этот код не опубликован по ошибке.strip = true
— это параметр компиляции для оптимизации размера нашего окончательного двоичного файла. Эта опция активна только в режиме компиляцииrelease
.
<<dependencies>>
блок (2)
→ toml «dependencies» = [dependencies] clap = { version = "3", features = ["derive"] } thiserror = "1" datafusion = "8" tokio = "1" log = "0.4" env_logger = "0.9.0"
- Библиотека Clap для разбора и обработки аргументов командной строки. Обратите внимание, что мы указываем версию ящика «3»; в этом случае
cargo
будет использовать последнюю версию основной ветви3.x
. Мы используем производную версию Clap API. Эта версия определяет параметры как структуру, интерпретируемую процедурными макросами для генерации нашего синтаксического анализатора аргументов. - Пакет «thiserror» помогает создавать собственные ошибки. Ошибки, полученные из этого крейта, реализуют трейт
std::error::Error
. - Ящики datafusion и tokio обрабатывают наши данные с помощью возможностей параллелизма.
- log и env_logger помогают интегрировать простое ведение журнала в приложение.
<<dev-dependencies>>
блок (3)
→ toml «dev-dependencies» = [dev-dependencies] fake = { version = "2.4", features=['derive']} tempfile = "3.3.0" rand = "0.8" assert_cmd = "2"
Мы добавляем ящики, используемые для тестирования, но не интегрированные в окончательный артефакт.
- Ящик поддельный генерирует настраиваемые случайные наборы данных.
- Пакет tempfile для создания и удаления временных файлов и каталогов.
- Пакет assert_cmd для тестирования двоичных файлов, запускаемых из командной строки.
Шаг 2 — Структура программы
Структура программы проста. Точкой входа пользователя в программу является оболочка ОС с интерфейсом командной строки. Пользователь может указать файл Parquet или CSV для загрузки, применить простое преобразование и записать его в указанном формате.
Ниже приведен наш основной программный файл, объединяющий структуру, утилиты и логику нашего приложения.
→ Rust «mdata_app/src/main.rs» = use std::fs; use std::num::ParseFloatError; use std::path::{Path, PathBuf}; use std::str::ParseBoolError; use clap::{ArgEnum, Parser, Subcommand}; use datafusion::arrow::datatypes::DataType; use datafusion::error::DataFusionError; use datafusion::prelude::*; use env_logger::{Builder, Env}; use log::{debug, info, LevelFilter, trace}; use thiserror::Error; (1) <<cli-args>> (2) <<structures>> (3) <<utilities>> (4) <<data-processing>> (5) <<program-main>> (6) <<unit-testing>>
В нашем программном файле main.rs
мы структурируем несколько блоков кода:
- (1): определения аргументов командной строки
- (2): Утилиты и обработка ошибок структур Rust
- (3): служебные функции анализа файлов и каталогов.
- (4): функции загрузки, обработки и записи данных.
- (5): точка входа в программу
- (6): код модульного тестирования
Основная программа анализирует аргументы командной строки с помощью Clap, настраивает уровень детализации журнала и вызывает основную функцию обработки данных.
→ Rust «program-main» = #[tokio::main] (1) async fn main() -> Result<(), MDataAppError> { let cli = MDataAppArgs::parse(); (2) let log_level = match cli.verbose { 1 => LevelFilter::Debug, 2 => LevelFilter::Trace, _ => LevelFilter::Info, }; (3) let env = Env::new().filter("MLOG"); let _builder = Builder::new().filter(Some("mdata_app"), log_level).parse_env(env).init(); debug!("Arguments {:#?}", cli); (4) mdata_app(cli).await?; Ok(()) }
Макрос #[tokio::main]
(1) инициализирует среду выполнения tokio и управляет примитивами синхронизации.
В (2) и (3) мы инициализируем детализацию библиотеки журналов на основе крейтов env_logger
и log
. Например, в следующей строке отображаются записи CLI конечного пользователя только на уровне Подробный уровень отладки.
debug!("Arguments {:#?}", cli);
Наконец, (4) функция обработки данных mdata_app
вызывается с аргументами CLI.
Шаг 3 — Разбор аргументов командной строки
Для разбора и обработки аргументов командной строки мы используем clap crate. Эта зависимость добавляется в файлCargo.toml
.
Вы можете использовать ящик для хлопков в двух режимах:
- С помощью Builder API вы программно создаете имена параметров и ограничения.
- С помощью Derive API вы определяете структуру Rust, которая включает все ваши параметры и украшена макросами. Наконец, строится объект clap, определяющий правила синтаксического анализа и проверки. Это API, который мы используем в нашем примере.
Первым шагом с помощью Clap Derive API является создание структуры, определяющей все наши параметры.
Каждая опция описывается своим именем, ограничением типа, документацией и значением по умолчанию.
→ Rust «cli-args» = #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] (1) #[clap(propagate_version = true)] pub struct MDataAppArgs { (2) #[clap(short, long, parse(from_os_str), help = "Input path")] input: PathBuf, #[clap(short, long, parse(from_os_str), help = "Output path")] output: PathBuf, #[clap(short, long, parse(from_occurrences), help = "Verbose level")] verbose: usize, #[clap(short, long, arg_enum, default_value_t = WriteFormat::Undefined, help = "Output format")] format: WriteFormat, #[clap(short, long, default_value_t = 0, help = "Limit the result to the first <limit> rows")] limit: usize, #[clap(short, long, parse(from_flag), help = "Display the inferred schema")] schema: bool, (3) <<subcommand-def>> } (4) <<subcommand-struct>>
- (1): с помощью этого первого макроса Clap использует определенную информацию в файле
Cargo.toml
для отображения информации автора, версии, о для конечный пользователь. - (2): мы добавляем запись структуры со спецификациями для каждого входного аргумента, используя макрос
clap
. - (3) + (4): мы определяем подкоманду хлопка для обработки наших операций по обработке данных. Я опишу этот шаг в следующем разделе.
Использование командной строки
В этой первой версии наши возможности минимальны.
Мы предлагаем только загрузку файла CSV или Parquet с --input
, экспорт его в указанный выходной файл --output
с определенным форматом --format
.
Пользователи могут указать максимальное количество строк, возвращаемых нашей программой, с помощью --limit
.
Наконец, в целях отладки пользователь может отобразить предполагаемую схему данных с помощью --schema
.
Теперь мы можем вызвать команду --help
, чтобы увидеть наши определенные аргументы.
$ cargo run -- --help mdata_app 0.1.0 USAGE: mdata_app [OPTIONS] --input <INPUT> --output <OUTPUT> [SUBCOMMAND] OPTIONS: -f, --format <FORMAT> Output format [default: undefined] [possible values: undefined, csv, parquet] -h, --help Print help information -i, --input <INPUT> Input path -l, --limit <LIMIT> Limit the result to the first <limit> rows [default: 0] -o, --output <OUTPUT> Output path -s, --schema Display the schema -v, --verbose Verbose level -V, --version Print version information SUBCOMMANDS: eq Add equality test in the where clause help Print this message or the help of the given subcommand(s)
Определение подкоманды
Наше основное приложение может загружать файл данных, применять некоторые преобразования и записывать результаты. Опции ввода и вывода управляются в определении первичной структуры CLI.
Для управления операцией преобразования данных мы используем функцию Clap: подкоманды. Он обеспечивает четкое разделение параметров между операциями с данными и атрибутами CLI.
Мы добавляем подкоманду фильтра, которая принимает необязательное определение Filters
. Упоминание Option<>
делает этот аргумент необязательным в нашем окончательном CLI.
→ Rust «subcommand-def» = #[clap(subcommand)] filter: Option<Filters>,
Мы определяем структуру Filter
с ее компонентами. В нашем случае наша первая реализация — это фильтр равенства Eq
. Мы применяем этот фильтр к определенному столбцу и определенному значению.
→ Rust «subcommand-struct» = #[derive(Subcommand, Debug)] enum Filters { /// Add equality test filter Eq { column: String, value: String }, }
С помощью подкоманды команда Clap позволяет создать специальное справочное сообщение для этой операции. Обратите внимание, что справочное сообщение определено в комментарии файла enum
Filters
.
$ cargo run -- eq --help mdata_app-eq 0.1.0 Add equality test filter USAGE: mdata_app --input <INPUT> --output <OUTPUT> eq <COLUMN> <VALUE> ARGS: <COLUMN> <VALUE> OPTIONS: -h, --help Print help information -V, --version Print version information
Заворачивать
Вот и все!
Теперь наша программа может анализировать, проверять и обрабатывать команды конечного пользователя, используя библиотеку Clap. Кроме того, операции с данными можно компоновать с помощью подкоманды Clap.
В следующей статье я описываю пример функции обработки данных с использованием Apache Arrow DataFusion.
Инженерия данных с помощью Rust и Apache Arrow DataFusion 3/4 — загрузка и обработка данных
Добро пожаловать в третью часть моей серии статей «Инженерия данных с помощью Rust и Apache Arrow DataFusion . Получите доступ к…medium.com»
Спасибо за прочтение!