msg_tool\format/
fixed.rs

1use crate::types::*;
2use unicode_segmentation::UnicodeSegmentation;
3
4const SPACE_STR_LIST: [&str; 2] = [" ", " "];
5
6pub struct FixedFormatter {
7    length: usize,
8    keep_original: bool,
9    #[allow(unused)]
10    typ: Option<ScriptType>,
11}
12
13impl FixedFormatter {
14    pub fn new(length: usize, keep_original: bool, typ: Option<ScriptType>) -> Self {
15        FixedFormatter {
16            length,
17            keep_original,
18            typ,
19        }
20    }
21
22    #[cfg(feature = "circus")]
23    fn is_circus(&self) -> bool {
24        matches!(self.typ, Some(ScriptType::Circus))
25    }
26
27    #[cfg(not(feature = "circus"))]
28    fn is_circus(&self) -> bool {
29        false
30    }
31
32    pub fn format(&self, message: &str) -> String {
33        let mut result = String::new();
34        let vec: Vec<_> = UnicodeSegmentation::graphemes(message, true).collect();
35        let mut current_length = 0;
36        let mut is_command = false;
37        let mut pre_is_lf = false;
38        let mut is_ruby = false;
39        let mut is_ruby_rt = false;
40        let mut last_command = None;
41        for grapheme in vec {
42            if grapheme == "\n" {
43                if self.keep_original
44                    || (self.is_circus() && last_command.as_ref().is_some_and(|cmd| cmd == "@n"))
45                {
46                    result.push('\n');
47                    current_length = 0;
48                }
49                pre_is_lf = true;
50                continue;
51            }
52            if current_length >= self.length {
53                result.push('\n');
54                current_length = 0;
55            }
56            if (current_length == 0 || pre_is_lf) && SPACE_STR_LIST.contains(&grapheme) {
57                continue;
58            }
59            result.push_str(grapheme);
60            if self.is_circus() {
61                if grapheme == "@" {
62                    is_command = true;
63                    last_command = Some(String::new());
64                } else if is_command && grapheme.len() != 1
65                    || !grapheme
66                        .chars()
67                        .next()
68                        .unwrap_or(' ')
69                        .is_ascii_alphanumeric()
70                {
71                    is_command = false;
72                }
73                if grapheme == "{" {
74                    is_ruby = true;
75                    is_ruby_rt = true;
76                } else if is_ruby && grapheme == "/" {
77                    is_ruby_rt = false;
78                    continue;
79                } else if is_ruby && grapheme == "}" {
80                    is_ruby = false;
81                    continue;
82                }
83            }
84            if is_command {
85                if let Some(ref mut cmd) = last_command {
86                    cmd.push_str(grapheme);
87                }
88            }
89            if !is_command && !is_ruby_rt {
90                current_length += 1;
91            }
92            pre_is_lf = false;
93        }
94        return result;
95    }
96}
97
98#[test]
99fn test_format() {
100    let formatter = FixedFormatter::new(10, false, None);
101    let message = "This is a test message.\nThis is another line.";
102    let formatted_message = formatter.format(message);
103    assert_eq!(
104        formatted_message,
105        "This is a \ntest messa\nge.This is\nanother li\nne."
106    );
107    assert_eq!(formatter.format("● This is a test."), "● This is \na test.");
108    assert_eq!(
109        formatter.format("● This is  a test."),
110        "● This is \na test."
111    );
112    let fommater2 = FixedFormatter::new(10, true, None);
113    assert_eq!(
114        fommater2.format("● Th\n is is a te st."),
115        "● Th\nis is a te\nst."
116    );
117    #[cfg(feature = "circus")]
118    {
119        let circus_formatter = FixedFormatter::new(10, false, Some(ScriptType::Circus));
120        assert_eq!(
121            circus_formatter.format("● @cmd1@cmd2@cmd3中文字数是一\n 二三 四五六七八九十"),
122            "● @cmd1@cmd2@cmd3中文字数是一二三\n四五六七八九十"
123        );
124        assert_eq!(
125            circus_formatter
126                .format("● @cmd1@cmd2@cmd3{rubyText/中文}字数是一\n 二三 四五六七八九十"),
127            "● @cmd1@cmd2@cmd3{rubyText/中文}字数是一二三\n四五六七八九十"
128        );
129        let circus_formatter2 = FixedFormatter::new(32, false, Some(ScriptType::Circus));
130        assert_eq!(
131            circus_formatter2.format("@re1@re2@b1@t30@w1「当然现在我很幸福哦?\n 因为有你在身边」@n\n「@b1@t38@w1当然现在我很幸福哦?\n 因为有敦也君在身边」"),
132            "@re1@re2@b1@t30@w1「当然现在我很幸福哦?因为有你在身边」@n\n「@b1@t38@w1当然现在我很幸福哦?因为有敦也君在身边」"
133        );
134    }
135}