use derive_more::{Deref, DerefMut};
use indexmap::IndexMap;
#[cfg(feature = "serialization")]
use serde::{Deserialize, Serialize};
use crate::utils::{To01String, get_key_ref, take_and_parse_key, take_key_owned};
use crate::{
VmfBlock, VmfSerializable,
errors::{VmfError, VmfResult},
};
#[derive(Debug, Default, Clone, PartialEq, Deref, DerefMut)]
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub struct Cameras {
pub active: i8,
#[deref]
#[deref_mut]
pub cams: Vec<Camera>,
}
impl TryFrom<VmfBlock> for Cameras {
type Error = VmfError;
fn try_from(mut block: VmfBlock) -> VmfResult<Self> {
let mut cams = Vec::with_capacity(block.blocks.len());
for group in block.blocks {
cams.push(Camera::try_from(group)?);
}
Ok(Self {
active: take_and_parse_key::<i8>(&mut block.key_values, "activecamera")?,
cams,
})
}
}
impl From<Cameras> for VmfBlock {
fn from(val: Cameras) -> Self {
let mut blocks = Vec::with_capacity(val.cams.len());
for cam in val.cams {
blocks.push(cam.into());
}
let mut key_values = IndexMap::new();
key_values.insert("active".to_string(), val.active.to_string());
VmfBlock {
name: "cameras".to_string(),
key_values,
blocks,
}
}
}
impl VmfSerializable for Cameras {
fn to_vmf_string(&self, indent_level: usize) -> String {
let indent: String = "\t".repeat(indent_level);
let mut output = String::with_capacity(64);
output.push_str(&format!("{0}cameras\n{0}{{\n", indent));
output.push_str(&format!(
"{}\t\"activecamera\" \"{}\"\n",
indent, self.active
));
for cam in &self.cams {
output.push_str(&format!("{0}\tcamera\n{0}\t{{\n", indent));
output.push_str(&format!(
"{}\t\t\"position\" \"{}\"\n",
indent, cam.position
));
output.push_str(&format!("{}\t\t\"look\" \"{}\"\n", indent, cam.look));
output.push_str(&format!("{}\t}}\n", indent));
}
output.push_str(&format!("{}}}\n", indent));
output
}
}
#[derive(Debug, Default, Clone, PartialEq)]
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub struct Camera {
pub position: String, pub look: String, }
impl TryFrom<VmfBlock> for Camera {
type Error = VmfError;
fn try_from(mut block: VmfBlock) -> VmfResult<Self> {
let kv = &mut block.key_values;
Ok(Self {
position: take_key_owned(kv, "position")?,
look: take_key_owned(kv, "look")?,
})
}
}
impl From<Camera> for VmfBlock {
fn from(val: Camera) -> Self {
let mut key_values = IndexMap::new();
key_values.insert("position".to_string(), val.position);
key_values.insert("look".to_string(), val.look);
VmfBlock {
name: "camera".to_string(),
key_values,
..Default::default()
}
}
}
#[derive(Debug, Default, Clone, PartialEq, Deref, DerefMut)]
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub struct Cordons {
pub active: i8,
#[deref]
#[deref_mut]
pub cordons: Vec<Cordon>,
}
impl TryFrom<VmfBlock> for Cordons {
type Error = VmfError;
fn try_from(mut block: VmfBlock) -> VmfResult<Self> {
let mut cordons = Vec::with_capacity(block.blocks.len());
for group in block.blocks {
cordons.push(Cordon::try_from(group)?);
}
Ok(Self {
active: take_and_parse_key::<i8>(&mut block.key_values, "active")?,
cordons,
})
}
}
impl From<Cordons> for VmfBlock {
fn from(val: Cordons) -> Self {
let mut blocks = Vec::new();
for cordon in val.cordons {
blocks.push(cordon.into());
}
let mut key_values = IndexMap::new();
key_values.insert("active".to_string(), val.active.to_string());
VmfBlock {
name: "cordons".to_string(),
key_values,
blocks,
}
}
}
impl VmfSerializable for Cordons {
fn to_vmf_string(&self, indent_level: usize) -> String {
let indent = "\t".repeat(indent_level);
let mut output = String::with_capacity(256);
output.push_str(&format!("{0}cordons\n{0}{{\n", indent));
output.push_str(&format!("{}\t\"active\" \"{}\"\n", indent, self.active));
for cordon in &self.cordons {
output.push_str(&cordon.to_vmf_string(indent_level + 1));
}
output.push_str(&format!("{}}}\n", indent));
output
}
}
#[derive(Debug, Default, Clone, PartialEq)]
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub struct Cordon {
pub name: String,
pub active: bool,
pub min: String, pub max: String, }
impl TryFrom<VmfBlock> for Cordon {
type Error = VmfError;
fn try_from(mut block: VmfBlock) -> VmfResult<Self> {
let sub_block_result: Option<(String, String)> =
block.blocks.get_mut(0).and_then(|sub_block| {
let maybe_min = sub_block.key_values.swap_remove("mins");
let maybe_max = sub_block.key_values.swap_remove("maxs");
maybe_min.zip(maybe_max)
});
let (min_string, max_string) = match sub_block_result {
Some((min_val, max_val)) => Ok((min_val, max_val)),
None => {
let min_res = take_key_owned(&mut block.key_values, "mins").map_err(|_| {
VmfError::InvalidFormat(
"Missing 'mins' key in Cordon block or its 'box' sub-block".to_string(),
)
});
let max_res = take_key_owned(&mut block.key_values, "maxs").map_err(|_| {
VmfError::InvalidFormat(
"Missing 'maxs' key in Cordon block or its 'box' sub-block".to_string(),
)
});
match (min_res, max_res) {
(Ok(min), Ok(max)) => Ok((min, max)),
(Err(e), _) => Err(e),
(_, Err(e)) => Err(e),
}
}
}?;
let name = take_key_owned(&mut block.key_values, "name")?;
let active = get_key_ref(&block.key_values, "active")? == "1";
Ok(Self {
name,
active,
min: min_string,
max: max_string,
})
}
}
impl From<Cordon> for VmfBlock {
fn from(val: Cordon) -> Self {
let mut key_values = IndexMap::new();
key_values.insert("name".to_string(), val.name);
key_values.insert("active".to_string(), val.active.to_01_string());
let mut box_block_key_values = IndexMap::new();
box_block_key_values.insert("mins".to_string(), val.min);
box_block_key_values.insert("maxs".to_string(), val.max);
let box_block = VmfBlock {
name: "box".to_string(),
key_values: box_block_key_values,
blocks: vec![],
};
VmfBlock {
name: "cordon".to_string(),
key_values,
blocks: vec![box_block],
}
}
}
impl VmfSerializable for Cordon {
fn to_vmf_string(&self, indent_level: usize) -> String {
let indent: String = "\t".repeat(indent_level);
let mut output = String::with_capacity(64);
output.push_str(&format!("{0}cordon\n{0}{{\n", indent));
output.push_str(&format!("{}\t\"name\" \"{}\"\n", indent, self.name));
output.push_str(&format!(
"{}\t\"active\" \"{}\"\n",
indent,
self.active.to_01_string()
));
output.push_str(&format!("{0}\tbox\n{}\t{{\n", indent));
output.push_str(&format!("{}\t\t\"mins\" \"{}\"\n", indent, self.min));
output.push_str(&format!("{}\t\t\"maxs\" \"{}\"\n", indent, self.max));
output.push_str(&format!("{}\t}}\n", indent));
output.push_str(&format!("{}}}\n", indent));
output
}
}