Closed22

enum to iter を考える【Rust】

tkttkt

enum を作って、それを loop で回して、それぞれのタイプについて処理をする、ということをしようと思った

tkttkt

が、enum と iter, vec の変換をする機構は rust は標準では持っていないらしい

tkttkt

が、enum と iter, vec の変換をする機構は rust は標準では持っていないらしい

tkttkt

外部の crate を使えばできるっぽいけど、それは果たして rust っぽいのか

tkttkt
  • 文字列タイプ(例: aから始まる, bから始まる)
  • 文字列(例: boot)

文字列に対して、文字列タイプに応じた処理をやっていく、みたいなことはできるけど、
文字列がどの文字列タイプにマッチしてるのか確認してから、処理する、
みたいな流れにすれば、タイプで loop させなくてもできそう

tkttkt

例が雑なのは、あとでできればコードサンプル載せたい

tkttkt

文字列がどの文字列タイプにマッチしてるのか確認してから、処理する、

のためには、どっちみち文字列タイプをループさせる必要があるから、ダメそう

tkttkt
#[derive(Debug)]
enum DiscordStringType {
    Channel,
    Role,
    Emoji,
    Mention,
}
impl DiscordStringType {
    fn to_regex(&self) -> Regex {
        match self {
            DiscordStringType::Channel => Regex::new(r"<#[0-9]+?>").unwrap(),
            DiscordStringType::Role => Regex::new(r"<@&[0-9]+?>").unwrap(),
            DiscordStringType::Emoji => Regex::new(r"<:(.+?):[0-9]+?>").unwrap(),
            DiscordStringType::Mention => Regex::new(r"<@![0-9]+?>").unwrap(),
        }
    }
    fn to_convert_type(&self) -> ConvertType {
        match self {
            DiscordStringType::Channel => ConvertType::Empty,
            DiscordStringType::Role => ConvertType::Empty,
            DiscordStringType::Emoji => ConvertType::MatchString,
            DiscordStringType::Mention => ConvertType::Empty,
        }
    }
    // TODO: このままだと変換に漏れがあるので修正したい
    fn values() -> Vec<DiscordStringType> {
        vec![
            DiscordStringType::Channel,
            DiscordStringType::Role,
            DiscordStringType::Emoji,
            DiscordStringType::Mention,
        ]
    }
}

enum ConvertType {
    Empty,
    MatchString,
}
impl ConvertType {
    fn convert(&self, regex: &Regex, str: &str) -> String {
        match self {
            ConvertType::Empty => regex.replace_all(str, "").to_string(),
            ConvertType::MatchString => regex.captures_iter(str)
                .collect::<Vec<_>>()
                .iter()
                .fold(str.to_string(), |acc, cap| {
                    acc.replace(&cap[0], &cap[1])
                }).to_string(),
        }
    }
}

fn convert_discord_string(str: &str) -> String {
    let mut converted = str.to_string();
    for type_ in DiscordStringType::values() {
        let re = type_.to_regex();
        let convert_type = type_.to_convert_type();
        converted = convert_type.convert(
            &re,
            &converted,
        );
    }
    converted
}
tkttkt
enum DiscordStringType {
    Channel,
    Role,
    Emoji,
    Mention,
}
impl DiscordStringType {
    // TODO: このままだと変換に漏れがあるので修正したい
    fn values() -> Vec<DiscordStringType> {
        vec![
            DiscordStringType::Channel,
            DiscordStringType::Role,
            DiscordStringType::Emoji,
            DiscordStringType::Mention,
        ]
    }
}

なんとかならんもんか

tkttkt

enum -> vec した場合、型の厳格性が失われるからあんまりやりたくないとかあるのかな

tkttkt

str -> enum を regex の pattern match で変換できれば良き。
そんなことはできるんだろうか
かけなくはなさそうだけど書き方はわからない

tkttkt
fn str_to_discord_string_type(str: &str) -> Option<DiscordStringType> {
    match str {
        Regex::new(r"<#[0-9]+?>") => Some(DiscordStringType::Channel),
        Regex::new(r"<@&[0-9]+?>") => Some(DiscordStringType::Role),
        Regex::new(r"<:(.+?):[0-9]+?>") => Some(DiscordStringType::Emoji),
        Regex::new(r"<@![0-9]+?>") => Some(DiscordStringType::Mention),
        _ => None,
    }
}

こういうかんじに書きたいけどかけない。ぴえん

tkttkt

この書き方するなら、if で一つづつ書いても変わらなさそう

