use super::error::{BrowserProtocolError, BrowserProtocolField, BrowserProtocolToken};
use std::net::IpAddr;
#[derive(Debug)]
pub struct InstanceInfo {
pub addr: IpAddr,
pub server_name: String,
pub instance_name: String,
pub is_clustered: bool,
pub version: String,
pub np_info: Option<NamedPipeInfo>,
pub tcp_info: Option<TcpInfo>,
pub via_info: Option<ViaInfo>,
pub rpc_info: Option<RpcInfo>,
pub spx_info: Option<SpxInfo>,
pub adsp_info: Option<AdspInfo>,
pub bv_info: Option<BvInfo>,
}
#[derive(Debug)]
pub struct NamedPipeInfo {
pub name: String,
}
#[derive(Debug)]
pub struct TcpInfo {
pub port: u16,
}
#[derive(Debug)]
pub struct ViaInfo {
pub machine_name: String,
pub addresses: Vec<ViaAddress>,
}
#[derive(Debug)]
pub struct ViaAddress {
pub nic: String,
pub port: String,
}
#[derive(Debug)]
pub struct RpcInfo {
pub computer_name: String,
}
#[derive(Debug)]
pub struct SpxInfo {
pub service_name: String,
}
#[derive(Debug)]
pub struct AdspInfo {
pub object_name: String,
}
#[derive(Debug)]
pub struct BvInfo {
pub item_name: String,
pub group_name: String,
pub org_name: String,
}
#[derive(Debug)]
pub struct DacInfo {
pub port: u16,
}
struct SplitIteratorWithPosition<'a> {
inner: std::str::Split<'a, char>,
position: usize,
}
impl<'a> SplitIteratorWithPosition<'a> {
fn new(inner: std::str::Split<'a, char>) -> SplitIteratorWithPosition<'a> {
SplitIteratorWithPosition {
inner: inner,
position: 0,
}
}
fn string_position(&self) -> usize {
self.position
}
}
impl<'a> Iterator for SplitIteratorWithPosition<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<&'a str> {
match self.inner.next() {
Some(x) => {
self.position += x.len() + 1;
Some(x)
}
None => None,
}
}
}
pub(crate) fn parse_instance_info(
addr: IpAddr,
string: &str,
) -> Result<(InstanceInfo, usize), BrowserProtocolError> {
#[inline]
fn expect_next<'a, T: Iterator<Item = &'a str>>(
iterator: &mut T,
identifier: &str,
field: BrowserProtocolField,
) -> Result<(), BrowserProtocolError> {
iterator
.next()
.ok_or_else(|| BrowserProtocolError::UnexpectedToken {
expected: BrowserProtocolToken::Identifier(field),
found: BrowserProtocolToken::EndOfMessage,
})
.and_then(|x| {
if x == identifier {
Ok(())
} else {
Err(BrowserProtocolError::UnexpectedToken {
expected: BrowserProtocolToken::Identifier(field),
found: BrowserProtocolToken::Literal(x.to_string()),
})
}
})
}
fn consume_next<'a, T: Iterator<Item = &'a str>>(
iterator: &mut T,
value_name: BrowserProtocolField,
) -> Result<&'a str, BrowserProtocolError> {
iterator
.next()
.ok_or_else(|| BrowserProtocolError::UnexpectedToken {
expected: BrowserProtocolToken::ValueOf(value_name),
found: BrowserProtocolToken::EndOfMessage,
})
}
let mut iterator = SplitIteratorWithPosition::new(string.split(';'));
expect_next(
&mut iterator,
"ServerName",
BrowserProtocolField::ServerName,
)?;
let server_name = consume_next(&mut iterator, BrowserProtocolField::ServerName)?;
expect_next(
&mut iterator,
"InstanceName",
BrowserProtocolField::InstanceName,
)?;
let instance_name = consume_next(&mut iterator, BrowserProtocolField::InstanceName)?;
expect_next(
&mut iterator,
"IsClustered",
BrowserProtocolField::IsClustered,
)?;
let is_clustered_str = consume_next(&mut iterator, BrowserProtocolField::IsClustered)?;
let is_clustered = match is_clustered_str {
"Yes" => true,
"No" => false,
v => {
return Err(BrowserProtocolError::UnexpectedToken {
expected: BrowserProtocolToken::ValueOf(BrowserProtocolField::IsClustered),
found: BrowserProtocolToken::Literal(v.to_string()),
})
}
};
expect_next(&mut iterator, "Version", BrowserProtocolField::Version)?;
let version = consume_next(&mut iterator, BrowserProtocolField::Version)?;
let mut np_info: Option<NamedPipeInfo> = None;
let mut tcp_info: Option<TcpInfo> = None;
let mut via_info: Option<ViaInfo> = None;
let mut rpc_info: Option<RpcInfo> = None;
let mut spx_info: Option<SpxInfo> = None;
let mut adsp_info: Option<AdspInfo> = None;
let mut bv_info: Option<BvInfo> = None;
loop {
match iterator.next() {
Some("np") => {
let pipe_name = consume_next(&mut iterator, BrowserProtocolField::NamedPipeName)?;
np_info = Some(NamedPipeInfo {
name: pipe_name.to_owned(),
});
}
Some("tcp") => {
let port_str = consume_next(&mut iterator, BrowserProtocolField::TcpPort)?;
let port: u16 =
port_str
.parse()
.map_err(|_| BrowserProtocolError::UnexpectedToken {
expected: BrowserProtocolToken::TcpPort,
found: BrowserProtocolToken::Literal(port_str.to_string()),
})?;
tcp_info = Some(TcpInfo { port });
}
Some("via") => {
let parameters = consume_next(&mut iterator, BrowserProtocolField::ViaMachineName)?;
let comma_idx =
parameters
.find(',')
.ok_or_else(|| BrowserProtocolError::UnexpectedToken {
expected: BrowserProtocolToken::ViaParameters,
found: BrowserProtocolToken::Literal(parameters.to_string()),
})?;
let machine_name = ¶meters[0..comma_idx];
let mut nic_port_parts = (¶meters[(comma_idx + 1)..]).split(&[',', ':'][..]);
let mut addresses = Vec::new();
while let Some(nic) = nic_port_parts.next() {
let port = nic_port_parts.next().ok_or_else(|| {
BrowserProtocolError::UnexpectedToken {
expected: BrowserProtocolToken::ViaParameters,
found: BrowserProtocolToken::Literal(parameters.to_string()),
}
})?;
addresses.push(ViaAddress {
nic: nic.to_owned(),
port: port.to_owned(),
});
}
via_info = Some(ViaInfo {
machine_name: machine_name.to_owned(),
addresses,
});
}
Some("rpc") => {
let computer_name =
consume_next(&mut iterator, BrowserProtocolField::RpcComputerName)?;
rpc_info = Some(RpcInfo {
computer_name: computer_name.to_owned(),
});
}
Some("spx") => {
let service_name =
consume_next(&mut iterator, BrowserProtocolField::SpxServiceName)?;
spx_info = Some(SpxInfo {
service_name: service_name.to_owned(),
});
}
Some("adsp") => {
let object_name =
consume_next(&mut iterator, BrowserProtocolField::AppleTalkObjectName)?;
adsp_info = Some(AdspInfo {
object_name: object_name.to_owned(),
});
}
Some("bv") => {
let item_name = consume_next(&mut iterator, BrowserProtocolField::BvItemName)?;
let group_name = consume_next(&mut iterator, BrowserProtocolField::BvGroupName)?;
let org_name = consume_next(&mut iterator, BrowserProtocolField::BvOrgName)?;
bv_info = Some(BvInfo {
item_name: item_name.to_owned(),
group_name: group_name.to_owned(),
org_name: org_name.to_owned(),
});
}
Some("") => break,
Some(x) => {
return Err(BrowserProtocolError::UnexpectedToken {
expected: BrowserProtocolToken::EndpointIdentifierOrSemicolon,
found: BrowserProtocolToken::Literal(x.to_string()),
})
}
None => {
return Err(BrowserProtocolError::UnexpectedToken {
expected: BrowserProtocolToken::EndpointIdentifierOrSemicolon,
found: BrowserProtocolToken::EndOfMessage,
})
}
};
}
let consumed = iterator.string_position();
Ok((
InstanceInfo {
addr,
server_name: server_name.to_owned(),
instance_name: instance_name.to_owned(),
is_clustered,
version: version.to_owned(),
np_info,
tcp_info,
via_info,
rpc_info,
spx_info,
adsp_info,
bv_info,
},
consumed,
))
}