coreon-core 0.1.0

Core abstractions for camel-rs: Exchange, Processor, Endpoint, Component, CamelContext.
Documentation
//! Camel-style URI: `scheme:path?k1=v1&k2=v2`.
//!
//! We don't use `url::Url` directly because Camel endpoints often use schemes
//! that aren't registered (e.g. `direct:foo`, `timer:tick`), which `url::Url`
//! parses into hostless authority forms that complicate path extraction.

use crate::error::{CamelError, Result};
use std::collections::HashMap;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CamelUri {
    pub scheme: String,
    pub path: String,
    pub params: HashMap<String, String>,
    raw: String,
}

impl CamelUri {
    pub fn parse(s: &str) -> Result<Self> {
        let (scheme, rest) = s.split_once(':').ok_or_else(|| CamelError::InvalidUri {
            uri: s.to_owned(),
            reason: "missing scheme separator ':'".to_owned(),
        })?;
        if scheme.is_empty() {
            return Err(CamelError::InvalidUri {
                uri: s.to_owned(),
                reason: "empty scheme".to_owned(),
            });
        }

        let (path, query) = match rest.split_once('?') {
            Some((p, q)) => (p, Some(q)),
            None => (rest, None),
        };

        let mut params = HashMap::new();
        if let Some(q) = query {
            for pair in q.split('&').filter(|p| !p.is_empty()) {
                match pair.split_once('=') {
                    Some((k, v)) => {
                        params.insert(k.to_owned(), v.to_owned());
                    }
                    None => {
                        params.insert(pair.to_owned(), String::new());
                    }
                }
            }
        }

        Ok(Self {
            scheme: scheme.to_owned(),
            path: path.to_owned(),
            params,
            raw: s.to_owned(),
        })
    }

    pub fn as_str(&self) -> &str {
        &self.raw
    }

    pub fn get_param(&self, key: &str) -> Option<&str> {
        self.params.get(key).map(|s| s.as_str())
    }
}

impl std::fmt::Display for CamelUri {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&self.raw)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parses_simple_uri() {
        let u = CamelUri::parse("direct:foo").expect("parse");
        assert_eq!(u.scheme, "direct");
        assert_eq!(u.path, "foo");
        assert!(u.params.is_empty());
    }

    #[test]
    fn parses_with_params() {
        let u = CamelUri::parse("timer:tick?period=1000&fixedRate=true").expect("parse");
        assert_eq!(u.scheme, "timer");
        assert_eq!(u.path, "tick");
        assert_eq!(u.get_param("period"), Some("1000"));
        assert_eq!(u.get_param("fixedRate"), Some("true"));
    }

    #[test]
    fn rejects_missing_scheme() {
        assert!(CamelUri::parse("no-scheme").is_err());
        assert!(CamelUri::parse(":empty-scheme").is_err());
    }
}