tkttkt
enum DiscordStringType {
    Channel,
    Role,
    Emoji,
    Mention,
}
impl DiscordStringType {
    fn to_regex(&self) -> Regex {
        match self {
            DiscordStringType::Channel => Regex::new(r"<#[0-9]+?>").unwrap(),
            DiscordStringType::Role => Regex::new(r"<@&[0-9]+?>").unwrap(),
            DiscordStringType::Emoji => Regex::new(r"<:(.+?):[0-9]+?>").unwrap(),
            DiscordStringType::Mention => Regex::new(r"<@![0-9]+?>").unwrap(),
        }
    }
    fn to_convert_type(&self) -> ConvertType {
        match self {
            DiscordStringType::Channel => ConvertType::Empty,
            DiscordStringType::Role => ConvertType::Empty,
            DiscordStringType::Emoji => ConvertType::MatchString,
            DiscordStringType::Mention => ConvertType::Empty,
        }
    }
}

enum ConvertType {
    Empty,
    MatchString,
}
impl ConvertType {
    fn convert(&self, regex: &Regex, str: &str) -> String {
        match self {
            ConvertType::Empty => regex.replace_all(str, "").to_string(),
            ConvertType::MatchString => regex
                .captures_iter(str)
                .collect::<Vec<_>>()
                .iter()
                .fold(str.to_string(), |acc, cap| acc.replace(&cap[0], &cap[1])),
        }
    }
}

fn convert_discord_string(str: &str) -> String {
    let (re, convert_type) = if let Some(type_) = str_to_discord_string_type(str) {
        (type_.to_regex(), type_.to_convert_type())
    } else {
        return str.to_string();
    };
    convert_discord_string(&convert_type.convert(&re, str))
}

fn str_to_discord_string_type(str: &str) -> Option<DiscordStringType> {
    if (Regex::new(r"<#[0-9]+?>").unwrap()).is_match(str) {
        Some(DiscordStringType::Channel)
    } else if (Regex::new(r"<@&[0-9]+?>").unwrap()).is_match(str) {
        Some(DiscordStringType::Role)
    } else if (Regex::new(r"<:(.+?):[0-9]+?>").unwrap()).is_match(str) {
        Some(DiscordStringType::Emoji)
    } else if (Regex::new(r"<@![0-9]+?>").unwrap()).is_match(str) {
        Some(DiscordStringType::Mention)
    } else {
        None
    }
}

なんか遠回しな感じ…

tkttkt
enum DiscordStringType {
    Channel,
    Role,
    Emoji,
    Mention,
}
impl DiscordStringType {
    fn to_regex(&self) -> Regex {
        match self {
            DiscordStringType::Channel => Regex::new(r"<#[0-9]+?>").unwrap(),
            DiscordStringType::Role => Regex::new(r"<@&[0-9]+?>").unwrap(),
            DiscordStringType::Emoji => Regex::new(r"<:(.+?):[0-9]+?>").unwrap(),
            DiscordStringType::Mention => Regex::new(r"<@![0-9]+?>").unwrap(),
        }
    }
    fn to_convert_type(&self) -> ConvertType {
        match self {
            DiscordStringType::Channel => ConvertType::Empty,
            DiscordStringType::Role => ConvertType::Empty,
            DiscordStringType::Emoji => ConvertType::MatchString,
            DiscordStringType::Mention => ConvertType::Empty,
        }
    }
    fn from_str(s: &str) -> Option<DiscordStringType> {
        let type_ = DiscordStringType::Channel;
        if type_.to_regex().is_match(s) {
            return Some(type_);
        }
        let type_ = DiscordStringType::Role;
        if type_.to_regex().is_match(s) {
            return Some(type_);
        }
        let type_ = DiscordStringType::Emoji;
        if type_.to_regex().is_match(s) {
            return Some(type_);
        }
        let type_ = DiscordStringType::Mention;
        if type_.to_regex().is_match(s) {
            return Some(type_);
        }
        None
    }
}

enum ConvertType {
    Empty,
    MatchString,
}
impl ConvertType {
    fn convert(&self, regex: &Regex, str: &str) -> String {
        match self {
            ConvertType::Empty => regex.replace_all(str, "").to_string(),
            ConvertType::MatchString => regex
                .captures_iter(str)
                .collect::<Vec<_>>()
                .iter()
                .fold(str.to_string(), |acc, cap| acc.replace(&cap[0], &cap[1])),
        }
    }
}

fn convert_discord_string(str: &str) -> String {
    let (re, convert_type) = if let Some(type_) = DiscordStringType::from_str(str) {
        (type_.to_regex(), type_.to_convert_type())
    } else {
        return str.to_string();
    };
    convert_discord_string(&convert_type.convert(&re, str))
}

やっぱり、from_str の条件分岐を書き忘れるとダメそうなのが気になる

このスクラップは2022/09/21にクローズされました