vmf_forge/vmf/
common.rs

1//! This module provides common structures and functions used across the VMF parser.
2
3use indexmap::IndexMap;
4use serde::{Deserialize, Serialize};
5
6use crate::utils::{get_key, parse_hs_key};
7use crate::{
8    errors::{VmfError, VmfResult},
9    utils::To01String,
10    VmfBlock, VmfSerializable,
11};
12
13/// Represents the editor data of a VMF entity or solid.
14#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
15pub struct Editor {
16    /// The color of the entity in the editor, in "R G B" format.
17    pub color: String,
18    /// The ID of the visgroup this entity is in, if any.
19    #[serde(default, skip_serializing_if = "Option::is_none")]
20    pub visgroup_id: Option<i32>,
21    /// The ID of the group this entity is in, if any.
22    #[serde(default, skip_serializing_if = "Option::is_none")]
23    pub group_id: Option<i32>,
24    /// Whether the entity is shown in the visgroup.
25    pub visgroup_shown: bool,
26    /// Whether the entity should automatically be shown in the visgroup.
27    pub visgroup_auto_shown: bool,
28    /// Comments associated with the entity, if any.
29    #[serde(default, skip_serializing_if = "Option::is_none")]
30    pub comments: Option<String>,
31    /// The logical position of the entity in the editor, in "[x y]" format.
32    #[serde(default, skip_serializing_if = "Option::is_none")]
33    pub logical_pos: Option<String>,
34}
35
36impl Default for Editor {
37    fn default() -> Self {
38        Self {
39            color: "255 255 255".to_string(),
40            visgroup_id: None,
41            group_id: None,
42            visgroup_shown: true,
43            visgroup_auto_shown: true,
44            comments: None,
45            logical_pos: None,
46        }
47    }
48}
49
50impl TryFrom<VmfBlock> for Editor {
51    type Error = VmfError;
52
53    fn try_from(block: VmfBlock) -> VmfResult<Self> {
54        let kv = &block.key_values;
55
56        Ok(Self {
57            color: get_key!(kv, "color", "255 255 255".to_string()).to_owned(),
58            visgroup_id: parse_hs_key!(kv, "visgroupid", i32).ok(),
59            group_id: parse_hs_key!(kv, "groupid", i32).ok(),
60            visgroup_shown: get_key!(kv, "visgroupshown", "_".to_string()) == "1",
61            visgroup_auto_shown: get_key!(kv, "visgroupautoshown", "_".to_string()) == "1",
62            comments: kv.get("comments").cloned(),
63            logical_pos: kv.get("logicalpos").cloned(),
64        })
65    }
66}
67
68impl From<Editor> for VmfBlock {
69    fn from(val: Editor) -> VmfBlock {
70        let mut key_values = IndexMap::new();
71        key_values.insert("color".to_string(), val.color);
72        if let Some(visgroup_id) = val.visgroup_id {
73            key_values.insert("visgroupid".to_string(), visgroup_id.to_string());
74        }
75        if let Some(group_id) = val.group_id {
76            key_values.insert("groupid".to_string(), group_id.to_string());
77        }
78        key_values.insert(
79            "visgroupshown".to_string(),
80            val.visgroup_shown.to_01_string(),
81        );
82        key_values.insert(
83            "visgroupautoshown".to_string(),
84            val.visgroup_auto_shown.to_01_string(),
85        );
86        if let Some(comments) = val.comments {
87            key_values.insert("comments".to_string(), comments);
88        }
89        if let Some(logical_pos) = val.logical_pos {
90            key_values.insert("logicalpos".to_string(), logical_pos);
91        }
92
93        VmfBlock {
94            name: "editor".to_string(),
95            key_values,
96            blocks: Vec::new(),
97        }
98    }
99}
100
101impl VmfSerializable for Editor {
102    fn to_vmf_string(&self, indent_level: usize) -> String {
103        let indent = "\t".repeat(indent_level);
104        let mut output = String::with_capacity(128);
105
106        output.push_str(&format!("{0}editor\n{0}{{\n", indent));
107        output.push_str(&format!("{}\t\"color\" \"{}\"\n", indent, self.color));
108        if let Some(visgroup_id) = self.visgroup_id {
109            output.push_str(&format!("{}\t\"visgroupid\" \"{}\"\n", indent, visgroup_id));
110        }
111        if let Some(group_id) = self.group_id {
112            output.push_str(&format!("{}\t\"groupid\" \"{}\"\n", indent, group_id));
113        }
114        output.push_str(&format!(
115            "{}\t\"visgroupshown\" \"{}\"\n",
116            indent,
117            self.visgroup_shown.to_01_string()
118        ));
119        output.push_str(&format!(
120            "{}\t\"visgroupautoshown\" \"{}\"\n",
121            indent,
122            self.visgroup_auto_shown.to_01_string()
123        ));
124        if let Some(comments) = &self.comments {
125            output.push_str(&format!("{}\t\"comments\" \"{}\"\n", indent, comments));
126        }
127        if let Some(logical_pos) = &self.logical_pos {
128            output.push_str(&format!("{}\t\"logicalpos\" \"{}\"\n", indent, logical_pos));
129        }
130
131        output.push_str(&format!("{}}}\n", indent));
132        output
133    }
134}