vintage_schematics/convert/
entities.rs1use std::sync::LazyLock;
4
5use eyre::{bail, eyre};
6
7use crate::{Regex, formats::common::MinecraftBlockEntity};
8
9static OLD_SIGN_TEXT_PATTERN: LazyLock<Regex> =
12 LazyLock::new(|| Regex::new(r#"(?i)^\{\s*"?text"?\s*:"(?P<text>.+)"\s*}\s*$"#).unwrap());
13
14pub struct Sign<'a> {
16 pub messages: Vec<&'a str>,
18
19 pub colour: Option<&'a str>,
21}
22
23impl Sign<'_> {
24 #[must_use]
26 pub fn text(&self) -> String {
27 String::from("\n") + &*self.messages.join("\n")
29 }
30
31 #[must_use]
34 pub fn colour(&self) -> (u8, u8, u8) {
35 match self.colour {
54 Some("white") => (237, 237, 237),
55 Some("light_gray" | "gray") => (221, 216, 199),
56 Some("green" | "lime" | "yellow") => (112, 154, 108),
57 Some("blue" | "light_blue" | "cyan") => (74, 113, 176),
58 Some("red" | "purple" | "pink") => (192, 66, 49),
59 _ => (25, 24, 22),
60 }
61 }
62}
63
64impl<'a> TryFrom<&'a MinecraftBlockEntity> for Sign<'a> {
65 type Error = eyre::Error;
66
67 fn try_from(value: &'a MinecraftBlockEntity) -> Result<Self, Self::Error> {
68 if value.id != "minecraft:sign" {
69 bail!("invalid sign: not a sign block")
70 }
71
72 if let Some(mininbt::Value::Compound(front_text)) = value.components.get("front_text") {
73 let messages = if let Some(mininbt::Value::List(messages)) = front_text.get("messages") {
75 messages
76 .iter()
77 .map(|message| message.as_str().unwrap_or_default().trim_matches('"'))
78 .collect::<Vec<_>>()
79 } else {
80 bail!("invalid sign: front_text.messages is missing or invalid");
81 };
82
83 Ok(Self {
84 messages,
85 colour: front_text.get("color").and_then(|c| c.as_str()),
86 })
87 } else {
88 let mut i = 1;
91 while value.components.contains_key(&format!("Text{i}")) {
92 i += 1;
93 }
94
95 let messages = (1..=i)
97 .map(|i| {
98 value
99 .components
100 .get(&format!("Text{i}"))
101 .and_then(|v| v.as_str())
102 .and_then(|v| OLD_SIGN_TEXT_PATTERN.captures(v))
103 .and_then(|c| c.name("text").map(|m| m.as_str()))
104 })
105 .collect::<Option<Vec<_>>>()
106 .ok_or_else(|| eyre!("invalid sign: no front_text or Text1"))?;
107
108 Ok(Self {
109 messages,
110 colour: value.components.get("Color").and_then(|v| v.as_str()),
111 })
112 }
113 }
114}