Создание CLI в Rust для исполнения гитарных аккордов

Играете на гитаре и вынуждены постоянно искать в интернете аккорды для исполнения песен с менее распространенными аккордами?

А как насчет инструмента командной строки (CLI), который принимает на вход название аккорда и выводит аппликатуру, отображающую способ его исполнения? 

В статье мы реализуем эту идею и с нуля создадим такой CLI в Rust. 

Конечная цель 

Итоговый результат работы  —  создание CLI с именем chord. Он принимает на вход одну строку, а именно название аккорда, и выводит его аппликатуру. Пример: 

$ chord C
x ◯ ◯
┌─┬─┬─┬─┬─┐
│ │ │ │ ◯ │
├─┼─┼─┼─┼─┤
│ │ ◯ │ │ │
├─┼─┼─┼─┼─┤
│ ◯ │ │ │ │
└─┴─┴─┴─┴─┘

Почему Rust? 

Rust выбран не случайно. По результатам опроса Stack Overflow, проведенного в 2021 году, он является самым излюбленным языком программирования: 

Rust отлично подходит как для создания бэкенд-сервисов, так и CLI. 

К делу! 

Установка Rust

Устанавливаем Rust следующим образом: 

$ curl --proto '=https' --tlsv1.2 -sSf <https://sh.rustup.rs> | sh

Более подробная информация по установке предоставлена по ссылке.

Создание нового проекта Rust

$ cargo new chord --bin

Created binary (application) `chord` package

Компилируем и запускаем программу, проверяя корректность всех настроек: 

$ cd chord
$ cargo run

Hello, world!

Полученный результат: 

|-Cargo.toml
|-Cargo.lock
|-src
| |-main.rs

Вывод грифа 

У гитары 6 струн. Как правило, для отображения постановки пальцев используется гриф. 

На данный момент main.rs выглядит так: 

fn main() {
println!("Hello, world!");
}

Внесем в него изменения для вывода пустого грифа: 

const FRETBOARD: &str = "◯ ◯ ◯ ◯ ◯ ◯
┌─┬─┬─┬─┬─┐
│ │ │ │ │ │
├─┼─┼─┼─┼─┤
│ │ │ │ │ │
├─┼─┼─┼─┼─┤
│ │ │ │ │ │
└─┴─┴─┴─┴─┘";
fn main() {
println!("{}", FRETBOARD);
}

Выполняем программу и получаем следующий результат: 

$ cargo run
◯ ◯ ◯ ◯ ◯ ◯
┌─┬─┬─┬─┬─┐
│ │ │ │ │ │
├─┼─┼─┼─┼─┤
│ │ │ │ │ │
├─┼─┼─┼─┼─┤
│ │ │ │ │ │
└─┴─┴─┴─┴─┘

Clap

Для парсинга аргументов командной строки потребуется библиотека Clap.

Присоединяем к проекту Clap в качестве зависимости: 

$ cargo add clap --features derive

Эта команда просто добавляет следующую строку в cargo.toml, где описаны зависимости для Rust: 

clap = { version = "3.2.21", features = ["derive"] }

Обновляем main.rs таким образом: 

const FRETBOARD: &str = "◯ ◯ ◯ ◯ ◯ ◯ 
┌─┬─┬─┬─┬─┐
│ │ │ │ │ │
├─┼─┼─┼─┼─┤
│ │ │ │ │ │
├─┼─┼─┼─┼─┤
│ │ │ │ │ │
└─┴─┴─┴─┴─┘";

use clap::Parser;

/// CLI, показывающий, как сыграть гитарный аккорд
#[derive(Parser, Debug)]
#[clap(version, about)]
struct Args {
   /// Название аккорда
   #[clap()]
   name: String,
}

fn main() {
    let args = Args::parse();

    println!("This is how you play '{}' chord: \n{}", args.name, FRETBOARD);
}

Макросы Rust значительно упрощают для Clap процедуру аннотации Args и обеспечивают бесплатный парсинг аргументов: 

/// CLI, показывающий, как сыграть гитарный аккорд
#[derive(Parser, Debug)]
#[clap(version, about)]
struct Args {
/// Название аккорда
#[clap()]
name: String,
}

Проверяем: 

$ cargo run -- --help

