🔗

Rust でコードを書いているタイミングで URLのパースのエラーを検知する

2022/05/29に公開

Rust でコードを書いているタイミングで URLのパースのエラーを検知するマクロ(safe_url!)を作成してみました. ( https://crates.io/ には公開していないので, コピペしてカスタマイズして使ってください. )

普通の書き方

#[test]
fn test() {
    let example_com = url::Url::parse("https//example.com").unwrap();
    println!("example_com is {:?}", &example_com);
}

この書き方だと実行時にエラーが発生してしまう. 実行してからすぐに通る処理なら, ミスにすぐ気付けるが, めったに通らない処理だと指定ミスに気づけない.

今回作成した safe_url! を使うと

不正なURL (https//example.com) を指定しているとき

正常なURL (https://example.com) を指定しているとき

このように実行前にミスを検知できる.

Cargo.toml
[package]
name = "safe-url-macro"
version = "0.1.0"
edition = "2021"

[lib]
proc-macro = true

[dependencies]
url = "2.2.2"
quote = "1.0.18"
syn = "1.0.95"

lib/src
#[proc_macro]
pub fn safe_url(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    match syn::parse::<syn::LitStr>(input) {
        Ok(literal) => {
            let string_literal_value = literal.value();
            match url::Url::parse(&string_literal_value) {
                Ok(_safe_url) => quote::quote! {
                    url::Url::parse(#string_literal_value).unwrap()
                }
                .into(),
                Err(error) => {
                    let message =
                        string_literal_value + " is not valid url. (" + &error.to_string() + ")";
                    quote::quote! {compile_error!(#message)}.into()
                }
            }
        }
        Err(_) => quote::quote! {compile_error!("safe_url macro support string literal")}.into(),
    }
}

tests/test.rs
#[test]
fn test() {
    let example_com = safe_url_macro::safe_url!("https//example.com");
    println!("example_com is {:?}", &example_com);
}

https://github.com/narumincho/safe-url-macro

URLの指定はテキストエディタによっては表示が変わったりするのでミスには気づきやすいけれど, ミスをするときがあるので, コンパイル時にバリデーションすることは大切.

const EXAMPLE_COM: url::Url = safe_url_macro::safe_url!("https://example.com");

このように const で定数としてかければ更に良かったけど, url::Url::parseconst fnではなかったため できなかった. URL パースの処理を const fn で自作すればできそう

こういうコンパイル時に色々検査できる機能が増えると良いなぁ

正規表現(Regex)にはこの proc-macro-regex が使えそう?

https://crates.io/crates/proc-macro-regex

GitHubで編集を提案

Discussion