#![doc = include_str!("../README.md")]
#![cfg_attr(test, deny(warnings, unreachable_pub))]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
use std::{
fmt,
ops::{Deref, DerefMut},
time::Duration,
};
pub use ::insim_core as core;
use bytes::{Buf, BufMut};
use insim_core::{
dash_lights::DashLights,
gear::Gear,
identifiers::PlayerId,
speed::{Speed, SpeedKind},
Decode, DecodeString, Encode, EncodeString,
};
bitflags::bitflags! {
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct OutgaugeFlags: u16 {
const SHIFT = 1;
const CTRL = (1 << 1);
const TURBO = (1 << 13);
const KM = (1 << 14);
const BAR = (1 << 15);
}
}
impl Encode for OutgaugeFlags {
fn encode(&self, buf: &mut bytes::BytesMut) -> Result<(), insim_core::EncodeError> {
self.bits().encode(buf)
}
}
impl Decode for OutgaugeFlags {
fn decode(buf: &mut bytes::Bytes) -> Result<Self, insim_core::DecodeError> {
Ok(Self::from_bits_truncate(u16::decode(buf)?))
}
}
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct OutgaugeSpeed;
impl SpeedKind for OutgaugeSpeed {
type Inner = f32;
fn name() -> &'static str {
"m/s"
}
fn from_meters_per_sec(value: f32) -> Self::Inner {
value
}
fn to_meters_per_sec(value: Self::Inner) -> f32 {
value
}
}
#[derive(Debug, Ord, PartialOrd, PartialEq, Eq, Hash, Clone, Copy, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct OutgaugeId(pub i32);
impl fmt::Display for OutgaugeId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Deref for OutgaugeId {
type Target = i32;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for OutgaugeId {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<i32> for OutgaugeId {
fn from(value: i32) -> Self {
Self(value)
}
}
impl Decode for OutgaugeId {
fn decode(buf: &mut bytes::Bytes) -> Result<Self, insim_core::DecodeError> {
Ok(OutgaugeId(buf.get_i32_le()))
}
}
impl Encode for OutgaugeId {
fn encode(&self, buf: &mut bytes::BytesMut) -> Result<(), insim_core::EncodeError> {
buf.put_i32_le(self.0);
Ok(())
}
}
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Outgauge {
pub time: Duration,
pub car: String,
pub flags: OutgaugeFlags,
pub gear: Gear,
pub plid: PlayerId,
pub speed: Speed<OutgaugeSpeed>,
pub rpm: f32,
pub turbo: f32,
pub engtemp: f32,
pub fuel: f32,
pub oilpressure: f32,
pub oiltemp: f32,
pub dashlights: DashLights,
pub showlights: DashLights,
pub throttle: f32,
pub brake: f32,
pub clutch: f32,
pub display1: String,
pub display2: String,
pub id: Option<OutgaugeId>,
}
impl Encode for Outgauge {
fn encode(&self, buf: &mut bytes::BytesMut) -> Result<(), insim_core::EncodeError> {
let time = self.time.as_millis();
(time as u32).encode(buf)?;
self.car.encode_ascii(buf, 4, false)?;
self.flags.encode(buf)?;
self.gear.encode(buf)?;
self.plid.encode(buf)?;
self.speed.encode(buf)?;
self.rpm.encode(buf)?;
self.turbo.encode(buf)?;
self.engtemp.encode(buf)?;
self.fuel.encode(buf)?;
self.oilpressure.encode(buf)?;
self.oiltemp.encode(buf)?;
self.dashlights.encode(buf)?;
self.showlights.encode(buf)?;
self.throttle.encode(buf)?;
self.clutch.encode(buf)?;
self.brake.encode(buf)?;
self.display1.encode_ascii(buf, 16, false)?;
self.display2.encode_ascii(buf, 16, false)?;
if let Some(id) = self.id {
id.encode(buf)?;
}
Ok(())
}
}
impl Decode for Outgauge {
fn decode(buf: &mut bytes::Bytes) -> Result<Self, insim_core::DecodeError> {
let time = Duration::from_millis(u32::decode(buf)? as u64);
let car = String::decode_ascii(buf, 4)?;
let flags = OutgaugeFlags::decode(buf)?;
let gear = Gear::decode(buf)?;
let plid = PlayerId::decode(buf)?;
let speed = Speed::decode(buf)?;
let rpm = f32::decode(buf)?;
let turbo = f32::decode(buf)?;
let engtemp = f32::decode(buf)?;
let fuel = f32::decode(buf)?;
let oilpressure = f32::decode(buf)?;
let oiltemp = f32::decode(buf)?;
let dashlights = DashLights::decode(buf)?;
let showlights = DashLights::decode(buf)?;
let throttle = f32::decode(buf)?;
let clutch = f32::decode(buf)?;
let brake = f32::decode(buf)?;
let display1 = String::decode_ascii(buf, 16)?;
let display2 = String::decode_ascii(buf, 16)?;
let id = if buf.has_remaining() {
Some(OutgaugeId::decode(buf)?)
} else {
None
};
Ok(Self {
time,
car,
flags,
gear,
plid,
speed,
rpm,
turbo,
engtemp,
fuel,
oilpressure,
oiltemp,
dashlights,
showlights,
throttle,
clutch,
brake,
display1,
display2,
id,
})
}
}
#[cfg(test)]
mod test {
use bytes::{BufMut, BytesMut};
use super::*;
const RAW: [u8; 92] = [
152, 7, 1, 0, 88, 82, 84, 0, 0, 224, 3, 1, 232, 40, 51, 65, 64, 38, 25, 69, 83, 255, 127, 191, 0, 0, 0, 0, 79, 246, 127, 63, 0, 0, 0, 0, 0, 0, 0, 0, 102, 7, 0, 0, 0, 0, 0, 0, 64, 38, 25, 60, 64, 32, 32, 55, 66, 33, 34, 62, 70, 117, 101, 108, 32, 57, 57, 46, 57, 37, 32, 32, 32, 0, 0, 0, 66, 114, 97, 107, 101, 32, 66, 97, 108, 32, 70, 114, 32, 55, 53, 37,
];
#[test]
fn test_outgauge_without_id() {
let mut input = BytesMut::new();
input.extend_from_slice(&RAW);
let mut buf = input.clone().freeze();
let outgauge = Outgauge::decode(&mut buf).unwrap();
assert_eq!(buf.remaining(), 0);
let mut output = BytesMut::new();
outgauge.encode(&mut output).unwrap();
assert_eq!(
output.as_ref(),
input.as_ref(),
"assert reads and writes. left=actual, right=expected"
);
assert_eq!(&outgauge.car, "XRT");
assert!(matches!(outgauge.gear, Gear::Gear(2)));
assert_eq!(outgauge.plid, PlayerId(1));
assert_eq!(outgauge.rpm, 2450.390625);
assert_eq!(outgauge.turbo, -0.9999896883964539);
assert_eq!(outgauge.engtemp, 0.0);
assert_eq!(outgauge.display1.trim_end_matches(' '), "Fuel 99.9%");
assert_eq!(outgauge.display2.trim_end_matches(' '), "Brake Bal Fr 75%");
}
#[test]
fn test_outgauge_with_id() {
let mut input = BytesMut::new();
input.extend_from_slice(&RAW);
input.put_i32_le(10);
let mut buf = input.clone().freeze();
let outgauge = Outgauge::decode(&mut buf).unwrap();
assert_eq!(buf.remaining(), 0);
assert!(matches!(outgauge.id, Some(OutgaugeId(10))));
let mut output = BytesMut::new();
outgauge.encode(&mut output).unwrap();
assert_eq!(
output.as_ref(),
input.as_ref(),
"assert reads and writes. left=actual, right=expected"
);
}
}