Skip to main content

headless_lms_server/domain/oauth/
resource_indicator.rs

1use serde::{Deserialize, Deserializer, Serialize};
2
3/// An RFC 8707 resource indicator, without trailing slashes, so that it compares equal to the
4/// resource URLs tokens are bound to.
5#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
6#[serde(transparent)]
7pub struct ResourceIndicator(String);
8
9impl ResourceIndicator {
10    /// The indicator for `resource`, without trailing slashes.
11    pub fn new(resource: &str) -> Self {
12        Self(resource.trim_end_matches('/').to_string())
13    }
14
15    /// The indicator as a URL string.
16    pub fn as_str(&self) -> &str {
17        &self.0
18    }
19}
20
21impl<'de> Deserialize<'de> for ResourceIndicator {
22    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
23        String::deserialize(deserializer).map(|resource| Self::new(&resource))
24    }
25}
26
27#[cfg(test)]
28mod tests {
29    use super::*;
30
31    #[test]
32    fn trailing_slashes_are_dropped() {
33        assert_eq!(
34            ResourceIndicator::new("https://example.com/api/v0/mcp/").as_str(),
35            "https://example.com/api/v0/mcp"
36        );
37    }
38}