Codex Handbook
plugin/src/plugin_id.rs 64 lines
//! Stable plugin identifier parsing and validation shared with the plugin cache.#[derive(Debug, thiserror::Error)]pub enum PluginIdError {    #[error("{0}")]    Invalid(String),}#[derive(Debug, Clone, PartialEq, Eq)]pub struct PluginId {    pub plugin_name: String,    pub marketplace_name: String,}impl PluginId {    pub fn new(plugin_name: String, marketplace_name: String) -> Result<Self, PluginIdError> {        validate_plugin_segment(&plugin_name, "plugin name").map_err(PluginIdError::Invalid)?;        validate_plugin_segment(&marketplace_name, "marketplace name")            .map_err(PluginIdError::Invalid)?;        Ok(Self {            plugin_name,            marketplace_name,        })    }    pub fn parse(plugin_key: &str) -> Result<Self, PluginIdError> {        let Some((plugin_name, marketplace_name)) = plugin_key.rsplit_once('@') else {            return Err(PluginIdError::Invalid(format!(                "invalid plugin key `{plugin_key}`; expected <plugin>@<marketplace>"            )));        };        if plugin_name.is_empty() || marketplace_name.is_empty() {            return Err(PluginIdError::Invalid(format!(                "invalid plugin key `{plugin_key}`; expected <plugin>@<marketplace>"            )));        }        Self::new(plugin_name.to_string(), marketplace_name.to_string()).map_err(|err| match err {            PluginIdError::Invalid(message) => {                PluginIdError::Invalid(format!("{message} in `{plugin_key}`"))            }        })    }    pub fn as_key(&self) -> String {        format!("{}@{}", self.plugin_name, self.marketplace_name)    }}/// Validates a single path segment used in plugin IDs and cache layout.pub fn validate_plugin_segment(segment: &str, kind: &str) -> Result<(), String> {    if segment.is_empty() {        return Err(format!("invalid {kind}: must not be empty"));    }    if !segment        .chars()        .all(|ch| ch.is_ascii_alphanumeric() || ch == '-' || ch == '_')    {        return Err(format!(            "invalid {kind}: only ASCII letters, digits, `_`, and `-` are allowed"        ));    }    Ok(())}