summaryrefslogtreecommitdiffstats
path: root/packages/svgbob/src/buffer/string_buffer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'packages/svgbob/src/buffer/string_buffer.rs')
-rw-r--r--packages/svgbob/src/buffer/string_buffer.rs116
1 files changed, 116 insertions, 0 deletions
diff --git a/packages/svgbob/src/buffer/string_buffer.rs b/packages/svgbob/src/buffer/string_buffer.rs
new file mode 100644
index 0000000..9350a90
--- /dev/null
+++ b/packages/svgbob/src/buffer/string_buffer.rs
@@ -0,0 +1,116 @@
+use std::ops::{Deref, DerefMut};
+use unicode_width::UnicodeWidthChar;
+
+/// A 2D string buffer
+/// where you can insert a character to any cell on the 2D grid
+/// each cell can be assigned with a string
+/// taking into account utf8 code which can not be char
+/// including but not limited to multi-width chars
+pub struct StringBuffer(Vec<Vec<char>>);
+
+impl Deref for StringBuffer {
+ type Target = Vec<Vec<char>>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl DerefMut for StringBuffer {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
+
+impl Default for StringBuffer {
+ fn default() -> Self {
+ Self(vec![])
+ }
+}
+
+impl StringBuffer {
+ pub fn new() -> Self {
+ StringBuffer(vec![])
+ }
+
+ /// add rows to this buffer
+ fn add_rows(&mut self, n: i32) {
+ for _i in 0..n {
+ self.push(vec![]);
+ }
+ }
+
+ /// add columns to the specified row of this buffer
+ fn add_column(&mut self, row: usize, n: i32) {
+ for _i in 0..n {
+ self[row].push(' ');
+ }
+ }
+
+ /// put a character in this x and y location, replacing the old character
+ /// if there is already
+ /// x and y can also be negative
+ pub fn add_char(&mut self, x: i32, y: i32, ch: char) {
+ if x >= 0 && y >= 0 {
+ if ch == '\0' {
+ println!("skipping {}", ch);
+ } else {
+ let row_index = y as usize;
+ let column_index = x as usize;
+ let row_diff = y - self.len() as i32;
+ if row_diff >= 0 {
+ self.add_rows(row_diff + 1);
+ }
+ let column = &self[y as usize];
+ let column_diff = x - column.len() as i32;
+ if column_diff >= 0 {
+ self.add_column(row_index as usize, column_diff + 1);
+ }
+ self[row_index][column_index] = ch;
+ }
+ }
+ }
+
+ /// replace insert a string on this location
+ pub fn add_str(&mut self, x: i32, y: i32, s: &str) {
+ for (i, ch) in s.chars().enumerate() {
+ self.add_char(x + i as i32, y, ch);
+ }
+ }
+}
+
+impl From<&str> for StringBuffer {
+ fn from(s: &str) -> Self {
+ let mut rows = vec![];
+ for line in s.lines() {
+ let mut row = vec![];
+ for ch in line.chars() {
+ row.push(ch);
+ if let Some(width) = ch.width() {
+ for _i in 1..width {
+ row.push('\0');
+ }
+ }
+ }
+ rows.push(row);
+ }
+
+ StringBuffer(rows)
+ }
+}
+
+impl ToString for StringBuffer {
+ fn to_string(&self) -> String {
+ let mut lines = vec![];
+ for row in self.iter() {
+ let row_contents: Vec<String> = row
+ .iter()
+ .filter(|ch| **ch != '\0')
+ .map(ToString::to_string)
+ .collect();
+ let line = row_contents.join("").trim_end().to_string();
+ lines.push(line);
+ }
+ lines.join("\n")
+ }
+}