use std::cmp::max;
use std::iter;
use std::io::Write;
use std::string::FromUtf8Error;
const DEFAULT_CAPACITY: usize = 1024;
const MAX_UNICODE_WIDTH: usize = 4;
const LINE_ENDING_WINDOWS: &'static [u8] = b"\r\n";
const LINE_ENDING_NOT_WINDOWS: &'static [u8] = b"\n";
#[derive(Debug,Clone)]
pub struct IndentedTextWriter {
inner: Vec<u8>,
tab_string: Vec<u8>,
indent_level: i32,
tabs_pending: bool,
indent_begin: u8,
indent_end: u8,
line_ending: &'static [u8],
}
impl Default for IndentedTextWriter {
fn default() -> IndentedTextWriter {
let inner = Vec::with_capacity(DEFAULT_CAPACITY);
let tab_string = " ".to_bytes();
let indent_level = 0;
let tabs_pending = false;
let indent_begin = '{'.to_bytes()[0];
let indent_end = '}'.to_bytes()[0];
IndentedTextWriter {
inner,
tab_string,
indent_level,
tabs_pending,
indent_begin,
indent_end,
line_ending: LINE_ENDING_NOT_WINDOWS,
}
}
}
impl IndentedTextWriter {
pub fn new(tab_string: &str, size: usize, indent_begin: char, indent_end: char, is_lf: bool) -> IndentedTextWriter {
let line_ending = if is_lf { LINE_ENDING_NOT_WINDOWS } else { LINE_ENDING_WINDOWS };
let inner = Vec::with_capacity(size);
IndentedTextWriter {
inner,
tab_string: tab_string.to_bytes(),
indent_level: 0,
tabs_pending: false,
indent_begin: indent_begin.to_bytes()[0],
indent_end: indent_end.to_bytes()[0],
line_ending
}
}
fn output_tabs(&mut self) {
if self.tabs_pending {
for _ in 1..=self.indent_level {
self.inner.write_all(&self.tab_string).unwrap()
}
self.tabs_pending = false;
}
}
pub fn get_tab_line(&self) -> String {
let mut s = String::new();
for _ in 1..=self.indent_level {
s.push_str(&String::from_utf8_lossy(&self.tab_string));
}
s
}
pub fn indent(&mut self) {
self.indent_level += 1;
}
pub fn indents(&mut self, len: i32) {
self.indent_level += len;
}
pub fn unindent(&mut self) {
self.indent_level -= 1;
}
pub fn unindents(&mut self, size: i32) {
self.indent_level -= size;
}
pub fn write<T: ToBytes>(&mut self, buf: T) {
self.output_tabs();
self.inner.write_all(&buf.to_bytes()).unwrap()
}
pub fn write_no_tabs<T: ToBytes>(&mut self, buf: T) {
self.inner.write_all(&buf.to_bytes()).unwrap()
}
pub fn write_line<T: ToBytes>(&mut self, buf: T) {
let bytes = buf.to_bytes();
let add_inc = bytes.iter().filter(|&&x| x == self.indent_begin).count() as i32;
let add_dec = bytes.iter().filter(|&&x| x == self.indent_end).count()as i32;
let inc = max(add_inc - add_dec, 0);
let dec = max(add_dec - add_inc, 0);
self.unindents(dec);
self.output_tabs();
self.inner.write_all(&buf.to_bytes()).unwrap();
self.inner.write_all(self.line_ending).unwrap();
self.tabs_pending = true;
self.indents(inc);
}
pub fn write_line_no_tabs<T: ToBytes>(&mut self, buf: T) {
self.inner.write_all(&buf.to_bytes()).unwrap();
self.inner.write_all(self.line_ending).unwrap()
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn string(self) -> Result<String, FromUtf8Error> {
String::from_utf8(self.inner)
}
}
pub trait ToBytes {
fn to_bytes(&self) -> Vec<u8>;
}
fn make_copyable_buf(len: usize) -> Vec<u8> {
iter::repeat(0).take(len).collect::<Vec<u8>>()
}
fn slice_to_vec(s: &[u8]) -> Vec<u8> {
let mut res = make_copyable_buf(s.len());
res.copy_from_slice(s);
res
}
impl ToBytes for String {
fn to_bytes(&self) -> Vec<u8> {
slice_to_vec(self.as_bytes())
}
}
impl<'a> ToBytes for &'a str {
fn to_bytes(&self) -> Vec<u8> {
slice_to_vec(self.as_bytes())
}
}
impl ToBytes for u8 {
fn to_bytes(&self) -> Vec<u8> {
vec![*self]
}
}
impl ToBytes for char {
fn to_bytes(&self) -> Vec<u8> {
let mut buf = [0; MAX_UNICODE_WIDTH];
slice_to_vec(self.encode_utf8(&mut buf).as_bytes())
}
}
impl<'a> ToBytes for &'a [u8] {
fn to_bytes(&self) -> Vec<u8> {
slice_to_vec(self)
}
}
#[cfg(test)]
mod tests {
use super::IndentedTextWriter;
#[test]
fn tests_generate_class() {
let mut writer = IndentedTextWriter::new(" ",1024, '{', '}', true);
writer.write_line("struct Data {");
writer.write_line("name: String,");
writer.write_line("value: i32");
writer.write_line("}");
println!("{}",writer.string().unwrap());
}
}