Rust

Rust로 간단한 CLI 유효성 검사 도구 만들기

드리프트2 2024. 11. 24. 21:40

Rust로 간단한 CLI 유효성 검사 도구 만들기

개요

Rust 공부하면서 배운 문법으로 간단한 무언가를 만들어보고 싶어서 이번에 소개할 유효성 검사 도구를 만들어봤습니다.

 

간단한 수준이지만 데이터 형식에 대한 기본적인 유효성 검사를 수행하는 프로그램입니다.

 

Rust 초보자로서 완벽한 코드를 작성했다는 자신은 없고, ChatGPT의 도움도 꽤 많이 받았습니다. 초보자가 만든 작은 프로젝트로 봐주시면 감사하겠습니다.


프로젝트 설정: Cargo.toml

[package]
name = "validation-tool"
version = "0.1.0"
edition = "2021"

[dependencies]
dialoguer = "0.11"
ferris-says = "0.3.1"
regex = "1.8"

 

Rust 입문을 위한 프로젝트이기 때문에 모든 것을 처음부터 작성하고 싶었지만, 너무 어려운 부분에서 막히고 싶지 않아 3개의 크레이트(dependency)를 사용했습니다.

 

각 크레이트의 역할은 다음과 같습니다.

  • dialoguer
    터미널에서 여러 선택지를 제공하고, 위아래 키로 선택할 수 있는 UI를 만들 수 있습니다. 프론트엔드 프레임워크 초기 설정 시 자주 보이는 방식이어서 해보고 싶었습니다.
  • ferris-says
    Rust 커뮤니티의 비공식 마스코트인 🦀 Ferris가 메시지를 출력하는 UI를 제공합니다. Rust 공식 사이트의 "시작하기" 섹션에서 사용되어 귀여워서 추가했습니다.
  • regex
    정규식을 다룰 수 있습니다. 전화번호 형식을 검사하는 데 사용했습니다.

유효성 검사 파일

main.rs 외에 checker.rs라는 유효성 검사를 담당하는 파일을 작성했습니다. 전체 코드는 아래와 같습니다.

checker.rs

use regex::Regex;
use std::fmt;

pub enum CheckerType {
    Number,
    Boolean,
    Japanese,
    PhoneNumber,
}

impl CheckerType {
    fn name(&self) -> String {
        match self {
            CheckerType::Number => String::from("숫자"),
            CheckerType::Boolean => String::from("참/거짓"),
            CheckerType::Japanese => String::from("한국어"),
            CheckerType::PhoneNumber => String::from("전화번호"),
        }
    }

    pub fn check(&self, input: &str) -> bool {
        match self {
            CheckerType::Number => check_number(input),
            CheckerType::Boolean => check_boolean(input),
            CheckerType::Japanese => check_japanese(input),
            CheckerType::PhoneNumber => check_phone_number(input),
        }
    }
}

impl fmt::Display for CheckerType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.name())
    }
}

fn check_number(input: &str) -> bool {
    input.parse::<f64>().is_ok()
}

fn check_boolean(input: &str) -> bool {
    input == "true" || input == "false"
}

fn check_japanese(input: &str) -> bool {
    input.chars().any(|c| matches!(c, '\u{30e0}'..='\u{9fcf}' | '\u{3005}'..='\u{3006}' | '\u{3040}'..='\u{309F}' | '\u{30A0}'..='\u{30FF}'))
}

fn check_phone_number(input: &str) -> bool {
    let phone_regex = Regex::new(r"^\d{2,4}-\d{2,4}-\d{4}$").unwrap();
    phone_regex.is_match(input)
}

주요 코드 설명

  • CheckerType 열거형은 유효성 검사 타입을 정의합니다. 여기에는 "숫자", "참/거짓", "한국어", "전화번호"가 포함되어 있습니다.
    "문자열"이나 "배열" 같은 복잡한 타입은 제외했고, 간단하고 재미로 할 수 있는 조합으로 선택했습니다.
  • name 메서드는 CheckerType의 각 타입에 따라 적절한 한국어 이름을 반환합니다.
  • check 메서드는 입력값에 따라 적절한 유효성 검사 함수를 호출합니다.
  • fmt::Display 트레이트를 구현하여 CheckerType을 문자열로 변환할 수 있도록 했습니다. 이는 dialoguer의 선택지로 사용할 수 있도록 하기 위한 것입니다.

각 타입에 대한 유효성 검사 함수도 간단하게 작성했습니다.


Main 함수 구현

이제 main.rs를 작성해보겠습니다. 전체 코드는 아래와 같습니다.

main.rs

mod checker;
use checker::CheckerType;
use dialoguer::{console::Term, theme::ColorfulTheme, Select};
use ferris_says::say;
use std::io::{self, stdout, BufWriter, Write};

fn main() {
    let validation_types = vec![
        CheckerType::Number,
        CheckerType::Boolean,
        CheckerType::Japanese,
        CheckerType::PhoneNumber,
    ];

    let selection = Select::with_theme(&ColorfulTheme::default())
        .with_prompt("유효성 검사 타입을 선택하세요")
        .items(&validation_types)
        .default(0)
        .interact_on_opt(&Term::stdout())
        .unwrap();

    let validation_type = &validation_types[selection.unwrap()];

    print!("유효성 검사를 수행할 데이터를 입력하세요: ");
    io::stdout().flush().unwrap();

    let mut input = String::new();
    io::stdin().read_line(&mut input).unwrap();
    let input = input.trim();

    let result = validation_type.check(input);

    let stdout = stdout();
    let message = if result {
        format!("검사 결과, '{}'는(은) {}입니다", input, validation_type)
    } else {
        format!("검사 결과, '{}'는(은) {}이(가) 아닙니다", input, validation_type)
    };
    let width = message.chars().count() + 4;
    let mut writer = BufWriter::new(stdout.lock());
    say(&message, width, &mut writer).unwrap();
}

주요 코드 설명

  • validation_types라는 벡터에 CheckerType을 정의한 후, dialoguer를 이용해 터미널에서 선택할 수 있도록 합니다.
  • dialoguer에서 선택한 옵션은 Option<usize>로 반환되므로, unwrap을 통해 선택된 값을 가져옵니다.
  • 이후 사용자로부터 입력을 받고, 선택된 유효성 검사 타입에 따라 결과를 출력합니다.
  • 최종적으로 ferris_says::say를 이용해 Ferris가 검사 결과를 출력하도록 했습니다.