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