mirror of
https://github.com/neocturne/MinedMap.git
synced 2025-04-19 11:05:08 +02:00
132 lines
2.9 KiB
Rust
132 lines
2.9 KiB
Rust
//! Processing of sign text
|
|
|
|
use std::{fmt::Display, sync::Arc};
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use super::{
|
|
de,
|
|
json_text::{FormattedText, FormattedTextList, JSONText},
|
|
};
|
|
|
|
/// Version-independent reference to (front or back) sign text
|
|
#[derive(Debug, Default)]
|
|
pub struct RawSignText<'a> {
|
|
/// Lines of sign text
|
|
///
|
|
/// A regular sign always has 4 lines of text. The back of pre-1.20
|
|
/// signs is represented as a [SignText] without any `messages`.
|
|
pub messages: Vec<&'a JSONText>,
|
|
/// Sign color
|
|
///
|
|
/// Defaults to "black".
|
|
pub color: Option<&'a str>,
|
|
}
|
|
|
|
impl RawSignText<'_> {
|
|
/// Decodes the [RawSignText] into a [SignText]
|
|
pub fn decode(&self) -> SignText {
|
|
let color = self.color.map(|c| Arc::new(c.to_owned()));
|
|
let parent = FormattedText {
|
|
color,
|
|
..Default::default()
|
|
};
|
|
SignText(
|
|
self.messages
|
|
.iter()
|
|
.map(|message| message.deserialize().linearize(&parent))
|
|
.collect(),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<'a> From<&'a de::BlockEntitySignV1_20Text> for RawSignText<'a> {
|
|
fn from(value: &'a de::BlockEntitySignV1_20Text) -> Self {
|
|
RawSignText {
|
|
messages: value.messages.iter().collect(),
|
|
color: value.color.as_deref(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Helper methods for [de::BlockEntitySign]
|
|
pub trait BlockEntitySignExt {
|
|
/// Returns the front and back text of a sign in a version-indepentent format
|
|
fn text(&self) -> (RawSignText, RawSignText);
|
|
}
|
|
|
|
impl BlockEntitySignExt for de::BlockEntitySign {
|
|
fn text(&self) -> (RawSignText, RawSignText) {
|
|
match self {
|
|
de::BlockEntitySign::V0 {
|
|
text1,
|
|
text2,
|
|
text3,
|
|
text4,
|
|
color,
|
|
} => (
|
|
RawSignText {
|
|
messages: vec![text1, text2, text3, text4],
|
|
color: color.as_deref(),
|
|
},
|
|
Default::default(),
|
|
),
|
|
de::BlockEntitySign::V1_20 {
|
|
front_text,
|
|
back_text,
|
|
} => (front_text.into(), back_text.into()),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
|
/// Deserialized and linearized sign text
|
|
pub struct SignText(pub Vec<FormattedTextList>);
|
|
|
|
impl SignText {
|
|
/// Checks if all lines of the sign text are empty
|
|
pub fn is_empty(&self) -> bool {
|
|
self.0.iter().all(|line| line.is_empty())
|
|
}
|
|
}
|
|
|
|
impl Display for SignText {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
let mut iter = self.0.iter();
|
|
|
|
let Some(first) = iter.next() else {
|
|
return Ok(());
|
|
};
|
|
first.fmt(f)?;
|
|
|
|
for text in iter {
|
|
f.write_str("\n")?;
|
|
text.fmt(f)?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
fn formatted_text(text: &str) -> FormattedText {
|
|
FormattedText {
|
|
text: text.to_string(),
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_sign_text_display() {
|
|
let sign_text = SignText(vec![
|
|
FormattedTextList(vec![formatted_text("a"), formatted_text("b")]),
|
|
FormattedTextList(vec![formatted_text("c")]),
|
|
FormattedTextList(vec![formatted_text("d")]),
|
|
FormattedTextList(vec![formatted_text("e")]),
|
|
]);
|
|
assert_eq!("ab\nc\nd\ne", sign_text.to_string());
|
|
}
|
|
}
|