ros_message/
message_path.rs
1use crate::{Error, Result};
2use lazy_static::lazy_static;
3use regex::Regex;
4use serde_derive::{Deserialize, Serialize};
5use std::convert::TryFrom;
6use std::fmt::{Display, Formatter};
7use std::hash::Hash;
8
9#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
11#[serde(into = "String")]
12#[serde(try_from = "&str")]
13pub struct MessagePath {
14 package: String,
15 name: String,
16}
17
18pub fn is_valid_package_name(package: &str) -> bool {
19 lazy_static! {
20 static ref RE_PACKAGE_CORRECT_CHAR_SET_AND_LENGTH: Regex =
21 Regex::new("^[a-z][a-z0-9_]+$").unwrap();
22 static ref RE_PACKAGE_CONSECUTIVE_UNDERSCORE: Regex = Regex::new("__").unwrap();
23 }
24 RE_PACKAGE_CORRECT_CHAR_SET_AND_LENGTH.is_match(package)
25 && !RE_PACKAGE_CONSECUTIVE_UNDERSCORE.is_match(package)
26}
27
28impl MessagePath {
29 pub fn new(package: impl Into<String>, name: impl Into<String>) -> Result<Self> {
53 let package = package.into();
54 let name = name.into();
55 if !is_valid_package_name(&package) {
56 return Err(Error::InvalidMessagePath {
57 name: format!("{}/{}",package,name),
58 reason: "package name needs to follow REP 144 rules (https://www.ros.org/reps/rep-0144.html)".into(),
59 });
60 }
61 Ok(Self { package, name })
62 }
63
64 pub fn peer(&self, name: impl Into<String>) -> Self {
82 Self {
83 package: self.package.clone(),
84 name: name.into(),
85 }
86 }
87
88 fn from_combined(input: &str) -> Result<Self> {
89 let parts = input.splitn(3, '/').collect::<Vec<&str>>();
90 match parts[..] {
91 [package, name] => Self::new(package, name),
92 _ => Err(Error::InvalidMessagePath {
93 name: input.into(),
94 reason: "string needs to be in `package/name` format".into(),
95 }),
96 }
97 }
98
99 pub fn package(&self) -> &str {
101 &self.package
102 }
103
104 pub fn name(&self) -> &str {
106 &self.name
107 }
108}
109
110impl Display for MessagePath {
111 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
112 write!(f, "{}/{}", self.package(), self.name())
113 }
114}
115
116impl<'a> TryFrom<&'a str> for MessagePath {
117 type Error = Error;
118
119 fn try_from(value: &'a str) -> Result<Self> {
120 Self::from_combined(value)
121 }
122}
123
124impl From<MessagePath> for String {
125 fn from(src: MessagePath) -> Self {
126 format!("{}", src)
127 }
128}