1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
use crate::{Error, MessagePath, Msg, Result};
use lazy_static::lazy_static;
use regex::RegexBuilder;
use serde_derive::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::fmt;
use std::fmt::Formatter;
/// A ROS service parsed from a `srv` file.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(into = "SrvSerde")]
#[serde(try_from = "SrvSerde")]
pub struct Srv {
path: MessagePath,
source: String,
req: Msg,
res: Msg,
}
impl fmt::Display for Srv {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.source.fmt(f)
}
}
impl Srv {
/// Create a service from a passed in path and source.
///
/// # Errors
///
/// Returns an error if there is an error parsing the service source.
///
/// # Examples
///
/// ```
/// # use ros_message::Srv;
/// # use std::convert::TryInto;
/// #
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let service = Srv::new(
/// "foo/Bar".try_into()?,
/// r#"# a comment that is ignored
/// Header header
/// uint32 a
/// byte[16] b
/// geometry_msgs/Point[] point
/// uint32 FOO=5
/// string SOME_TEXT=this is # some text, don't be fooled by the hash
/// ---
/// uint32 a
/// geometry_msgs/Point[] point
/// uint32 FOO=6
/// "#,
/// )?;
///
/// assert_eq!(service.path(), &"foo/Bar".try_into()?);
/// assert_eq!(service.request().fields().len(), 6);
/// assert_eq!(service.response().fields().len(), 3);
/// # Ok(())
/// # }
/// ```
pub fn new(path: MessagePath, source: impl Into<String>) -> Result<Srv> {
let source = source.into();
let (req, res) = Self::build_req_res(&path, &source)?;
Ok(Srv {
path,
source,
req,
res,
})
}
/// Returns the path of the service.
pub fn path(&self) -> &MessagePath {
&self.path
}
/// Returns the original source.
pub fn source(&self) -> &str {
&self.source
}
/// Returns the request message.
pub fn request(&self) -> &Msg {
&self.req
}
/// Returns the response message.
pub fn response(&self) -> &Msg {
&self.res
}
fn build_req_res(path: &MessagePath, source: &str) -> Result<(Msg, Msg)> {
lazy_static! {
static ref RE_SPLIT: regex::Regex = RegexBuilder::new("^---$")
.multi_line(true)
.build()
.expect("Invalid regex `^---$`");
}
let (req, res) = match RE_SPLIT.split(source).collect::<Vec<_>>().as_slice() {
&[req] => (req, ""),
&[req, res] => (req, res),
&[] => {
return Err(Error::BadMessageContent(format!(
"Service {} does not have any content",
path
)))
}
v => {
return Err(Error::BadMessageContent(format!(
"Service {} is split into {} parts",
path,
v.len()
)))
}
};
Ok((
Msg::new(path.peer(format!("{}Req", path.name())), req)?,
Msg::new(path.peer(format!("{}Res", path.name())), res)?,
))
}
}
#[derive(Serialize, Deserialize)]
struct SrvSerde {
path: MessagePath,
source: String,
}
impl TryFrom<SrvSerde> for Srv {
type Error = Error;
fn try_from(src: SrvSerde) -> Result<Self> {
Self::new(src.path, &src.source)
}
}
impl From<Srv> for SrvSerde {
fn from(src: Srv) -> Self {
Self {
path: src.path,
source: src.source,
}
}
}