chord 0.1.0
A CLI to show you how to play a guitar chord

USAGE:
    chord <NAME>

ARGS:
    <NAME>    Name of the chord

OPTIONS:
    -h, --help       Print help information
    -V, --version    Print version information

$ cargo run -- C

This is how you play 'C' chord: 
◯ ◯ ◯ ◯ ◯ ◯ 
┌─┬─┬─┬─┬─┐
│ │ │ │ │ │
├─┼─┼─┼─┼─┤
│ │ │ │ │ │
├─┼─┼─┼─┼─┤
│ │ │ │ │ │
└─┴─┴─┴─┴─┘

Двойное тире --  —  это общепринятый способ указать на окончание параметров для cargo, а остальное передается программе CLI. Такой принцип действия характерен не только для cargo, но и многих других shell-команд. 

Есть еще одна скрытая особенность, на которую стоит обратить внимание. Заметили, что A CLI to show you how to play a guitar chord (CLI, показывающий, как сыграть гитарный аккорд) и Name of the chord (Название аккорда) являются комментариями в исходном коде? Они также включены в справочное сообщение. 

Из исходного кода:

/// A CLI to show you how to play a guitar chord
#[derive(Parser, Debug)]
#[clap(version, about)]
struct Args {
/// Name of the chord
#[clap()]
name: String,
}

Из вывода:

chord 0.1.0
A CLI to show you how to play a guitar chord

USAGE:
    chord <NAME>

ARGS:
    <NAME>    Name of the chord

Это вся необходимая информация, которую нужно знать о Clap. Переходим непосредственно к самому CLI. 

CLI

Обновляем функцию main следующим образом: 

fn main() {
let args = Args::parse();

let chords: HashMap<&str, &str> =
HashMap::from([("C", "x32010"), ("G", "320003"), ("D", "xx0232")]);

match chords.get(&args.name[..]) {
None => println!("Unknown chord '{}'", args.name),
Some(pattern) => {
let mut board: Vec<char> = FRETBOARD.chars().collect();
for (i, ch) in pattern.chars().enumerate() {
let idx: usize = i * 2;
if ch == 'x' {
board[idx] = ch
} else {
let value: usize = ch.to_digit(10).unwrap() as usize;
board[idx] = ' ';
board[idx + 24 * value] = '◯'
}
}
println!(
"This is how you play '{}' chord: \n{}",
args.name,
board.iter().collect::<String>()
)
}
}
}

Если название входного аккорда неизвестно, то выводится сообщение об ошибке Unknown chord. В противном случае мы накладываем постановку пальцев поверх пустого грифа, представленного ранее.

Тестируем код:

$ cargo run -- C                    

This is how you play 'C' chord: 
x     ◯   ◯
┌─┬─┬─┬─┬─┐
│ │ │ │ ◯ │
├─┼─┼─┼─┼─┤
│ │ ◯ │ │ │
├─┼─┼─┼─┼─┤
│ ◯ │ │ │ │
└─┴─┴─┴─┴─┘

$ cargo run -- Asus4

Unknown chord 'Asus4'

Отличный результат! Быстро и просто!

Установка 

До сих пор мы компилировали и запускали CLI. cargo занимается не только сборкой, выполнением, управлением зависимостями библиотек, но и установкой. 

Устанавливаем CLI chord:

cargo install --path .

И работаем с данным CLI напрямую: 

$ chord G

This is how you play 'G' chord:
◯ ◯ ◯
┌─┬─┬─┬─┬─┐
│ │ │ │ │ │
├─┼─┼─┼─┼─┤
│ ◯ │ │ │ │
├─┼─┼─┼─┼─┤
◯ │ │ │ │ ◯
└─┴─┴─┴─┴─┘

Теперь вы знаете, как создать CLI в Rust. 

Заключение 

Забыли, как сыграть аккорд? Не беда  —  у вас под рукой отличный инструмент.

Переходите по ссылке в ветку blog-post-checkpoint, где предоставлен код для данной статьи. С новейшей версией CLI можно ознакомиться по ссылке на ветке master

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Yuchen Z.: Build a Command Line Tool With Rust to Play Guitar Chords

Предыдущая статьяПревращаем субъективные понятия в объективные метрики: простая методика
Следующая статья7 самых популярных библиотек React