use crate::device::rpc::{DecodeError, EncodeError, RpcDescriptor, RpcValue, RpcValueType};
use crate::tio::proxy;
pub fn load_rpc_specs(device: &proxy::Port) -> Result<Vec<RpcDescriptor>, proxy::RpcError> {
let nrpcs: u16 = device.get("rpc.listinfo")?;
let mut specs = Vec::with_capacity(nrpcs as usize);
for id in 0..nrpcs {
let (meta, name): (u16, String) = device.rpc("rpc.listinfo", id)?;
specs.push(parse_rpc_spec(meta, name));
}
Ok(specs)
}
pub fn parse_rpc_spec(meta: u16, name: String) -> RpcDescriptor {
let data_type = meta & 0x000F; let data_size = ((meta >> 4) & 0x000F) as u8;
let readable = (meta & 0x0100) != 0;
let writable = (meta & 0x0200) != 0;
let persistent = (meta & 0x0400) != 0;
let unknown = meta == 0;
let data_kind = if unknown {
RpcValueType::Raw { meta }
} else {
match data_type {
0 => {
match data_size {
0 => RpcValueType::Unit,
1 | 2 | 4 | 8 => RpcValueType::Int {
signed: false,
size: data_size,
},
_ => RpcValueType::Raw { meta },
}
}
1 => {
match data_size {
0 => RpcValueType::Unit,
1 | 2 | 4 | 8 => RpcValueType::Int {
signed: true,
size: data_size,
},
_ => RpcValueType::Raw { meta },
}
}
2 => {
match data_size {
4 | 8 => RpcValueType::Float { size: data_size },
0 => RpcValueType::Unit,
_ => RpcValueType::Raw { meta },
}
}
3 => {
let max_len = if data_size == 0 {
None
} else {
Some(data_size as u16)
};
RpcValueType::String { max_len }
}
_ => RpcValueType::Raw { meta },
}
};
let segments = name.split('.').map(|s| s.to_string()).collect();
RpcDescriptor {
full_name: name,
segments,
data_kind,
readable,
writable,
persistent,
meta_raw: meta,
}
}
pub fn resolve_arg_type(meta: Option<u16>, name: &str) -> RpcValueType {
let kind = meta
.map(|m| parse_rpc_spec(m, name.to_string()).data_kind)
.unwrap_or(RpcValueType::String { max_len: None });
match kind {
RpcValueType::Raw { .. } => RpcValueType::String { max_len: None },
other => other,
}
}
pub fn rpc_encode_arg(input: &str, kind: &RpcValueType) -> Result<Vec<u8>, EncodeError> {
match kind {
RpcValueType::Unit => {
if input.is_empty() {
Ok(Vec::new())
} else {
Err(EncodeError::NotEncodableForKind("unit"))
}
}
RpcValueType::String { max_len } => {
if let Some(max) = max_len {
if input.len() > *max as usize {
return Err(EncodeError::StringTooLong {
max: *max,
actual: input.len(),
});
}
}
Ok(input.as_bytes().to_vec())
}
RpcValueType::Int {
signed: false,
size,
} => match *size {
1 => Ok(input.parse::<u8>()?.to_le_bytes().to_vec()),
2 => Ok(input.parse::<u16>()?.to_le_bytes().to_vec()),
4 => Ok(input.parse::<u32>()?.to_le_bytes().to_vec()),
8 => Ok(input.parse::<u64>()?.to_le_bytes().to_vec()),
s => Err(EncodeError::UnsupportedIntSize(s)),
},
RpcValueType::Int { signed: true, size } => match *size {
1 => Ok(input.parse::<i8>()?.to_le_bytes().to_vec()),
2 => Ok(input.parse::<i16>()?.to_le_bytes().to_vec()),
4 => Ok(input.parse::<i32>()?.to_le_bytes().to_vec()),
8 => Ok(input.parse::<i64>()?.to_le_bytes().to_vec()),
s => Err(EncodeError::UnsupportedIntSize(s)),
},
RpcValueType::Float { size } => match *size {
4 => Ok(input.parse::<f32>()?.to_le_bytes().to_vec()),
8 => Ok(input.parse::<f64>()?.to_le_bytes().to_vec()),
s => Err(EncodeError::UnsupportedFloatSize(s)),
},
RpcValueType::Raw { .. } => Ok(Vec::new()),
}
}
pub fn rpc_decode_reply(reply: &[u8], kind: &RpcValueType) -> Result<RpcValue, DecodeError> {
match kind {
RpcValueType::Unit => Ok(RpcValue::Unit),
RpcValueType::String { .. } => {
match std::str::from_utf8(reply) {
Ok(s) => Ok(RpcValue::Str(s.to_owned())),
Err(_) => Ok(RpcValue::Bytes(reply.to_vec())), }
}
RpcValueType::Int {
signed: false,
size,
} => match *size {
1 => {
if reply.len() < 1 {
return Err(DecodeError::InsufficientBytes {
expected: 1,
got: reply.len(),
});
}
Ok(RpcValue::U64(u8::from_le_bytes([reply[0]]) as u64))
}
2 => {
if reply.len() < 2 {
return Err(DecodeError::InsufficientBytes {
expected: 2,
got: reply.len(),
});
}
Ok(RpcValue::U64(
u16::from_le_bytes(reply[0..2].try_into().unwrap()) as u64,
))
}
4 => {
if reply.len() < 4 {
return Err(DecodeError::InsufficientBytes {
expected: 4,
got: reply.len(),
});
}
Ok(RpcValue::U64(
u32::from_le_bytes(reply[0..4].try_into().unwrap()) as u64,
))
}
8 => {
if reply.len() < 8 {
return Err(DecodeError::InsufficientBytes {
expected: 8,
got: reply.len(),
});
}
Ok(RpcValue::U64(u64::from_le_bytes(
reply[0..8].try_into().unwrap(),
)))
}
s => Err(DecodeError::UnsupportedIntSize(s)),
},
RpcValueType::Int { signed: true, size } => match *size {
1 => {
if reply.len() < 1 {
return Err(DecodeError::InsufficientBytes {
expected: 1,
got: reply.len(),
});
}
Ok(RpcValue::I64(i8::from_le_bytes([reply[0]]) as i64))
}
2 => {
if reply.len() < 2 {
return Err(DecodeError::InsufficientBytes {
expected: 2,
got: reply.len(),
});
}
Ok(RpcValue::I64(
i16::from_le_bytes(reply[0..2].try_into().unwrap()) as i64,
))
}
4 => {
if reply.len() < 4 {
return Err(DecodeError::InsufficientBytes {
expected: 4,
got: reply.len(),
});
}
Ok(RpcValue::I64(
i32::from_le_bytes(reply[0..4].try_into().unwrap()) as i64,
))
}
8 => {
if reply.len() < 8 {
return Err(DecodeError::InsufficientBytes {
expected: 8,
got: reply.len(),
});
}
Ok(RpcValue::I64(i64::from_le_bytes(
reply[0..8].try_into().unwrap(),
)))
}
s => Err(DecodeError::UnsupportedIntSize(s)),
},
RpcValueType::Float { size } => match *size {
4 => {
if reply.len() < 4 {
return Err(DecodeError::InsufficientBytes {
expected: 4,
got: reply.len(),
});
}
Ok(RpcValue::F64(
f32::from_le_bytes(reply[0..4].try_into().unwrap()) as f64,
))
}
8 => {
if reply.len() < 8 {
return Err(DecodeError::InsufficientBytes {
expected: 8,
got: reply.len(),
});
}
Ok(RpcValue::F64(f64::from_le_bytes(
reply[0..8].try_into().unwrap(),
)))
}
s => Err(DecodeError::UnsupportedFloatSize(s)),
},
RpcValueType::Raw { .. } => Ok(RpcValue::Bytes(reply.to_vec())),
}
}